diff --git a/OAT.xml b/OAT.xml index 9cbf948..7e3a5ee 100644 --- a/OAT.xml +++ b/OAT.xml @@ -1,83 +1,108 @@ - - - - - - COPYING - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + COPYING + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md index 509292a..bcd3fff 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,31 @@ # libnl #### Introduction & Software Architecture -- [Refer to the official documentation](https://www.infradead.org/~tgr/libnl/) -#### Usage Guidelines +- [Refer to Official Documentation](https://www.infradead.org/~tgr/libnl/) -- [Refer to the official API documentation](https://www.infradead.org/~tgr/libnl/doc/api/group__cb.html) +#### Usage Instructions -#### Generates Header File +- [Refer to Official API Documentation](https://www.infradead.org/~tgr/libnl/doc/api/group__cb.html) -The following patches is carried by the openEuler:libnl3 open source library: +#### Patch Details + +`solve-oh-compile-problem3_11_0.patch` applies the OHOS adaptation patch and archives the patch (with conflicts resolved) to `archive/patches/` for reuse in subsequent version upgrades. + +#### Build and Upgrade + +This repository adopts the "pre-prepared code environment" approach: the `libnl/` source tree has already been decompressed, configured, patched, and its flex/bison syntax files pre-generated and committed to the repository. During the build phase, compilation is handled directly by `BUILD.gn`, which does not depend on autotools/flex/bison and does not execute scripts during the `gn gen` phase. When upgrading libnl, please run `prepare.sh` and refer to [docs/UPGRADE_GUIDE.md](docs/UPGRADE_GUIDE.md). +#### Contributing -The following patches are added to solve the compilation problem under the OpenHarmony project: +[How to Contribute](https://gitee.com/openharmony/docs/blob/HEAD/zh-cn/contribute/参与贡献.md) -solve-oh-compile-problem3_11_0.patch +[Commit Message Specifications](https://gitee.com/openharmony/device_qemu/wikis/Commit%20message%E8%A7%84%E8%8C%83) -#### Contribution -[How to involve](https://gitee.com/openharmony/docs/blob/HEAD/zh-cn/contribute/参与贡献.md) - -[Commit message spec](https://gitee.com/openharmony/device_qemu/wikis/Commit%20message%E8%A7%84%E8%8C%83) - -#### Repositories Involved +#### Related Repositories [third_party_wpa_supplicant](https://gitee.com/openharmony/third_party_wpa_supplicant) [drivers_peripheral](https://gitee.com/openharmony/drivers_peripheral) - diff --git a/README_ZH.md b/README_ZH.md index a4b4677..2bce560 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -10,11 +10,16 @@ #### patch包说明 -以下patch包为openEuler:libnl3开源库本身携带 +solve-oh-compile-problem3_11_0.patch 应用 OHOS 适配 patch,并把(解决冲突后的) +patch 归档到archive/patches/ 供后续版本升级复用 +#### 构建与升级 + +本仓库采用“代码环境提前准备”方式:`libnl/` 源码树已完成解压、configure、 +打 patch 及 flex/bison 语法文件预生成并入库,构建期由 `BUILD.gn` 直接编译, +不依赖 autotools/flex/bison,也不在 `gn gen` 阶段执行脚本。升级 libnl 时请运行 +`prepare.sh` 并参考 [docs/UPGRADE_GUIDE.md](docs/UPGRADE_GUIDE.md)。 -以下patch包为解决在OpenHarmony工程下编译存在的问题自行添加 -solve-oh-compile-problem3_11_0.patch #### 参与贡献 diff --git a/archive/libnl-3.11.0.sha256sums b/archive/libnl-3.11.0.sha256sums new file mode 100644 index 0000000..1b6d934 --- /dev/null +++ b/archive/libnl-3.11.0.sha256sums @@ -0,0 +1 @@ +871d8f9f05b86dab080988bb994748384c1416a5d03de1f9f5e811f265094b04 libnl3_11_0.tar.gz diff --git a/solve-oh-compile-problem3_11_0.patch b/archive/patches/solve-oh-compile-problem3_11_0.patch similarity index 100% rename from solve-oh-compile-problem3_11_0.patch rename to archive/patches/solve-oh-compile-problem3_11_0.patch diff --git a/docs/UPGRADE_GUIDE.md b/docs/UPGRADE_GUIDE.md new file mode 100644 index 0000000..f6dd92a --- /dev/null +++ b/docs/UPGRADE_GUIDE.md @@ -0,0 +1,151 @@ +# libnl 升级指导 + +本仓库采用 **代码环境提前准备** 的方式集成 libnl:上游源码已经过 +解压、`configure`、flex/bison 语法文件预生成、以及应用 OpenHarmony 适配 patch, +结果直接入库到 `libnl/` 目录。因此日常构建只需 `gn`/`ninja` 直接 +编译 `BUILD.gn`,**不依赖** autotools / configure / flex / bison 等主机工具, +也不在 `gn gen` 阶段执行任何脚本。 + +> 历史背景:旧方案在每次 `gn gen` 时通过 `BUILD.gn` 的 `exec_script("install.sh")` +> 重复解压 tarball 并运行 `autogen.sh` + `configure`,缓慢且强依赖主机环境。 +> 现已由 `prepare.sh`(仅升级时手动运行)替代。 + +## 仓库内容约定 + +- `libnl/`:已准备好的源码树(含 `include/config.h`、`include/netlink/version.h` + 以及预生成的语法文件 `lib/route/{pktloc_grammar,pktloc_syntax}.{c,h}`、 + `lib/route/cls/{ematch_grammar,ematch_syntax}.{c,h}`)。直接被 `BUILD.gn` 编译。 +- `prepare.sh`:分三阶段的环境准备脚本(升级时手动运行,构建期不调用)。 +- `solve-oh-compile-problem3_11_0.patch`:当前生效的 OpenHarmony 适配 patch。 +- `archive/patches/`:历史各版本(解决冲突后的)适配 patch 归档,供后续升级参考复用。 +- `archive/libnl-.sha256sums`:**整个上游 tar 包**的 SHA256 校验和(标准 + `sha256sum` 单行格式、记录规范包名),随上游基线 commit 入库,供整包可信校验。 +- `COPYING`:LGPL v2.1 许可文件。 +- 上游 tarball **不入库**;上游版本/地址记录在 `README.OpenSource`(其元数据结构 + 保持不变,**不**在其中记录 SHA256)。源码可信校验改由上述 `*.sha256sums` + 整包校验和 + git「上游基线 commit」提供。 + +## 升级流程:三个独立 commit + +为清晰区分 **上游原始代码 / 构建环境产物 / OHOS 适配** 三类变更,每次升级按 +`upstream → build → patch` 三个阶段进行,**每个阶段各提交一次独立 commit**。 +这样 `git diff` 在三个 commit 之间分别只显示一类变更,溯源清晰、便于审计与回溯。 + +以从 `3.11.0` 升级到新版本 `` 为例: + +### 0. 准备:更新脚本变量与版本元信息 + +- 编辑 `prepare.sh` 顶部变量:`LIBNL_VERSION`、`UPSTREAM_URL`、`EXTRACTED_DIR` + (若上游归档顶层目录名变化)。 +- 更新 `README.OpenSource` 的 `Version Number` 与 `Upstream URL` + (**不**新增 SHA256 字段,保持其元数据结构不变)。 +- 更新 `bundle.json` 的 `version`(如适用)。 + +### 1. commit 1 —— 纯上游源码基线(可信溯源) + +```sh +# 方式一:使用本地已下载 tarball;方式二:省略路径,从 Upstream URL 下载 +./prepare.sh upstream /path/to/libnl-.tar.gz +``` + +该阶段会清空 `libnl/` 并解压**未经任何修改**的上游源码树(不含 config.h、 +不含语法生成物、不含 OHOS 改动)。脚本会: + +- 对**整个上游 tar 包**计算 SHA256,写入 `archive/libnl-.sha256sums` + (标准 `sha256sum` 单行格式、记录规范包名),随本 commit 入库。 +- 该整包校验和可用于: + ```sh + # 将上游 tar 包按规范名(如 libnl3_11_0.tar.gz)放到 archive/ 下后校验 + ( cd archive && sha256sum -c libnl-.sha256sums ) + ``` + 或直接把该值与**上游社区发布的 sha256sum 文件**比对,确认 tar 包可信。 + > 说明:校验对象是**整包 tar.gz**,与上游社区通常发布的校验形式一致;因此 + > 无需逐文件计算,也不受入库后 configure/patch 改动的影响。 + +- 提交: + ```sh + git add libnl/ archive/libnl-.sha256sums README.OpenSource + git commit -m "Import libnl upstream" + ``` + +### 2. commit 2 —— 构建环境产物(configure + 语法文件) + +```sh +./prepare.sh build +``` + +该阶段运行 `autogen.sh` + `configure`(产出 `config.h`/`version.h`)、flex/bison +预生成 8 个语法文件、清理 autotools 中间产物,并自动调整 `libnl/.gitignore` +使上述生成物可入库。 + +- 本 commit 的 diff 仅为「新增 config.h + version.h + 8 个语法文件 + .gitignore 调整」, + 不含任何 OHOS 源码改动。 +- 提交: + ```sh + git add libnl/ + git commit -m "libnl build env (configure + grammars)" + ``` + +### 3. commit 3 —— OHOS 适配 patch + +```sh +./prepare.sh patch +``` + +该阶段应用 `solve-oh-compile-problem3_11_0.patch`,并把(解决冲突后的)patch +归档到 `archive/patches/solve-oh-compile-problem-.patch`。 + +- **若 patch 冲突**:脚本会报错中止。请手动解决冲突、更新根目录的 + `solve-oh-compile-problem*.patch`(或依据上游变更重做),再重新运行 + `./prepare.sh patch`。当前 patch 涉及的文件(升级后重点检查是否仍兼容): + - `lib/utils.c`、`lib/route/mdb.c`、`lib/route/neigh.c`、`lib/route/link/bridge.c` + - `include/base/nl-base-utils.h` + - `include/config.h`(交叉编译值修正:`HAVE_STRERROR_L`、 + `HAVE_DECL_GETPROTOBYNAME_R`、`HAVE_DECL_GETPROTOBYNUMBER_R` 置 0) + - `include/nl-priv-dynamic-core/nl-core.h`、`src/lib/utils.c` +- 本 commit 的 diff 仅为「OHOS 对上游源码的适配改动 + 归档 patch」。 +- 提交: + ```sh + git add libnl/ archive/patches/ + git commit -m "libnl OHOS adaptation patch" + ``` + +> 提示:`upstream`/`build`/`patch` 操作的文件集互不相交,`build` 与 `patch` 顺序 +> 理论上可换;但务必保持 `configure`(在 build 阶段)早于 `patch`,因为 patch 要 +> 修正 configure 生成的 `config.h` 交叉编译值。 + +### 4. 同步 `BUILD.gn` 的 `sources` 列表 + +若上游新增/删除/重命名了 `lib/`、`src/lib/` 下的 `.c` 文件,需相应增删 `BUILD.gn` +中 `sources` 条目。语法文件(`pktloc_*`、`ematch_*`)已作为预生成源直接列入 +`sources`,无需 `action()`。此改动可并入 commit 3 或单独提交。 + +### 5. 构建验证 + +```sh +./build.sh --product-name --build-target libnl_share +``` + +验证要点: + +- `gn gen` 阶段不再执行任何脚本(无 `install.sh` / autotools 调用)。 +- 即使主机 **未安装** flex / bison / autoconf,也能成功编译出 `libnl_share` 动态库。 + +## 一次性快速搭建(非升级场景) + +若只是想在本地一次性重建已准备好的源码树(不关心三 commit 拆分): + +```sh +./prepare.sh all /path/to/libnl-3.11.0.tar.gz +``` + +## 设计要点(为何这样做) + +- `configure` 产物中,GN 构建实际只需要 `include/config.h` 与 + `include/netlink/version.h` 两个文件;其余 Makefile / libtool / `.pc` 等 + 对 GN 构建无用,故 `build` 阶段在生成后即清理,保持源码树精简、 + 避免泄漏主机路径。 +- `config.h` 由 `configure` 在主机生成后,再由 patch 修正为交叉编译正确值, + 因此 **顺序必须是先 `configure` 后 `patch`**。 +- 三阶段对应三 commit,使「上游代码」「构建产物」「OHOS 适配」在版本历史中 + 天然解耦:审计上游变更只看 commit 1,审计 OHOS 改动只看 commit 3。 diff --git a/install.sh b/install.sh deleted file mode 100755 index faaea07..0000000 --- a/install.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation version 2.1 -# of the License. -# -# Copyright(c) 2023 Huawei Device Co., Ltd. - -set -e -cd $1 -touch test.lock -( - flock -x 180 -if [ -d "libnl" ];then - rm -rf libnl -fi -tar xvf libnl-libnl3_11_0.tar.gz -mv libnl-libnl3_11_0 libnl -cd $1/libnl -./autogen.sh -./configure -patch -p1 < $1/solve-oh-compile-problem3_11_0.patch --fuzz=0 --no-backup-if-mismatch -exit 0 -)180>test.lock diff --git a/libnl-3.7.0.tar.gz b/libnl-3.7.0.tar.gz deleted file mode 100644 index f7ab177..0000000 Binary files a/libnl-3.7.0.tar.gz and /dev/null differ diff --git a/libnl-libnl3_11_0.tar.gz b/libnl-libnl3_11_0.tar.gz deleted file mode 100644 index bb71dc4..0000000 Binary files a/libnl-libnl3_11_0.tar.gz and /dev/null differ diff --git a/libnl/.clang-format b/libnl/.clang-format new file mode 100644 index 0000000..ad32417 --- /dev/null +++ b/libnl/.clang-format @@ -0,0 +1,133 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# clang-format configuration file. Intended for clang-format >= 11. +# +# 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 +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 + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 8 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false + +# Taken from: +# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ tools/ \ +# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \ +# | LC_ALL=C sort -u +ForEachMacros: + - 'list_for_each_safe' + - 'nl_list_for_each_entry' + - 'nl_list_for_each_entry_safe' + - 'nla_for_each_attr' + - 'nla_for_each_nested' + - 'nlmsg_for_each' + - 'nlmsg_for_each_attr' + - 'nlmsg_for_each_msg' + +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +IndentGotoLabels: false +IndentPPDirectives: None +IndentWidth: 8 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 8 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true + +# Taken from git's rules +PenaltyBreakAssignment: 10 +PenaltyBreakBeforeFirstCallParameter: 30 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakString: 10 +PenaltyExcessCharacter: 100 +PenaltyReturnTypeOnItsOwnLine: 60 + +PointerAlignment: Right +ReflowComments: false +SortIncludes: false +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatementsExceptForEachMacros +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp03 +TabWidth: 8 +UseTab: Always +... diff --git a/libnl/.flake8 b/libnl/.flake8 new file mode 100644 index 0000000..afddf49 --- /dev/null +++ b/libnl/.flake8 @@ -0,0 +1,7 @@ +[flake8] +extend-ignore = + E203 + E501 +filename = + *.py + *.py.in diff --git a/libnl/.git-blame-ignore-revs b/libnl/.git-blame-ignore-revs new file mode 100644 index 0000000..a5b6f4c --- /dev/null +++ b/libnl/.git-blame-ignore-revs @@ -0,0 +1,7 @@ +# List of commits that did automated reformatting. You can ignore them +# during git-blame with `--ignore-rev` or `--ignore-revs-file`. +# +# $ git config --add 'blame.ignoreRevsFile' '.git-blame-ignore-revs' +# +3c753e3c94b01c63bf99b2709f4780e2df4608eb +53da4712439dfd1ccdfeb8d4ca3027082a826e29 diff --git a/libnl/.github/workflows/ci.yml b/libnl/.github/workflows/ci.yml new file mode 100644 index 0000000..6ad5fd2 --- /dev/null +++ b/libnl/.github/workflows/ci.yml @@ -0,0 +1,223 @@ +name: libnl3-ci + +on: + push: + pull_request: + +env: + NLTST_IN_CI: 1 + +jobs: + clang-format: + runs-on: ubuntu-latest + container: + image: fedora:40 + steps: + - name: Install packages + run: | + sudo dnf install -y \ + clang-tools-extra \ + git + + - name: Check out repository code + uses: actions/checkout@v3 + + - name: Setup git + run: | + git config --global --add safe.directory "$PWD" + + - name: Formatting with clang-format + run: | + clang-format --version + ./tools/clang-format.sh -n + + ci: + strategy: + matrix: + include: + - cc: gcc + - cc: clang + runs-on: ubuntu-latest + steps: + + - name: Install packages + run: | + sudo apt-get update + sudo apt-get -y --no-install-recommends install \ + check \ + valgrind \ + libtool-bin + + - name: Check out repository code + uses: actions/checkout@v3 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: 3.x + + - name: Lint Python + if: ${{ matrix.cc == 'gcc' }} + run: | + python3 -m pip install flake8 + flake8 . --count --show-source --statistics + + - name: Code formatting with Python black + if: ${{ matrix.cc == 'gcc' }} + run: | + python3 -m pip install black==22.12.0 + black --check . + + - name: Build + run: | + set -x + + export CC="${{ matrix.cc }}" + export CFLAGS="-DNL_MORE_ASSERTS=1000 -O2 -Werror -std=gnu11 -fexceptions" + CONFIGURE_ARGS= + if [ "$CC" = "clang" ]; then + CFLAGS="$CFLAGS -Wno-error=unused-command-line-argument -Wno-error=unused-function" + export LDFLAGS="-Wl,--no-undefined-version,--fatal-warnings" + CONFIGURE_ARGS="--enable-debug=no" + else + export LDFLAGS="-Wl,--no-undefined-version" + fi + + ./autogen.sh + ./configure $CONFIGURE_ARGS + make -j 15 + shell: bash + + - name: Build Unit Tests + run: | + make -j 15 check-build + + - name: Run Unit Tests + run: | + set -x + export NLTST_SEED_RAND= + for i in `seq 1 5`; do + tests/check-direct + tests/check-all + make -j 15 check || (cat ./test-suite.log; false) + done + + - name: Run Unit Tests w/Valgrind + run: | + set -x + export NLTST_SEED_RAND= + CK_FORK=no libtool --mode=execute valgrind --error-exitcode=66 --leak-check=full -s --show-leak-kinds=all ./tests/check-direct + CK_FORK=no libtool --mode=execute valgrind --error-exitcode=66 --leak-check=full -s --show-leak-kinds=all ./tests/check-all + shell: bash + + - name: Install packages for Release + run: | + python3 -m pip install \ + asciidoc \ + graphviz \ + pygments + sudo apt-get -y --no-install-recommends install \ + doxygen \ + graphviz \ + mscgen \ + source-highlight + + - name: Build Release + run: | + set -x + git clean -fdx + export CC="${{ matrix.cc }}" + NO_GPG_SIGN=1 ./tools/build_release.sh BuildAll + + - name: Build out-of-tree and disable-static + run: | + set -x + git clean -fdx + export CC="${{ matrix.cc }}" + export CFLAGS="-Werror -std=gnu11 -fexceptions" + if [ "$CC" = "clang" ]; then + CFLAGS="$CFLAGS -Wno-error=unused-command-line-argument -Wno-error=unused-function" + fi + + ./autogen.sh + mkdir build + cd build + ../configure --disable-static + make -j 15 + make -j 15 check-build + export NLTST_SEED_RAND= + make -j 15 check || (cat ./test-suite.log; false) + + - name: Link with mold + run: | + sudo apt-get -y --no-install-recommends install \ + mold + git clean -fdx + export LDFLAGS="-fuse-ld=mold -Wl,--fatal-warnings" + ./autogen.sh + ./configure + make -j 15 V=1 + + - run: echo "🍏 This job's status is ${{ job.status }}." + + ci-alpine: + runs-on: ubuntu-latest + container: + image: alpine:latest + steps: + - name: Install packages + run: | + apk add \ + autoconf \ + automake \ + bash \ + bison \ + check-dev \ + flex \ + gcc \ + git \ + libtool \ + linux-headers \ + make \ + musl-dev \ + pkgconfig + + - name: Check out repository code + uses: actions/checkout@v3 + + - name: Setup git + run: | + git config --global --add safe.directory "$PWD" + + - name: Build + run: | + set -x + + export CC="${{ matrix.cc }}" + export CFLAGS="-DNL_MORE_ASSERTS=1000 -O2 -Werror -std=gnu11 -fexceptions" + if [ "$CC" = "clang" ]; then + CFLAGS="$CFLAGS -Wno-error=unused-command-line-argument -Wno-error=unused-function" + export LDFLAGS="-Wl,--no-undefined-version,--fatal-warnings" + else + export LDFLAGS="-Wl,--no-undefined-version" + fi + + ./autogen.sh + ./configure + make -j 15 + + - name: Build Unit Tests + run: | + make -j 15 check-build + + - name: Run Unit Tests + run: | + set -x + export NLTST_SEED_RAND= + for i in `seq 1 5`; do + tests/check-direct + # unshare() does not work (EPERM). This test currently cannot pass + # (odd). + # tests/check-all + # make -j 15 check || (cat ./test-suite.log; false) + done diff --git a/libnl/.gitignore b/libnl/.gitignore new file mode 100644 index 0000000..cb825cd --- /dev/null +++ b/libnl/.gitignore @@ -0,0 +1,152 @@ +*~ +.deps +.libs +.dirstamp +*.la +*.lo +*.o +*.swp +Makefile +Makefile.in +defs.h.in +defs.h.in~ +cscope.* +test-suite.log + +/*.pc +/aclocal.m4 +/autom4te.cache +/build-aux/ +/config.* +/configure +/doc/*.html +/doc/Doxyfile +/doc/aclocal.m4 +/doc/api/*.css +/doc/api/*.dot +/doc/api/*.html +/doc/api/*.js +/doc/api/*.map +/doc/api/*.md5 +/doc/api/*.png +/doc/api/*.tag +/doc/api/_formula* +/doc/api/doxygen.svg +/doc/api/formula.repository +/doc/api/jquery.js +/doc/asciidoc.tmp +/doc/autom4te.cache/ +/doc/build-aux/ +/doc/config.* +/doc/configure +/doc/images/*.odg +/doc/images/asciidoc__*.png +/doc/images/core__* +/doc/libnl.dict +/include/config.h +/include/config.h.in +/include/netlink/version.h +/include/stamp-h1 +/lib/defs.h +/lib/lex.yy.c +/lib/libnl-*.so* +/lib/libnl.so* +/lib/route/cls/ematch_grammar.[ch] +/lib/route/cls/ematch_syntax.[ch] +/lib/route/pktloc_grammar.c +/lib/route/pktloc_grammar.h +/lib/route/pktloc_syntax.c +/lib/route/pktloc_syntax.h +/lib/stamp-h1 +/libnl-1.pc +/libtool +/m4/libtool.m4 +/m4/lt*.m4 +/python/**/capi* +/python/build/ +/python/setup.py +/src/genl-ctrl-list +/src/idiag-socket-details +/src/nf-ct-add +/src/nf-ct-events +/src/nf-ct-list +/src/nf-exp-add +/src/nf-exp-delete +/src/nf-exp-list +/src/nf-log +/src/nf-monitor +/src/nf-queue +/src/nl-addr-add +/src/nl-addr-delete +/src/nl-addr-list +/src/nl-class-add +/src/nl-class-delete +/src/nl-class-list +/src/nl-classid-lookup +/src/nl-cls-add +/src/nl-cls-delete +/src/nl-cls-list +/src/nl-fib-lookup +/src/nl-link-enslave +/src/nl-link-ifindex2name +/src/nl-link-list +/src/nl-link-name2ifindex +/src/nl-link-release +/src/nl-link-set +/src/nl-link-stats +/src/nl-list-caches +/src/nl-list-sockets +/src/nl-monitor +/src/nl-neigh-add +/src/nl-neigh-delete +/src/nl-neigh-list +/src/nl-neightbl-list +/src/nl-nh-list +/src/nl-pktloc-lookup +/src/nl-qdisc-add +/src/nl-qdisc-delete +/src/nl-qdisc-list +/src/nl-route-add +/src/nl-route-delete +/src/nl-route-get +/src/nl-route-list +/src/nl-rule-list +/src/nl-tctree-list +/src/nl-util-addr +/tags +/tests/check-all +/tests/check-all.log +/tests/check-all.trs +/tests/check-direct +/tests/check-direct.log +/tests/check-direct.trs +/tests/test-*.log +/tests/test-*.trs +/tests/test-cache-mngr +/tests/test-complex-HTB-with-hash-filters +/tests/test-create-bond +/tests/test-create-bridge +/tests/test-create-geneve +/tests/test-create-ifb +/tests/test-create-ip6tnl +/tests/test-create-ipgre +/tests/test-create-ipgretap +/tests/test-create-ipip +/tests/test-create-ipvlan +/tests/test-create-ipvti +/tests/test-create-macsec +/tests/test-create-macvlan +/tests/test-create-macvtap +/tests/test-create-sit +/tests/test-create-veth +/tests/test-create-vlan +/tests/test-create-vrf +/tests/test-create-vxlan +/tests/test-create-xfrmi +/tests/test-delete-link +/tests/test-genl +/tests/test-loopback-up-down +/tests/test-nf-cache-mngr +/tests/test-socket-creation +/tests/test-suite.log +/tests/test-u32-filter-with-actions diff --git a/libnl/.indent.pro b/libnl/.indent.pro new file mode 100644 index 0000000..ee5d177 --- /dev/null +++ b/libnl/.indent.pro @@ -0,0 +1 @@ +-linux -il0 -cs -lp -cbi0 diff --git a/libnl/COPYING b/libnl/COPYING new file mode 100644 index 0000000..4362b49 --- /dev/null +++ b/libnl/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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 Library or any portion +of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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 with +this License. + + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/libnl/ChangeLog b/libnl/ChangeLog new file mode 100644 index 0000000..3d93d1c --- /dev/null +++ b/libnl/ChangeLog @@ -0,0 +1,336 @@ +ChangeLog discontinued, git history can be found here: +https://github.com/thom311/libnl + +Summary of Changes from 1.0-pre6 to 1.0-pre7 +================================================ + Thomas Graf + o Generic netlink support + o Route Addition/Deletion + o Added nl_cache_subset() + o Have nl_object_clone() make real clones without + sharing any data. + o Remove old nl_object_alloc() not based on a object + operations to avoid bugs due to missing init. + o Added nl-list-caches utility + o Removed nlmsg_build_no_hdr(), duplicate + o Reworked message interface + o Fixed nlmsg_put() and genlmsg_put() to correctly reserve + tail room for user specific headers. + o Added nl_cache_move() + o Renamed nl_cache_delete() to nl_cache_remove() (no API break) + o Fixed reference counting while objects stay in caches. + o Object marking + o Moved attribute mask for objects into generic structure + o nl-list-caches: List available dump procedures + o Use PAGE_SIZE as initial buffer size when reading from + netlink socket + o Double buffer size when recv() returns MSG_TRUNC + o Replaced filter object operation with new compare operation + capable of listing differences between two objects + o Added nl_object_identical() to check if two objects are + identical from a uniqueness point of view + o Added nl_object_diff() returning a bitmask of differences in + attributes + o Added nl_object_attr_list() generating a list of attribute + name the object has assigned + o Cache updates based on event notifications, code based on + Patrick McHardy's patches + o Cache Manager + o Added NL_AUTO_PID, NL_AUTO_SEQ for convenience + o Disable MSG_PEEK by default and provide nl_socket_enable_msg_peek() + o Fixed nl_recvmsgs() to return 0 when interrupted via NL_STOP or + NL_SKIP + o Fixed nl_recvmsgs() to stop reading after parsing if not in the + middle of a multipart message. + o Fixed nl_recvmsgs() to not stop after receiving an ACK + o Fixed nl_recvmsgs() to not blindly discard remaining messages + if a NLMSG_DONE message is received. + + Petr Gotthard + Siemens AG Oesterreich + o Fix u32 to properly handle multiple keys + o rtnl_htb_set_(r|c)buffer() + o Fixed MTU handling in HTB class, problem pointed out + by Edouard Thuleau + + Zilvinas Valinskas + o Fix wrong msg_namelen in nl_recv() + o Fix memory leak in flnl_request_put() + + Helmut Schaa + o Fix for using libnl from within C++ + + Patrick McHardy + o *_alloc_cache(): Only refill cache if handle is provided + + James Oakley + o Fix rtnl_link_set_arptype() typo + + Philip Craig + o Change address family type from char to int + o Fix the error handling when the build fails. + o add nl_cache_mngr_get_fd() + o add netfilter support + o add netfilter conntrack support + o add netfilter log support + +Summary of Changes from 1.0-pre5 to 1.0-pre6 +================================================ + Christopher Aillon + o Use $(libdir) instead of $(prefix)/lib for 64bit awesomeness. + + Thomas Graf + o Extend nl_msg to include source address, destination address + and the protocol being used. + o Make nl_send*() take a nl_msg instead of a nlmsghdr (API BREAK) + o Change callbacks to take a nl_msg instead of source address + and nlmsghdr (API BREAK) + o caches must specify the protocol they're hooked up from now on + if they intend to be associated with message types. + o cache_mngt_associate now takes the protocol besides the message + type to allow for multiple protocols to be supported (API BREAK) + o overwrite destination address in nl_send() when specified in the + message itself, allows for unbound addressing. + o Support for netlink based fib_lookup() + o Documentation fixes + o Fix double nlmsg_free() in nl_recvmsgs() while receiving + a multipart message and the read was interrupted. + o Change cache operations to store names for message types. + o Provide interface to convert message type to character string. + o Add dp_dump_msgtype to prefix each dumped element with a + pretty printed message type. + o netlink fib lookup support + o nl_recvmsgs() debugging + o use nl_cachemngt_type2name() when pretty printing netlink header + o Routing protocol translations. + o Routing metric translations. + o Revised route dumping + o Nexthop flag translations. + o Add support for IFF_DORMANT + + Petr Gotthard + Siemens AG Oesterreich + o Fix access to obj after freeing it + o Fix u32 selector access after realloc() + o Fix missing out-of-memory error handling in various places + o Enhance nl-monitor to have group selection selectable and + demonstrate usage of select() + o Don't ignore IFF_RUNNING any longer + o fw classifier support + + Patrick McHardy + o Fix conflicting types for __u64 + o Fix printf format string warnings + o Fix object cloning + o Deal with structure padding in nl_object_clone + o Fix nl_addr leak + o Set ce_msgtype in all parsed objects + o Fix addr flag filter + o Fix RTNLGRP definitions (was based on broken kernel version) + o Export nl_get_errno() + o Add function to get/set peer pid + o Add IFF_LOWER_UP + o Add/export some missing accessor functions + o print /0 prefix in nl_addr2str() + o Fix invalid free in nl_addr_parse for AF_UNSPEC addresses + o Use __str2flags instead of __str2type in rtnl_link_str2flags() + o Make sure object and filter types match in nl_object_match() + o Add support for credential passing over netlink sockets (API BREAK) + o Add support for custom dump callbacks + o Add NL_DUMP_ENV format + + Michael Biebl + "Alex V. Myltsev" + o Makefile fixes + + +Summary of Changes from 1.0-pre4 to 1.0-pre5 +================================================ + Thomas Graf + o Use minimized local copies for , , + and to avoid compile troubles with + applications including + Reported by Christopher Aillon. + +Summary of Changes from 1.0-pre3 to 1.0-pre4 +================================================ + Thomas Graf + o Fix wrong rtnl_addr_set_prefixlen() external declaration, + reported by Dan Williams. + o Fix nl_addr_parse() to not change the original string + for prefixes. + o Do not build documentation per default, but have the user + issue 'make gendoc' + o Assume neighbours to be permanent, set NUD_PERMANENT if not + specified otherwise. + +Summary of Changes from 1.0-pre2 to 1.0-pre3 +================================================ + Thomas Graf + o Fix SFQ parser to allocate qdisc options. + o Fix rule statistics dumping to not call itself. + o Complete Netem qdisc interface. + o Add rtnl_*_put() and rtnl_*_free() to increase readability. + o Cleanup of nl-* tools + o Fix inclusion guards of route/neightbl.h + o Fix nl_connect() to only modify rx/tx socket buffers if not + already modified by the user. + o Fix wrong nl_handle_alloc() prototype. + o Fix typo in route/addr.c causing label to be marked as + local address. + o Use ~0UL as default prefix length instead of 0. + o Fix neighbour message parser to correctly store core. + attributes and provide them again. + o Fix neighbour message parser to correctly guess address family. + to make it compatible with nl_addr_parse() and ether llc + addresses. + o Add rtnl_route_table2str(), rtnl_route_str2table(). + o Add nl_cache_nitems_filter() to find out if a filter produces + any matches. + o Remove rtnl_rule_set_(dst|src)_str() (obsolete). + o Remove scope and protocol field of routing rule. + o Complete routing rules module. + o Move realms translations from route to rtnl module. + +Summary of Changes from 1.0-pre1 to 1.0-pre2 +================================================ + Thomas Graf + o More API documentation + o Added flags argument to rtnl_addr_(add|build_add_request)(). + o Added rtnl_addr_(set|get)_multicast(). + o Moved scope translations routines from route/route.c to + route/rtnl.c, required by other modules as well. + o Removed old rtattr bits from rtnetlink-kernel.h + o Customized libnl.css for doxygen documentation + o Removed non-reentrant translation routines, only bloating + the code and too risky. + o Fixed wrong version number from 1.0-pre1. + o Re-enabled unfinished policer module. + o Reworked TBF module, automatic caluclation of transmit times, + limit setable via latency, automatic cell size calculation, + options TLV generation. (untested) + o Renamed nl_xmittime() to rtnl_tc_calc_txtime(). + o Renamde nl_build_rtable() to rtnl_tc_build_rate_table() + + Petr Gotthard , + Siemens AG Oesterreich + o Fix symlinks to libnl library files to be moveable + o Fix extern struct prototypes meant to be static. + o Add empty install target to src/Makefile + + Simon Stelling + o Use LIBDIR instead of $(prefix)/lib for users to allow librariers + into $(prefix)/lib64. + +Summary of Changes from 0.5.0 to 1.0-pre1 +================================================ + Thomas Graf + o Uncountable number of changes, rewrite of certain modules, + several major API breakages + + Petr Gotthard , + Siemens AG Oesterreich + o added class_build, rtnl_class_build_add_request, rtnl_class_add + o added HTB (Hierarchical Token Bucket) class support + o added nl_xmittime, nl_build_rtable + o added nl_data_append to realloc a nl_data structure + o added rtnl_rcopy_ratespec as reverse to rtnl_copy_ratespec + o fixed byte order conversion of rtnl_filter.protocol + o SuSE and Fedora Linux compile fixes + o fixed u32 classifier support + o added rtnl_u32_set_handle, rtnl_u32_set_classid, rtnl_u32_set_flags + and several rtnl_u32_add_key_... operations to u32 classifier + +Summary of Changes from 0.4.4 to 0.5.0 +================================================ + Thomas Graf + o API documentation + o nl_cache_filter to manually filter on a object + o partial routing support + o routing rules support + o Properly set address family when setting addresses + o debug flag and some rare messages, more to come + o make error message verboseness configurable + o tc fixes to wait for ack + o cleanup and adaption of address code to latest internal API + o various cleanups + o dozens of API breakages (better now than later) + + Daniel Hottinger + o arch 64bit printf length modifier fixes + + Baruch Even , + Mediatrix Telecom, inc. + o address support + +Summary of changes from 0.4.3 to 0.4.4 +================================================ + Thomas Graf : + o overall cleanups for better code quality + o replace dump_(brief|full|with_stats) ops with + dump[NL_DUMP_MAX] array to allow further additions without + breaking the ABI. + o add of send_ack callback, called whenever when oppenent + asks for an ACK. + o make nl_parse_rtattr initialize the tb buffer like in the + kernel, caller must no longer take care of it. + o remove nl_addrnattr (obsolete) + o fixed nl_msg_append_raw to correctly calculate length + for raw data not aligned to NLMSG_ALIGN + o fix memory leak in nl_recv in case of errors + o correctly check sequence numbers if more than one message + was sent out before the answer is being received. + o add workaround for buggy netlink applications not properly + setting NLM_F_MULTI. + +Summary of changes from 0.4.2 to 0.4.3 +================================================ + + Thomas Graf : + o use parser_param in nl_cache_parse + o EGP: dump nfilters attribute + o allow retrieving of filters attached to classes via + FILTER_CACHE_PARENT(C) cache argument + o filter message building API + +Summary of changes from 0.4.1 to 0.4.2 +================================================ + + Baruch Even : + o memory leak fix in nl_parse_rtattr + o reset padding to 0 when appending raw data to a nl_msg + o avoid overwriting nlmsg ptr when buffer extending fails + o typo fixes + o create symlinks libnl.so.0 and libnl.so + + Thomas Graf : + o EGP classifier support + o avoid null pointer in printf call + o added nl_cache_parse to put nl_msg's into a cache + o added rtnl_filter_build to build a nl_msg filter message + o correctly install header files + o nl_msg_payload/nl_msg_payloadlen to access nl_msg payload + o nl_parse_nested macro to simplify nested TLV parsing + o NL_ERROR_ASSERT compile flag to assert(0) on errors + o rta alignment fix in nl_msg_append_tlv + o added nl_msg_parse_rtattr as shortcut for nl_parse_rtattr + for nl_msg API + o added nl_parse_nested for nested TLVs + o added RTA_ARRAY_ELEMS macro to calculate array length + for array TLVs + o added nl_wait_for_ack to wait for the next ack + o added rtnl_link_build_change_request(...) + o added rtnl_neigh_build_*_request + o converted neighbour code to use nl_wait_for_ack + o cb_recvmsgs_ow callback to overwrite internal calls to + nl_recvmsgs_def + o cb_seq_check callback to overwrite default sequence checking + o added nl_parser_param as argument for message parsers including + a callback to be called upon successful parsing of a message. + Removes the requirement of having all parsed messages to be added + to a cache. + o added cb_recv_ow and nl_send_ow callbacks to overwrite internal + calls to nl_recv and nl_send. + + Jamal Hadi Salim + o Linux 2.4 compile fixes diff --git a/libnl/Makefile.am b/libnl/Makefile.am new file mode 100644 index 0000000..227fa56 --- /dev/null +++ b/libnl/Makefile.am @@ -0,0 +1,1213 @@ +# -*- Makefile -*- + +ACLOCAL_AMFLAGS = -I m4 + +lib_LTLIBRARIES = +noinst_LTLIBRARIES = +check_LTLIBRARIES = + +check_PROGRAMS = +check_programs = +check_local = +check_build = + +bin_PROGRAMS = +sbin_PROGRAMS = +noinst_PROGRAMS = + +TESTS = + +CLEANFILES = +EXTRA_DIST = + +DISTCHECK_CONFIGURE_FLAGS = \ + --disable-dependency-tracking + +pkgconfig_DATA = \ + libnl-3.0.pc \ + libnl-genl-3.0.pc \ + libnl-idiag-3.0.pc \ + libnl-nf-3.0.pc \ + libnl-route-3.0.pc \ + libnl-xfrm-3.0.pc \ + $(NULL) + +warn_cppflags = \ + -Wall \ + -Wextra \ + \ + -Wdeclaration-after-statement \ + -Wmissing-prototypes \ + -Wpointer-arith \ + -Wvla \ + \ + -Wno-unused-parameter \ + $(NULL) + +defines_cppflags = \ + -D_NL_SYSCONFDIR_LIBNL=\"$(sysconfdir)/libnl\" \ + -D_NL_PKGLIBDIR=\"$(pkglibdir)\" \ + $(NULL) + +############################################################################### + +libnlincludedir = $(includedir)/libnl@MAJ_VERSION@ + +libnlinclude_netlinkdir = $(libnlincludedir)/netlink +libnlinclude_netlink_HEADERS = \ + include/netlink/addr.h \ + include/netlink/attr.h \ + include/netlink/cache-api.h \ + include/netlink/cache.h \ + include/netlink/data.h \ + include/netlink/errno.h \ + include/netlink/handlers.h \ + include/netlink/hash.h \ + include/netlink/hashtable.h \ + include/netlink/list.h \ + include/netlink/msg.h \ + include/netlink/netlink-compat.h \ + include/netlink/netlink-kernel.h \ + include/netlink/netlink.h \ + include/netlink/object-api.h \ + include/netlink/object.h \ + include/netlink/socket.h \ + include/netlink/types.h \ + include/netlink/utils.h \ + include/netlink/version.h \ + $(NULL) +libnlinclude_netlink_fib_lookupdir = $(libnlincludedir)/netlink/fib_lookup +libnlinclude_netlink_fib_lookup_HEADERS = \ + include/netlink/fib_lookup/lookup.h \ + include/netlink/fib_lookup/request.h \ + $(NULL) +libnlinclude_netlink_genldir = $(libnlincludedir)/netlink/genl +libnlinclude_netlink_genl_HEADERS = \ + include/netlink/genl/ctrl.h \ + include/netlink/genl/family.h \ + include/netlink/genl/genl.h \ + include/netlink/genl/mngt.h \ + $(NULL) +libnlinclude_netlink_idiagdir = $(libnlincludedir)/netlink/idiag +libnlinclude_netlink_idiag_HEADERS = \ + include/netlink/idiag/idiagnl.h \ + include/netlink/idiag/meminfo.h \ + include/netlink/idiag/msg.h \ + include/netlink/idiag/req.h \ + include/netlink/idiag/vegasinfo.h \ + $(NULL) +libnlinclude_netlink_netfilterdir = $(libnlincludedir)/netlink/netfilter +libnlinclude_netlink_netfilter_HEADERS = \ + include/netlink/netfilter/ct.h \ + include/netlink/netfilter/exp.h \ + include/netlink/netfilter/log.h \ + include/netlink/netfilter/log_msg.h \ + include/netlink/netfilter/netfilter.h \ + include/netlink/netfilter/nfnl.h \ + include/netlink/netfilter/queue.h \ + include/netlink/netfilter/queue_msg.h \ + $(NULL) +libnlinclude_netlink_routedir = $(libnlincludedir)/netlink/route +libnlinclude_netlink_route_HEADERS = \ + include/netlink/route/action.h \ + include/netlink/route/addr.h \ + include/netlink/route/class.h \ + include/netlink/route/classifier.h \ + include/netlink/route/link.h \ + include/netlink/route/mdb.h \ + include/netlink/route/neighbour.h \ + include/netlink/route/neightbl.h \ + include/netlink/route/netconf.h \ + include/netlink/route/nexthop.h \ + include/netlink/route/nh.h \ + include/netlink/route/pktloc.h \ + include/netlink/route/qdisc.h \ + include/netlink/route/route.h \ + include/netlink/route/rtnl.h \ + include/netlink/route/rule.h \ + include/netlink/route/tc-api.h \ + include/netlink/route/tc.h \ + $(NULL) +libnlinclude_netlink_route_actdir = $(libnlincludedir)/netlink/route/act +libnlinclude_netlink_route_act_HEADERS = \ + include/netlink/route/act/gact.h \ + include/netlink/route/act/mirred.h \ + include/netlink/route/act/nat.h \ + include/netlink/route/act/skbedit.h \ + include/netlink/route/act/vlan.h \ + $(NULL) +libnlinclude_netlink_route_clsdir = $(libnlincludedir)/netlink/route/cls +libnlinclude_netlink_route_cls_HEADERS = \ + include/netlink/route/cls/basic.h \ + include/netlink/route/cls/cgroup.h \ + include/netlink/route/cls/ematch.h \ + include/netlink/route/cls/flower.h \ + include/netlink/route/cls/fw.h \ + include/netlink/route/cls/matchall.h \ + include/netlink/route/cls/police.h \ + include/netlink/route/cls/u32.h \ + $(NULL) +libnlinclude_netlink_route_cls_ematchdir = $(libnlincludedir)/netlink/route/cls/ematch +libnlinclude_netlink_route_cls_ematch_HEADERS = \ + include/netlink/route/cls/ematch/cmp.h \ + include/netlink/route/cls/ematch/meta.h \ + include/netlink/route/cls/ematch/nbyte.h \ + include/netlink/route/cls/ematch/text.h \ + $(NULL) +libnlinclude_netlink_route_linkdir = $(libnlincludedir)/netlink/route/link +libnlinclude_netlink_route_link_HEADERS = \ + include/netlink/route/link/api.h \ + include/netlink/route/link/bonding.h \ + include/netlink/route/link/bridge_info.h \ + include/netlink/route/link/bridge.h \ + include/netlink/route/link/can.h \ + include/netlink/route/link/geneve.h \ + include/netlink/route/link/inet.h \ + include/netlink/route/link/inet6.h \ + include/netlink/route/link/info-api.h \ + include/netlink/route/link/ip6gre.h \ + include/netlink/route/link/ip6tnl.h \ + include/netlink/route/link/ip6vti.h \ + include/netlink/route/link/ipgre.h \ + include/netlink/route/link/ipip.h \ + include/netlink/route/link/ipvlan.h \ + include/netlink/route/link/ipvti.h \ + include/netlink/route/link/macsec.h \ + include/netlink/route/link/macvlan.h \ + include/netlink/route/link/macvtap.h \ + include/netlink/route/link/ppp.h \ + include/netlink/route/link/sit.h \ + include/netlink/route/link/sriov.h \ + include/netlink/route/link/team.h \ + include/netlink/route/link/veth.h \ + include/netlink/route/link/vlan.h \ + include/netlink/route/link/vrf.h \ + include/netlink/route/link/vxlan.h \ + include/netlink/route/link/xfrmi.h \ + $(NULL) +libnlinclude_netlink_route_qdiscdir = $(libnlincludedir)/netlink/route/qdisc +libnlinclude_netlink_route_qdisc_HEADERS = \ + include/netlink/route/qdisc/cbq.h \ + include/netlink/route/qdisc/dsmark.h \ + include/netlink/route/qdisc/fifo.h \ + include/netlink/route/qdisc/fq_codel.h \ + include/netlink/route/qdisc/hfsc.h \ + include/netlink/route/qdisc/htb.h \ + include/netlink/route/qdisc/mqprio.h \ + include/netlink/route/qdisc/netem.h \ + include/netlink/route/qdisc/plug.h \ + include/netlink/route/qdisc/prio.h \ + include/netlink/route/qdisc/red.h \ + include/netlink/route/qdisc/sfq.h \ + include/netlink/route/qdisc/tbf.h \ + $(NULL) +libnlinclude_netlink_xfrmdir = $(libnlincludedir)/netlink/xfrm +libnlinclude_netlink_xfrm_HEADERS = \ + include/netlink/xfrm/ae.h \ + include/netlink/xfrm/lifetime.h \ + include/netlink/xfrm/sa.h \ + include/netlink/xfrm/selector.h \ + include/netlink/xfrm/sp.h \ + include/netlink/xfrm/template.h \ + $(NULL) + +if ENABLE_CLI +libnlinclude_netlink_clidir = $(libnlincludedir)/netlink/cli +libnlinclude_netlink_cli_HEADERS = \ + include/netlink/cli/addr.h \ + include/netlink/cli/class.h \ + include/netlink/cli/cls.h \ + include/netlink/cli/ct.h \ + include/netlink/cli/exp.h \ + include/netlink/cli/link.h \ + include/netlink/cli/mdb.h \ + include/netlink/cli/neigh.h \ + include/netlink/cli/nh.h \ + include/netlink/cli/qdisc.h \ + include/netlink/cli/route.h \ + include/netlink/cli/rule.h \ + include/netlink/cli/tc.h \ + include/netlink/cli/utils.h \ + $(NULL) +endif + +public_headers = \ + $(libnlinclude_netlink_HEADERS) \ + $(libnlinclude_netlink_fib_lookup_HEADERS) \ + $(libnlinclude_netlink_genl_HEADERS) \ + $(libnlinclude_netlink_idiag_HEADERS) \ + $(libnlinclude_netlink_netfilter_HEADERS) \ + $(libnlinclude_netlink_route_HEADERS) \ + $(libnlinclude_netlink_route_act_HEADERS) \ + $(libnlinclude_netlink_route_cls_HEADERS) \ + $(libnlinclude_netlink_route_cls_ematch_HEADERS) \ + $(libnlinclude_netlink_route_link_HEADERS) \ + $(libnlinclude_netlink_route_qdisc_HEADERS) \ + $(libnlinclude_netlink_xfrm_HEADERS) \ + $(libnlinclude_netlink_cli_HEADERS) \ + $(NULL) + +noinst_HEADERS = \ + include/base/nl-base-utils.h \ + include/linux-private/linux/atm.h \ + include/linux-private/linux/atmapi.h \ + include/linux-private/linux/atmioc.h \ + include/linux-private/linux/atmsap.h \ + include/linux-private/linux/byteorder/little_endian.h \ + include/linux-private/linux/can/netlink.h \ + include/linux-private/linux/const.h \ + include/linux-private/linux/errno.h \ + include/linux-private/linux/ethtool.h \ + include/linux-private/linux/falloc.h \ + include/linux-private/linux/fib_rules.h \ + include/linux-private/linux/gen_stats.h \ + include/linux-private/linux/genetlink.h \ + include/linux-private/linux/hdlc/ioctl.h \ + include/linux-private/linux/if.h \ + include/linux-private/linux/if_addr.h \ + include/linux-private/linux/if_arp.h \ + include/linux-private/linux/if_bridge.h \ + include/linux-private/linux/if_ether.h \ + include/linux-private/linux/if_link.h \ + include/linux-private/linux/if_macsec.h \ + include/linux-private/linux/if_packet.h \ + include/linux-private/linux/if_tunnel.h \ + include/linux-private/linux/if_vlan.h \ + include/linux-private/linux/in.h \ + include/linux-private/linux/in6.h \ + include/linux-private/linux/in_route.h \ + include/linux-private/linux/inet_diag.h \ + include/linux-private/linux/ioctl.h \ + include/linux-private/linux/ip.h \ + include/linux-private/linux/ipv6.h \ + include/linux-private/linux/libc-compat.h \ + include/linux-private/linux/limits.h \ + include/linux-private/linux/lwtunnel.h \ + include/linux-private/linux/mpls.h \ + include/linux-private/linux/mpls_iptunnel.h \ + include/linux-private/linux/neighbour.h \ + include/linux-private/linux/netconf.h \ + include/linux-private/linux/netdevice.h \ + include/linux-private/linux/netfilter.h \ + include/linux-private/linux/netfilter/nf_conntrack_common.h \ + include/linux-private/linux/netfilter/nf_conntrack_tcp.h \ + include/linux-private/linux/netfilter/nfnetlink.h \ + include/linux-private/linux/netfilter/nfnetlink_compat.h \ + include/linux-private/linux/netfilter/nfnetlink_conntrack.h \ + include/linux-private/linux/netfilter/nfnetlink_log.h \ + include/linux-private/linux/netfilter/nfnetlink_queue.h \ + include/linux-private/linux/netlink.h \ + include/linux-private/linux/nexthop.h \ + include/linux-private/linux/pkt_cls.h \ + include/linux-private/linux/pkt_sched.h \ + include/linux-private/linux/posix_types.h \ + include/linux-private/linux/rtnetlink.h \ + include/linux-private/linux/seg6.h \ + include/linux-private/linux/seg6_hmac.h \ + include/linux-private/linux/seg6_iptunnel.h \ + include/linux-private/linux/seg6_local.h \ + include/linux-private/linux/snmp.h \ + include/linux-private/linux/sock_diag.h \ + include/linux-private/linux/socket.h \ + include/linux-private/linux/stddef.h \ + include/linux-private/linux/swab.h \ + include/linux-private/linux/taskstats.h \ + include/linux-private/linux/tc_act/tc_gact.h \ + include/linux-private/linux/tc_act/tc_mirred.h \ + include/linux-private/linux/tc_act/tc_nat.h \ + include/linux-private/linux/tc_act/tc_skbedit.h \ + include/linux-private/linux/tc_act/tc_vlan.h \ + include/linux-private/linux/tc_ematch/tc_em_cmp.h \ + include/linux-private/linux/tc_ematch/tc_em_meta.h \ + include/linux-private/linux/tc_ematch/tc_em_nbyte.h \ + include/linux-private/linux/tc_ematch/tc_em_text.h \ + include/linux-private/linux/types.h \ + include/linux-private/linux/version.h \ + include/linux-private/linux/veth.h \ + include/linux-private/linux/xfrm.h \ + include/nl-aux-core/nl-core.h \ + include/nl-aux-route/nl-route.h \ + include/nl-aux-xfrm/nl-xfrm.h \ + include/nl-default.h \ + include/nl-priv-dynamic-core/cache-api.h \ + include/nl-priv-dynamic-core/nl-core.h \ + include/nl-priv-dynamic-core/object-api.h \ + include/nl-priv-dynamic-route/nl-priv-dynamic-route.h \ + include/nl-priv-static-route/nl-priv-static-route.h \ + $(NULL) + +############################################################################### + +# Hack to avoid using ylwrap. It does not function correctly in combination +# with --header-file= + +lib/route/pktloc_grammar.h: lib/route/pktloc_grammar.c + @true + +lib/route/pktloc_grammar.c: lib/route/pktloc_grammar.l lib/route/.dirstamp + $(AM_V_GEN) $(FLEX) --header-file=lib/route/pktloc_grammar.h $(LFLAGS) -o $@ $< + +lib/route/pktloc_syntax.h: lib/route/pktloc_syntax.c + @true + +lib/route/pktloc_syntax.c: lib/route/pktloc_syntax.y lib/route/.dirstamp + $(AM_V_GEN) $(YACC) -d $(YFLAGS) -o $@ $< + +lib/route/cls/ematch_grammar.h: lib/route/cls/ematch_grammar.c + @true + +lib/route/cls/ematch_grammar.c: lib/route/cls/ematch_grammar.l lib/route/cls/.dirstamp + $(AM_V_GEN) $(FLEX) --header-file=lib/route/cls/ematch_grammar.h $(LFLAGS) -o $@ $< + +lib/route/cls/ematch_syntax.h: lib/route/cls/ematch_syntax.c + @true + +lib/route/cls/ematch_syntax.c: lib/route/cls/ematch_syntax.y lib/route/cls/.dirstamp + $(AM_V_GEN) $(YACC) -d $(YFLAGS) -o $@ $< + +grammar_files_sources = \ + lib/route/cls/ematch_grammar.c \ + lib/route/cls/ematch_syntax.c \ + lib/route/pktloc_grammar.c \ + lib/route/pktloc_syntax.c \ + $(NULL) + +grammar_files_headers = $(grammar_files_sources:%.c=%.h) + +CLEANFILES += \ + $(grammar_files_sources) \ + $(grammar_files_headers) + +EXTRA_DIST += \ + lib/route/cls/ematch_grammar.l \ + lib/route/cls/ematch_syntax.y \ + lib/route/pktloc_grammar.l \ + lib/route/pktloc_syntax.y \ + $(NULL) + +############################################################################### + +default_includes = \ + -I$(srcdir)/include/linux-private \ + -I$(srcdir)/include \ + -I$(builddir)/include \ + $(NULL) + +lib_cppflags = \ + $(warn_cppflags) \ + $(defines_cppflags) \ + $(default_includes) \ + $(NULL) + +lib_LTLIBRARIES += lib/libnl-3.la + +lib_libnl_3_la_SOURCES = \ + lib/addr.c \ + lib/attr.c \ + lib/cache.c \ + lib/cache_mngr.c \ + lib/cache_mngt.c \ + lib/data.c \ + lib/error.c \ + lib/handlers.c \ + lib/hash.c \ + lib/hashtable.c \ + lib/mpls.c \ + lib/mpls.h \ + lib/msg.c \ + lib/nl-core.h \ + lib/nl.c \ + lib/object.c \ + lib/socket.c \ + lib/utils.c \ + lib/version.c \ + $(NULL) +EXTRA_lib_libnl_3_la_DEPENDENCIES = \ + libnl-3.sym +lib_libnl_3_la_CPPFLAGS = \ + $(lib_cppflags) +lib_libnl_3_la_LDFLAGS = \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ + -Wl,--version-script=$(srcdir)/libnl-3.sym + +lib_LTLIBRARIES += lib/libnl-route-3.la + +lib_libnl_route_3_la_SOURCES = \ + lib/fib_lookup/lookup.c \ + lib/fib_lookup/request.c \ + lib/route/act.c \ + lib/route/act/gact.c \ + lib/route/act/mirred.c \ + lib/route/act/nat.c \ + lib/route/act/skbedit.c \ + lib/route/act/vlan.c \ + lib/route/addr.c \ + lib/route/class.c \ + lib/route/classid.c \ + lib/route/cls.c \ + lib/route/cls/basic.c \ + lib/route/cls/cgroup.c \ + lib/route/cls/ematch.c \ + lib/route/cls/ematch/cmp.c \ + lib/route/cls/ematch/container.c \ + lib/route/cls/ematch/meta.c \ + lib/route/cls/ematch/nbyte.c \ + lib/route/cls/ematch/text.c \ + lib/route/cls/flower.c \ + lib/route/cls/fw.c \ + lib/route/cls/mall.c \ + lib/route/cls/police.c \ + lib/route/cls/u32.c \ + lib/route/link-sriov.h \ + lib/route/link.c \ + lib/route/link/api.c \ + lib/route/link/bonding.c \ + lib/route/link/bridge.c \ + lib/route/link/bridge_info.c \ + lib/route/link/can.c \ + lib/route/link/dummy.c \ + lib/route/link/geneve.c \ + lib/route/link/ifb.c \ + lib/route/link/inet.c \ + lib/route/link/inet6.c \ + lib/route/link/ip6gre.c \ + lib/route/link/ip6tnl.c \ + lib/route/link/ip6vti.c \ + lib/route/link/ipgre.c \ + lib/route/link/ipip.c \ + lib/route/link/ipvlan.c \ + lib/route/link/ipvti.c \ + lib/route/link/link-api.h \ + lib/route/link/macsec.c \ + lib/route/link/macvlan.c \ + lib/route/link/ppp.c \ + lib/route/link/sit.c \ + lib/route/link/sriov.c \ + lib/route/link/team.c \ + lib/route/link/veth.c \ + lib/route/link/vlan.c \ + lib/route/link/vrf.c \ + lib/route/link/vxlan.c \ + lib/route/link/xfrmi.c \ + lib/route/mdb.c \ + lib/route/neigh.c \ + lib/route/neightbl.c \ + lib/route/netconf.c \ + lib/route/nexthop-encap.h \ + lib/route/nexthop.c \ + lib/route/nexthop_encap.c \ + lib/route/nh.c \ + lib/route/nh_encap_mpls.c \ + lib/route/nl-route.h \ + lib/route/pktloc.c \ + lib/route/qdisc.c \ + lib/route/qdisc/blackhole.c \ + lib/route/qdisc/cbq.c \ + lib/route/qdisc/dsmark.c \ + lib/route/qdisc/fifo.c \ + lib/route/qdisc/fq_codel.c \ + lib/route/qdisc/hfsc.c \ + lib/route/qdisc/htb.c \ + lib/route/qdisc/ingress.c \ + lib/route/qdisc/mqprio.c \ + lib/route/qdisc/netem.c \ + lib/route/qdisc/plug.c \ + lib/route/qdisc/prio.c \ + lib/route/qdisc/red.c \ + lib/route/qdisc/sfq.c \ + lib/route/qdisc/tbf.c \ + lib/route/route.c \ + lib/route/route_obj.c \ + lib/route/route_utils.c \ + lib/route/rtnl.c \ + lib/route/rule.c \ + lib/route/tc-api.h \ + lib/route/tc.c \ + $(NULL) +nodist_lib_libnl_route_3_la_SOURCES = \ + $(grammar_files_sources) +EXTRA_lib_libnl_route_3_la_DEPENDENCIES = \ + libnl-route-3.sym +lib_libnl_route_3_la_CPPFLAGS = \ + $(lib_cppflags) \ + -I$(srcdir)/lib/route \ + -I$(builddir)/lib/route \ + -I$(builddir)/lib/route/cls \ + $(NULL) +lib_libnl_route_3_la_LDFLAGS = \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ + -Wl,--version-script=$(srcdir)/libnl-route-3.sym +lib_libnl_route_3_la_LIBADD = \ + lib/libnl-3.la + +$(lib_libnl_route_3_la_OBJECTS): $(grammar_files_headers) + +lib_LTLIBRARIES += lib/libnl-idiag-3.la + +lib_libnl_idiag_3_la_SOURCES = \ + lib/idiag/idiag.c \ + lib/idiag/idiag_meminfo_obj.c \ + lib/idiag/idiag_msg_obj.c \ + lib/idiag/idiag_req_obj.c \ + lib/idiag/idiag_vegasinfo_obj.c \ + lib/idiag/nl-idiag.h \ + $(NULL) +EXTRA_lib_libnl_idiag_3_la_DEPENDENCIES = \ + libnl-idiag-3.sym +lib_libnl_idiag_3_la_CPPFLAGS = \ + $(lib_cppflags) +lib_libnl_idiag_3_la_LDFLAGS = \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ + -Wl,--version-script=$(srcdir)/libnl-idiag-3.sym +lib_libnl_idiag_3_la_LIBADD = \ + lib/libnl-3.la + +lib_LTLIBRARIES += lib/libnl-genl-3.la + +lib_libnl_genl_3_la_SOURCES = \ + lib/genl/ctrl.c \ + lib/genl/family.c \ + lib/genl/genl.c \ + lib/genl/mngt.c \ + lib/genl/nl-genl.h \ + $(NULL) +EXTRA_lib_libnl_genl_3_la_DEPENDENCIES = \ + libnl-genl-3.sym +lib_libnl_genl_3_la_CPPFLAGS = \ + $(lib_cppflags) +lib_libnl_genl_3_la_LDFLAGS = \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ + -Wl,--version-script=$(srcdir)/libnl-genl-3.sym +lib_libnl_genl_3_la_LIBADD = \ + lib/libnl-3.la + +lib_LTLIBRARIES += lib/libnl-nf-3.la + +lib_libnl_nf_3_la_SOURCES = \ + lib/netfilter/ct.c \ + lib/netfilter/ct_obj.c \ + lib/netfilter/exp.c \ + lib/netfilter/exp_obj.c \ + lib/netfilter/log.c \ + lib/netfilter/log_msg.c \ + lib/netfilter/log_msg_obj.c \ + lib/netfilter/log_obj.c \ + lib/netfilter/netfilter.c \ + lib/netfilter/nfnl.c \ + lib/netfilter/nl-netfilter.h \ + lib/netfilter/queue.c \ + lib/netfilter/queue_msg.c \ + lib/netfilter/queue_msg_obj.c \ + lib/netfilter/queue_obj.c \ + $(NULL) +lib_libnl_nf_3_la_CPPFLAGS = \ + $(lib_cppflags) +EXTRA_lib_libnl_nf_3_la_DEPENDENCIES = \ + libnl-nf-3.sym +lib_libnl_nf_3_la_LDFLAGS = \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ + -Wl,--version-script=$(srcdir)/libnl-nf-3.sym +lib_libnl_nf_3_la_LIBADD = \ + lib/libnl-3.la \ + lib/libnl-route-3.la + +lib_LTLIBRARIES += lib/libnl-xfrm-3.la + +lib_libnl_xfrm_3_la_SOURCES = \ + lib/xfrm/ae.c \ + lib/xfrm/lifetime.c \ + lib/xfrm/nl-xfrm.h \ + lib/xfrm/sa.c \ + lib/xfrm/selector.c \ + lib/xfrm/sp.c \ + lib/xfrm/template.c \ + $(NULL) +lib_libnl_xfrm_3_la_CPPFLAGS = \ + $(lib_cppflags) +lib_libnl_xfrm_3_la_LDFLAGS = \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ + -Wl,--version-script=$(srcdir)/libnl-xfrm-3.sym +EXTRA_lib_libnl_xfrm_3_la_DEPENDENCIES = \ + libnl-xfrm-3.sym +lib_libnl_xfrm_3_la_LIBADD = \ + lib/libnl-3.la + +lib_cli_ltlibraries_cls = \ + lib/cli/cls/basic.la \ + lib/cli/cls/cgroup.la \ + $(NULL) +lib_cli_ltlibraries_qdisc = \ + lib/cli/qdisc/bfifo.la \ + lib/cli/qdisc/blackhole.la \ + lib/cli/qdisc/fq_codel.la \ + lib/cli/qdisc/hfsc.la \ + lib/cli/qdisc/htb.la \ + lib/cli/qdisc/ingress.la \ + lib/cli/qdisc/pfifo.la \ + lib/cli/qdisc/plug.la \ + $(NULL) + +if ENABLE_CLI +pkglib_clsdir = $(pkglibdir)/cli/cls +pkglib_qdiscdir = $(pkglibdir)/cli/qdisc +pkglib_cls_LTLIBRARIES = $(lib_cli_ltlibraries_cls) +pkglib_qdisc_LTLIBRARIES = $(lib_cli_ltlibraries_qdisc) +else +check_LTLIBRARIES += \ + $(lib_cli_ltlibraries_cls) \ + $(lib_cli_ltlibraries_qdisc) +endif + +lib_cli_ldflags = \ + -module -avoid-version + +lib_cli_cls_basic_la_CPPFLAGS = $(lib_cppflags) +lib_cli_cls_basic_la_LDFLAGS = $(lib_cli_ldflags) +lib_cli_cls_cgroup_la_CPPFLAGS = $(lib_cppflags) +lib_cli_cls_cgroup_la_LDFLAGS = $(lib_cli_ldflags) +lib_cli_qdisc_bfifo_la_CPPFLAGS = $(lib_cppflags) +lib_cli_qdisc_bfifo_la_LDFLAGS = $(lib_cli_ldflags) +lib_cli_qdisc_blackhole_la_CPPFLAGS = $(lib_cppflags) +lib_cli_qdisc_blackhole_la_LDFLAGS = $(lib_cli_ldflags) +lib_cli_qdisc_fq_codel_la_CPPFLAGS = $(lib_cppflags) +lib_cli_qdisc_fq_codel_la_LDFLAGS = $(lib_cli_ldflags) +lib_cli_qdisc_hfsc_la_CPPFLAGS = $(lib_cppflags) +lib_cli_qdisc_hfsc_la_LDFLAGS = $(lib_cli_ldflags) +lib_cli_qdisc_htb_la_CPPFLAGS = $(lib_cppflags) +lib_cli_qdisc_htb_la_LDFLAGS = $(lib_cli_ldflags) +lib_cli_qdisc_ingress_la_CPPFLAGS = $(lib_cppflags) +lib_cli_qdisc_ingress_la_LDFLAGS = $(lib_cli_ldflags) +lib_cli_qdisc_pfifo_la_CPPFLAGS = $(lib_cppflags) +lib_cli_qdisc_pfifo_la_LDFLAGS = $(lib_cli_ldflags) +lib_cli_qdisc_plug_la_CPPFLAGS = $(lib_cppflags) +lib_cli_qdisc_plug_la_LDFLAGS = $(lib_cli_ldflags) + +############################################################################### + +src_lib_ldflags = + +if ENABLE_CLI +lib_LTLIBRARIES += src/lib/libnl-cli-3.la +src_lib_ldflags += -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) +else +check_LTLIBRARIES += src/lib/libnl-cli-3.la +endif + +src_lib_libnl_cli_3_la_SOURCES = \ + src/lib/addr.c \ + src/lib/class.c \ + src/lib/cls.c \ + src/lib/ct.c \ + src/lib/exp.c \ + src/lib/link.c \ + src/lib/neigh.c \ + src/lib/nh.c \ + src/lib/qdisc.c \ + src/lib/route.c \ + src/lib/rule.c \ + src/lib/tc.c \ + src/lib/utils.c \ + $(NULL) +EXTRA_src_lib_libnl_cli_3_la_DEPENDENCIES = \ + libnl-cli-3.sym +src_lib_libnl_cli_3_la_CPPFLAGS = \ + $(warn_cppflags) \ + $(defines_cppflags) \ + $(default_includes) \ + $(NULL) +src_lib_libnl_cli_3_la_LDFLAGS = \ + $(src_lib_ldflags) \ + -Wl,--version-script=$(srcdir)/libnl-cli-3.sym +src_lib_libnl_cli_3_la_LIBADD = \ + lib/libnl-3.la \ + lib/libnl-route-3.la \ + lib/libnl-nf-3.la \ + lib/libnl-genl-3.la \ + -ldl \ + $(NULL) + +############################################################################### + +src_cppflags = \ + $(warn_cppflags) \ + $(defines_cppflags) \ + $(default_includes) \ + $(NULL) + +src_ldadd = \ + src/lib/libnl-cli-3.la \ + lib/libnl-3.la \ + lib/libnl-nf-3.la \ + lib/libnl-genl-3.la \ + lib/libnl-route-3.la \ + lib/libnl-idiag-3.la \ + $(NULL) + +cli_programs = \ + src/genl-ctrl-list \ + src/idiag-socket-details \ + src/nf-ct-add \ + src/nf-ct-events \ + src/nf-ct-list \ + src/nf-exp-add \ + src/nf-exp-delete \ + src/nf-exp-list \ + src/nf-log \ + src/nf-monitor \ + src/nf-queue \ + src/nl-addr-add \ + src/nl-addr-delete \ + src/nl-addr-list \ + src/nl-class-add \ + src/nl-class-delete \ + src/nl-class-list \ + src/nl-classid-lookup \ + src/nl-cls-add \ + src/nl-cls-delete \ + src/nl-cls-list \ + src/nl-fib-lookup \ + src/nl-link-enslave \ + src/nl-link-ifindex2name \ + src/nl-link-list \ + src/nl-link-name2ifindex \ + src/nl-link-release \ + src/nl-link-set \ + src/nl-link-stats \ + src/nl-list-caches \ + src/nl-list-sockets \ + src/nl-monitor \ + src/nl-neigh-add \ + src/nl-neigh-delete \ + src/nl-neigh-list \ + src/nl-neightbl-list \ + src/nl-nh-list \ + src/nl-pktloc-lookup \ + src/nl-qdisc-add \ + src/nl-qdisc-delete \ + src/nl-qdisc-list \ + src/nl-route-add \ + src/nl-route-delete \ + src/nl-route-get \ + src/nl-route-list \ + src/nl-rule-list \ + src/nl-tctree-list \ + src/nl-util-addr \ + $(NULL) + +if ENABLE_CLI +if ENABLE_CLI_INSTALL_BIN +bin_PROGRAMS += $(cli_programs) +else +if ENABLE_CLI_INSTALL_SBIN +sbin_PROGRAMS += $(cli_programs) +else +noinst_PROGRAMS += $(cli_programs) +endif +endif +else +check_PROGRAMS += $(cli_programs) +endif + +src_genl_ctrl_list_CPPFLAGS = $(src_cppflags) +src_genl_ctrl_list_LDADD = $(src_ldadd) +src_idiag_socket_details_CPPFLAGS = $(src_cppflags) +src_idiag_socket_details_LDADD = $(src_ldadd) +src_nf_ct_add_CPPFLAGS = $(src_cppflags) +src_nf_ct_add_LDADD = $(src_ldadd) +src_nf_ct_events_CPPFLAGS = $(src_cppflags) +src_nf_ct_events_LDADD = $(src_ldadd) +src_nf_ct_list_CPPFLAGS = $(src_cppflags) +src_nf_ct_list_LDADD = $(src_ldadd) +src_nf_exp_add_CPPFLAGS = $(src_cppflags) +src_nf_exp_add_LDADD = $(src_ldadd) +src_nf_exp_delete_CPPFLAGS = $(src_cppflags) +src_nf_exp_delete_LDADD = $(src_ldadd) +src_nf_exp_list_CPPFLAGS = $(src_cppflags) +src_nf_exp_list_LDADD = $(src_ldadd) +src_nf_log_CPPFLAGS = $(src_cppflags) +src_nf_log_LDADD = $(src_ldadd) +src_nf_monitor_CPPFLAGS = $(src_cppflags) +src_nf_monitor_LDADD = $(src_ldadd) +src_nf_queue_CPPFLAGS = $(src_cppflags) +src_nf_queue_LDADD = $(src_ldadd) +src_nl_addr_add_CPPFLAGS = $(src_cppflags) +src_nl_addr_add_LDADD = $(src_ldadd) +src_nl_addr_delete_CPPFLAGS = $(src_cppflags) +src_nl_addr_delete_LDADD = $(src_ldadd) +src_nl_addr_list_CPPFLAGS = $(src_cppflags) +src_nl_addr_list_LDADD = $(src_ldadd) +src_nl_class_add_CPPFLAGS = $(src_cppflags) +src_nl_class_add_LDADD = $(src_ldadd) +src_nl_class_delete_CPPFLAGS = $(src_cppflags) +src_nl_class_delete_LDADD = $(src_ldadd) +src_nl_class_list_CPPFLAGS = $(src_cppflags) +src_nl_class_list_LDADD = $(src_ldadd) +src_nl_classid_lookup_CPPFLAGS = $(src_cppflags) +src_nl_classid_lookup_LDADD = $(src_ldadd) +src_nl_cls_add_CPPFLAGS = $(src_cppflags) +src_nl_cls_add_LDADD = $(src_ldadd) +src_nl_cls_delete_CPPFLAGS = $(src_cppflags) +src_nl_cls_delete_LDADD = $(src_ldadd) +src_nl_cls_list_CPPFLAGS = $(src_cppflags) +src_nl_cls_list_LDADD = $(src_ldadd) +src_nl_fib_lookup_CPPFLAGS = $(src_cppflags) +src_nl_fib_lookup_LDADD = $(src_ldadd) +src_nl_link_enslave_CPPFLAGS = $(src_cppflags) +src_nl_link_enslave_LDADD = $(src_ldadd) +src_nl_link_ifindex2name_CPPFLAGS = $(src_cppflags) +src_nl_link_ifindex2name_LDADD = $(src_ldadd) +src_nl_link_list_CPPFLAGS = $(src_cppflags) +src_nl_link_list_LDADD = $(src_ldadd) +src_nl_link_name2ifindex_CPPFLAGS = $(src_cppflags) +src_nl_link_name2ifindex_LDADD = $(src_ldadd) +src_nl_link_release_CPPFLAGS = $(src_cppflags) +src_nl_link_release_LDADD = $(src_ldadd) +src_nl_link_set_CPPFLAGS = $(src_cppflags) +src_nl_link_set_LDADD = $(src_ldadd) +src_nl_link_stats_CPPFLAGS = $(src_cppflags) +src_nl_link_stats_LDADD = $(src_ldadd) +src_nl_list_caches_CPPFLAGS = $(src_cppflags) +src_nl_list_caches_LDADD = $(src_ldadd) +src_nl_list_sockets_CPPFLAGS = $(src_cppflags) +src_nl_list_sockets_LDADD = $(src_ldadd) +src_nl_monitor_CPPFLAGS = $(src_cppflags) +src_nl_monitor_LDADD = $(src_ldadd) +src_nl_neigh_add_CPPFLAGS = $(src_cppflags) +src_nl_neigh_add_LDADD = $(src_ldadd) +src_nl_neigh_delete_CPPFLAGS = $(src_cppflags) +src_nl_neigh_delete_LDADD = $(src_ldadd) +src_nl_neigh_list_CPPFLAGS = $(src_cppflags) +src_nl_neigh_list_LDADD = $(src_ldadd) +src_nl_neightbl_list_CPPFLAGS = $(src_cppflags) +src_nl_neightbl_list_LDADD = $(src_ldadd) +src_nl_nh_list_CPPFLAGS = $(src_cppflags) +src_nl_nh_list_LDADD = $(src_ldadd) +src_nl_pktloc_lookup_CPPFLAGS = $(src_cppflags) +src_nl_pktloc_lookup_LDADD = $(src_ldadd) +src_nl_qdisc_add_CPPFLAGS = $(src_cppflags) +src_nl_qdisc_add_LDADD = $(src_ldadd) +src_nl_qdisc_delete_CPPFLAGS = $(src_cppflags) +src_nl_qdisc_delete_LDADD = $(src_ldadd) +src_nl_qdisc_list_CPPFLAGS = $(src_cppflags) +src_nl_qdisc_list_LDADD = $(src_ldadd) +src_nl_route_add_CPPFLAGS = $(src_cppflags) +src_nl_route_add_LDADD = $(src_ldadd) +src_nl_route_delete_CPPFLAGS = $(src_cppflags) +src_nl_route_delete_LDADD = $(src_ldadd) +src_nl_route_get_CPPFLAGS = $(src_cppflags) +src_nl_route_get_LDADD = $(src_ldadd) +src_nl_route_list_CPPFLAGS = $(src_cppflags) +src_nl_route_list_LDADD = $(src_ldadd) +src_nl_rule_list_CPPFLAGS = $(src_cppflags) +src_nl_rule_list_LDADD = $(src_ldadd) +src_nl_tctree_list_CPPFLAGS = $(src_cppflags) +src_nl_tctree_list_LDADD = $(src_ldadd) +src_nl_util_addr_CPPFLAGS = $(src_cppflags) +src_nl_util_addr_LDADD = $(src_ldadd) + +############################################################################### + +tests_cppflags = \ + $(warn_cppflags) \ + $(defines_cppflags) \ + $(default_includes) \ + $(NULL) + +tests_ldadd = \ + lib/libnl-3.la \ + lib/libnl-nf-3.la \ + lib/libnl-genl-3.la \ + lib/libnl-route-3.la \ + $(NULL) + +if WITH_CHECK +check_LTLIBRARIES += tests/libnl-test-util.la +endif + +tests_libnl_test_util_la_SOURCES = \ + tests/nl-test-util.h \ + tests/nl-test-util.c \ + $(NULL) +tests_libnl_test_util_la_CPPFLAGS = \ + $(tests_cppflags) \ + $(CHECK_CFLAGS) \ + $(NULL) +tests_libnl_test_util_la_LIBADD = \ + $(tests_ldadd) \ + $(CHECK_LIBS) \ + $(NULL) + +check_PROGRAMS += \ + tests/test-complex-HTB-with-hash-filters \ + tests/test-create-bond \ + tests/test-create-bridge \ + tests/test-create-geneve \ + tests/test-create-ifb \ + tests/test-create-ip6tnl \ + tests/test-create-ipgre \ + tests/test-create-ipgretap \ + tests/test-create-ipip \ + tests/test-create-ipvlan \ + tests/test-create-ipvti \ + tests/test-create-macsec \ + tests/test-create-macvlan \ + tests/test-create-macvtap \ + tests/test-create-sit \ + tests/test-create-veth \ + tests/test-create-vlan \ + tests/test-create-vrf \ + tests/test-create-vxlan \ + tests/test-create-xfrmi \ + tests/test-delete-link \ + tests/test-loopback-up-down \ + tests/test-socket-creation \ + tests/test-u32-filter-with-actions \ + $(NULL) + +tests_test_complex_HTB_with_hash_filters_CPPFLAGS = $(tests_cppflags) +tests_test_complex_HTB_with_hash_filters_LDADD = $(tests_ldadd) +tests_test_create_bond_CPPFLAGS = $(tests_cppflags) +tests_test_create_bond_LDADD = $(tests_ldadd) +tests_test_create_bridge_CPPFLAGS = $(tests_cppflags) +tests_test_create_bridge_LDADD = $(tests_ldadd) +tests_test_create_geneve_CPPFLAGS = $(tests_cppflags) +tests_test_create_geneve_LDADD = $(tests_ldadd) +tests_test_create_ifb_CPPFLAGS = $(tests_cppflags) +tests_test_create_ifb_LDADD = $(tests_ldadd) +tests_test_create_ip6tnl_CPPFLAGS = $(tests_cppflags) +tests_test_create_ip6tnl_LDADD = $(tests_ldadd) +tests_test_create_ipgre_CPPFLAGS = $(tests_cppflags) +tests_test_create_ipgre_LDADD = $(tests_ldadd) +tests_test_create_ipgretap_CPPFLAGS = $(tests_cppflags) +tests_test_create_ipgretap_LDADD = $(tests_ldadd) +tests_test_create_ipip_CPPFLAGS = $(tests_cppflags) +tests_test_create_ipip_LDADD = $(tests_ldadd) +tests_test_create_ipvlan_CPPFLAGS = $(tests_cppflags) +tests_test_create_ipvlan_LDADD = $(tests_ldadd) +tests_test_create_ipvti_CPPFLAGS = $(tests_cppflags) +tests_test_create_ipvti_LDADD = $(tests_ldadd) +tests_test_create_macsec_CPPFLAGS = $(tests_cppflags) +tests_test_create_macsec_LDADD = $(tests_ldadd) +tests_test_create_macvlan_CPPFLAGS = $(tests_cppflags) +tests_test_create_macvlan_LDADD = $(tests_ldadd) +tests_test_create_macvtap_CPPFLAGS = $(tests_cppflags) +tests_test_create_macvtap_LDADD = $(tests_ldadd) +tests_test_create_sit_CPPFLAGS = $(tests_cppflags) +tests_test_create_sit_LDADD = $(tests_ldadd) +tests_test_create_veth_CPPFLAGS = $(tests_cppflags) +tests_test_create_veth_LDADD = $(tests_ldadd) +tests_test_create_vlan_CPPFLAGS = $(tests_cppflags) +tests_test_create_vlan_LDADD = $(tests_ldadd) +tests_test_create_vrf_CPPFLAGS = $(tests_cppflags) +tests_test_create_vrf_LDADD = $(tests_ldadd) +tests_test_create_vxlan_CPPFLAGS = $(tests_cppflags) +tests_test_create_vxlan_LDADD = $(tests_ldadd) +tests_test_create_xfrmi_CPPFLAGS = $(tests_cppflags) +tests_test_create_xfrmi_LDADD = $(tests_ldadd) +tests_test_delete_link_CPPFLAGS = $(tests_cppflags) +tests_test_delete_link_LDADD = $(tests_ldadd) +tests_test_loopback_up_down_CPPFLAGS = $(tests_cppflags) +tests_test_loopback_up_down_LDADD = $(tests_ldadd) +tests_test_socket_creation_CPPFLAGS = $(tests_cppflags) +tests_test_socket_creation_LDADD = $(tests_ldadd) +tests_test_u32_filter_with_actions_CPPFLAGS = $(tests_cppflags) +tests_test_u32_filter_with_actions_LDADD = $(tests_ldadd) + +check_PROGRAMS += \ + tests/test-cache-mngr \ + tests/test-genl \ + tests/test-nf-cache-mngr \ + $(NULL) + +tests_cli_ldadd = \ + $(tests_ldadd) \ + src/lib/libnl-cli-3.la \ + $(NULL) + +tests_test_cache_mngr_CPPFLAGS = $(tests_cppflags) +tests_test_cache_mngr_LDADD = $(tests_cli_ldadd) +tests_test_genl_CPPFLAGS = $(tests_cppflags) +tests_test_genl_LDADD = $(tests_cli_ldadd) +tests_test_nf_cache_mngr_CPPFLAGS = $(tests_cppflags) +tests_test_nf_cache_mngr_LDADD = $(tests_cli_ldadd) + + +if WITH_CHECK +check_programs += tests/check-all +endif + +tests_check_all_SOURCES = \ + tests/check-all.c \ + tests/cksuite-all-addr.c \ + tests/cksuite-all-attr.c \ + tests/cksuite-all-ematch-tree-clone.c \ + tests/cksuite-all-netns.c \ + tests/cksuite-all.h \ + $(NULL) + +tests_check_all_CPPFLAGS = \ + $(tests_cppflags) \ + $(CHECK_CFLAGS) + +tests_check_all_LDADD = \ + $(tests_ldadd) \ + tests/libnl-test-util.la \ + $(CHECK_LIBS) \ + $(NULL) + +if WITH_CHECK +if ENABLE_STATIC +check_programs += tests/check-direct +endif +endif + +tests_check_direct_SOURCES = \ + tests/check-direct.c \ + $(NULL) + +tests_check_direct_CPPFLAGS = \ + $(tests_cppflags) \ + $(CHECK_CFLAGS) \ + $(NULL) + +tests_check_direct_LDFLAGS = \ + -static \ + $(NULL) + +tests_check_direct_LDADD = \ + $(tests_ldadd) \ + tests/libnl-test-util.la \ + $(CHECK_LIBS) \ + $(NULL) + +############################################################################### + +dist_man8_MANS = \ + man/genl-ctrl-list.8 \ + man/nl-classid-lookup.8 \ + man/nl-pktloc-lookup.8 \ + man/nl-qdisc-add.8 \ + man/nl-qdisc-delete.8 \ + man/nl-qdisc-list.8 \ + $(NULL) + +############################################################################### + +python-build: + cd "$(srcdir)/python"; \ + python setup.py build + +.PHONY: python-build + +EXTRA_DIST += \ + python/README \ + \ + python/doc/conf.py \ + python/doc/core.rst \ + python/doc/index.rst \ + python/doc/route_addr.rst \ + python/doc/route.rst \ + \ + python/examples/iface.py \ + python/examples/nl80211.py \ + python/examples/wiphy.py \ + \ + python/netlink/capi.i \ + python/netlink/__init__.py \ + python/netlink/core.py \ + python/netlink/util.py \ + python/netlink/utils.h \ + \ + python/netlink/genl/capi.i \ + python/netlink/genl/__init__.py \ + \ + python/netlink/route/capi.i \ + python/netlink/route/__init__.py \ + python/netlink/route/address.py \ + python/netlink/route/link.py \ + python/netlink/route/tc.py \ + python/netlink/route/links/__init__.py \ + python/netlink/route/links/dummy.py \ + python/netlink/route/links/inet.py \ + python/netlink/route/links/vlan.py \ + python/netlink/route/qdisc/__init__.py \ + python/netlink/route/qdisc/htb.py \ + \ + python/tests/test-create-bridge.py + +############################################################################### + +%.build-headers-test.c: % + mkdir -p "$(dir $@)" + printf "#include <$$(echo "$<" | sed 's|.*\\nint main(void) { return 0; }" > $@ + +%.build-headers-test.o: %.build-headers-test.c + $(CC) -Wall -Werror -D_NL_NO_WARN_DEPRECATED_HEADER -I$(srcdir)/include -I$(builddir)/include -c -o $@ $< + +BUILD_HEADERS_OBJS = $(patsubst %,%.build-headers-test.o,$(public_headers)) + +# Test whether the public headers are all self-contained and can be build. +# This test is not hooked up as `make check`. +check-build-headers: $(BUILD_HEADERS_OBJS) + +CLEANFILES += $(BUILD_HEADERS_OBJS) + +check_build += check-build-headers + +############################################################################### + +if HAS_CXX +%.build-headers-test-cxx.cpp: % + mkdir -p "$(dir $@)" + printf "#include \n#include <$$(echo "$<" | sed 's|.*\\nint main(void) { return 0; }" > $@ + +%.build-headers-test-cxx.o: %.build-headers-test-cxx.cpp %.build-headers-test.o + $(CXX) -Wall -Werror -D_NL_NO_WARN_DEPRECATED_HEADER -I$(srcdir)/include -I$(builddir)/include -c -o $@ $< + +BUILD_HEADERS_OBJS_CXX = $(patsubst %,%.build-headers-test-cxx.o,$(public_headers)) + +CLEANFILES += $(BUILD_HEADERS_OBJS_CXX) + +check-build-headers-cxx: $(BUILD_HEADERS_OBJS_CXX) + +check_build += check-build-headers-cxx +endif + +############################################################################### + +check-local: $(check_build) $(check_local) + +.PHONY: $(check_local) + +check_PROGRAMS += $(check_programs) +TESTS += $(check_programs) + +if ENABLE_CLI +pkgconfig_DATA += libnl-cli-3.0.pc +endif + +pkgsysconfdir = $(sysconfdir)/libnl +pkgsysconf_DATA = \ + etc/pktloc \ + etc/classid + +EXTRA_DIST += \ + $(pkgsysconf_DATA) \ + libnl-3.sym \ + libnl-cli-3.sym \ + libnl-genl-3.sym \ + libnl-idiag-3.sym \ + libnl-nf-3.sym \ + libnl-route-3.sym \ + libnl-xfrm-3.sym \ + $(NULL) + +############################################################################### + +check-build: all $(check_PROGRAMS) $(check_LTLIBRARIES) $(check_build) + +check-progs: check-build + +.PHONY: check-progs check-build + +############################################################################### diff --git a/libnl/autogen.sh b/libnl/autogen.sh new file mode 100755 index 0000000..b0e2fde --- /dev/null +++ b/libnl/autogen.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -e + +die() { + echo "$@" >&2 + exit 1 +} + +BASEDIR="$(dirname "$0")" + +cd "$BASEDIR" || die "Could not change into base directory $BASEDIR" + +autoreconf -fi || die "Error during autoreconf" +rm -rf autom4te.cache + +doc/autogen.sh || die "Error during doc/autogen.sh" diff --git a/libnl/configure.ac b/libnl/configure.ac new file mode 100644 index 0000000..1556ea4 --- /dev/null +++ b/libnl/configure.ac @@ -0,0 +1,185 @@ +# SPDX-License-Identifier: LGPL-2.1-only + +# +# Copyright (c) 2003-2013 Thomas Graf +# + +# copied from glib +m4_define([libnl_major_version], [3]) +m4_define([libnl_minor_version], [11]) +m4_define([libnl_micro_version], [0]) +m4_define([libnl_git_sha], [m4_esyscmd([ ( [ -d ./.git/ ] && [ "$(readlink -f ./.git/)" = "$(readlink -f "$(git rev-parse --git-dir 2>/dev/null)" 2>/dev/null)" ] && git rev-parse --verify -q HEAD 2>/dev/null ) || true ])]) + + +# The following explanation may help to understand the above rules a bit +# better: consider that there are three possible kinds of reactions from +# users of your library to changes in a shared library: +# +# 1. Programs using the previous version may use the new version as drop-in +# replacement, and programs using the new version can also work with the +# previous one. In other words, no recompiling nor relinking is needed. +# In this case, bump revision only, don't touch current nor age. +# +# 2. Programs using the previous version may use the new version as drop-in +# replacement, but programs using the new version may use APIs not +# present in the previous one. In other words, a program linking against +# the new version may fail with “unresolved symbols” if linking against +# the old version at runtime: set revision to 0, bump current and age. +# +# 3. Programs may need to be changed, recompiled, relinked in order to use +# the new version. Bump current, set revision and age to 0. + +m4_define([libnl_lt_current], [226]) +m4_define([libnl_lt_revision], [0]) +m4_define([libnl_lt_age], [26]) + +m4_define([libnl_version], + [libnl_major_version.libnl_minor_version.libnl_micro_version]) + +AC_INIT([libnl],[libnl_version],[],[],[http://www.infradead.org/~tgr/libnl/]) +AC_CONFIG_HEADERS([include/config.h]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_MACRO_DIR([m4]) +AM_INIT_AUTOMAKE([-Wall -Wno-portability foreign subdir-objects]) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES(yes)], []) +m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) + +MAJ_VERSION=libnl_major_version +AC_SUBST(MAJ_VERSION) +MIN_VERSION=libnl_minor_version +AC_SUBST(MIN_VERSION) +MIC_VERSION=libnl_micro_version +AC_SUBST(MIC_VERSION) +LIBNL_GIT_SHA=libnl_git_sha +LIBNL_VERSION=libnl_version +AC_SUBST(LIBNL_VERSION) + +LT_CURRENT=libnl_lt_current +AC_SUBST(LT_CURRENT) +LT_REVISION=libnl_lt_revision +AC_SUBST(LT_REVISION) +LT_AGE=libnl_lt_age +AC_SUBST(LT_AGE) + +AC_USE_SYSTEM_EXTENSIONS + +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_CXX +AC_PROG_INSTALL +LT_INIT +AC_PROG_MKDIR_P +AC_CHECK_PROGS(FLEX, 'flex') +AC_CHECK_PROGS(YACC, 'bison -y') + +AC_C_CONST +AC_C_INLINE + +AC_LANG_PUSH([C++]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM( + [[#ifndef __cplusplus + #error "broken C++" + #endif]])],, + [CXX=;]) +AC_LANG_POP([C++]) +AM_CONDITIONAL([HAS_CXX], [test "x$CXX" != x]) + +PKG_CHECK_MODULES([CHECK], [check >= 0.9.0], + [has_check="yes"], + [AC_MSG_WARN([*** Disabling building of unit tests]) + has_check="no"]) +AM_CONDITIONAL(WITH_CHECK, [test "$has_check" = 'yes']) + +AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH], + [Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]), + [pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig']) +AC_SUBST([pkgconfigdir]) + +AC_ARG_ENABLE([cli], + AS_HELP_STRING([--enable-cli=yes|no|no-inst|bin|sbin], [Whether to build command line interface utils. Defaults to 'yes' which is a synonym for 'bin'. 'no-inst' means only build, not installing. 'bin'/'sbin' means installing to bin/sbin directory]), + [enable_cli="$enableval"], [enable_cli="yes"]) +if test "$enable_cli" != "no" && + test "$enable_cli" != "no-inst" && + test "$enable_cli" != "sbin"; then + enable_cli="bin" +fi +AM_CONDITIONAL([ENABLE_CLI], [test "$enable_cli" != "no"]) +AM_CONDITIONAL([ENABLE_CLI_INSTALL_BIN], [test "$enable_cli" = "bin"]) +AM_CONDITIONAL([ENABLE_CLI_INSTALL_SBIN], [test "$enable_cli" = "sbin"]) + +AC_CHECK_HEADERS(dlfcn.h, [], []) + +AC_ARG_ENABLE([pthreads], + AS_HELP_STRING([--disable-pthreads], [Disable pthreads support]), + [enable_pthreads="$enableval"], [enable_pthreads="yes"]) +AM_CONDITIONAL([DISABLE_PTHREADS], [test "$enable_pthreads" = "no"]) +if test "x$enable_pthreads" = "xno"; then + AC_DEFINE([DISABLE_PTHREADS], [1], [Define to 1 to disable pthreads]) +else + AC_CHECK_LIB([pthread], [pthread_mutex_lock], [], AC_MSG_ERROR([libpthread is required])) +fi + +AM_CONDITIONAL([ENABLE_STATIC], [test "$enable_static" != "no"]) + +AC_ARG_ENABLE([debug], + AS_HELP_STRING([--disable-debug], [Do not include debugging statements]), + [enable_debug="$enableval"], [enable_debug="yes"]) +if test "$enable_debug" = "yes"; then + AC_DEFINE([NL_DEBUG], [1], [Define to 1 to enable debugging]) +else + enable_debug=no + AC_DEFINE([NL_DEBUG], [0], [Define to 1 to enable debugging]) +fi + +AC_CONFIG_SUBDIRS([doc]) + +AC_CHECK_FUNCS([strerror_l]) + +AC_CHECK_DECLS([getprotobyname_r, getprotobynumber_r], [], [], [[ + #include +]]) + +AC_CONFIG_FILES([ +Makefile +libnl-3.0.pc +libnl-route-3.0.pc +libnl-genl-3.0.pc +libnl-nf-3.0.pc +libnl-cli-3.0.pc +libnl-xfrm-3.0.pc +libnl-idiag-3.0.pc +python/setup.py +include/netlink/version.h +]) + +ac_errcount=0 +if test -z "$YACC"; then + AC_MSG_WARN(bison not found. Please install before continuing.) + ac_errcount=$((ac_errcount + 1)) +fi +if test -z "$FLEX"; then + AC_MSG_WARN(flex not found. Please install before continuing.) + ac_errcount=$((ac_errcount + 1)) +fi +if test $ac_errcount -gt 0; then + AC_MSG_ERROR(Required packages are missing. Please install them and rerun ./configure) +fi + +AC_OUTPUT + +CXX_MSG="none" +if test "x$CXX" != x ; then + CXX_MSG="[$CXX]" +fi + +echo +echo "libnl $LIBNL_VERSION configuration${LIBNL_GIT_SHA:+ (git:$LIBNL_GIT_SHA)}:" +echo " --enable-pthreads=$enable_pthreads" +echo " --enable-static=$enable_static" +echo +echo " --enable-debug=$enable_debug" +echo " --enable-cli=$enable_cli" +echo +echo " check: $has_check" +echo " CXX: $CXX_MSG (only used for tests)" +echo diff --git a/libnl/doc/AUTHORS b/libnl/doc/AUTHORS new file mode 100644 index 0000000..26e3cb4 --- /dev/null +++ b/libnl/doc/AUTHORS @@ -0,0 +1 @@ +Thomas Graf diff --git a/libnl/doc/COPYING b/libnl/doc/COPYING new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/libnl/doc/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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 +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/libnl/doc/Doxyfile.in b/libnl/doc/Doxyfile.in new file mode 100644 index 0000000..0c4fcc4 --- /dev/null +++ b/libnl/doc/Doxyfile.in @@ -0,0 +1,1806 @@ +# Doxyfile 1.8.1.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = libnl + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = @PACKAGE_VERSION@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = ./ + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = YES + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = NO + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = arg=\param \ + "ref_asciidoc{3}=\3" \ + "ref_core{2}=\ref_asciidoc{core,\1,\2 (Netlink Core Library Development Guide)}" \ + "ref_route{2}=\ref_asciidoc{route,\1,\2 (Netlink Routing Development Guide)}" \ + "ref_idiagnl{2}=\ref_asciidoc{idiag,\1,\2 (Netlink Inet Diag Development Guide)}" \ + "core_doc{2}=\ref_core{\1,\2}" \ + "route_doc{2}=\ref_route{\1,\2}" \ + "idiagnl_doc{2}=\ref_idiagnl{\1,\2}" \ + "callback=\par Callback Invocation:\n" \ + "lowlevel=\copydoc low_level_api" + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +SYMBOL_CACHE_SIZE = 0 + +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = YES + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = YES + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = NO + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = NO + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = NO + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = DoxygenLayout.xml + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = NO + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = @top_srcdir@/../lib \ + @top_srcdir@/../src/lib \ + @top_srcdir@/../include/netlink \ + @top_srcdir@/../src \ + @top_srcdir@/../doc/src + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.c \ + *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = SCCS + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = @top_srcdir@/src + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C, C++ and Fortran comments will always remain visible. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = nl_ + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = api + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# style sheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = @srcdir@/libnl.css + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. + +HTML_DYNAMIC_SECTIONS = YES + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.infradead.libnl + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 1 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 205 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. +# However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = NO + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = NO + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = api/libnl.tag + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = YES + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = NO + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = FreeSans.ttf + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = NO + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = NO + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = NO + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = YES + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# managable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = NO + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = YES + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 100 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/libnl/doc/DoxygenLayout.xml b/libnl/doc/DoxygenLayout.xml new file mode 100644 index 0000000..589d0f1 --- /dev/null +++ b/libnl/doc/DoxygenLayout.xml @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libnl/doc/Makefile.am b/libnl/doc/Makefile.am new file mode 100644 index 0000000..67ae1fc --- /dev/null +++ b/libnl/doc/Makefile.am @@ -0,0 +1,80 @@ +# -*- Makefile -*- + +.PHONY: gendoc api_ref asciidoc + +ASCIIDOCOPTS = \ + -a pygments \ + -a language=c \ + -a icons \ + -a toc2 \ + -a numbered \ + -a imagesdir="./images/" \ + -a iconsdir="./images/icons" \ + -a stylesdir="$(abs_srcdir)/stylesheets/" + +EXTRA_DIST = \ + core.txt \ + core.html \ + route.txt \ + route.html \ + index.txt \ + index.html \ + libnl.css \ + stylesheets \ + images \ + api + +dist-hook: + rm -f $(distdir)/aclocal.m4 + rm -f $(distdir)/configure + rm -f $(distdir)/configure.in + rm -rf $(distdir)/m4 + rm -f $(distdir)/README + rm -f $(distdir)/missing + rm -f $(distdir)/Doxyfile.in + rm -f $(distdir)/Makefile.am + rm -f $(distdir)/Makefile.in + +link_doc: +if LINK_DOC + ./gen-tags.sh > libnl.dict +else + @echo "Warning: Linking to API reference is disabled, check configure output" +endif + + +%.html: %.txt link_doc +if HAVE_ASCIIDOC + ./resolve-asciidoc-refs.py $< > asciidoc.tmp + asciidoc $(ASCIIDOCOPTS) -o $@ asciidoc.tmp +if LINK_DOC + ./doxygen-link.py libnl.dict $@ > asciidoc.tmp + mv asciidoc.tmp $@ +endif +else + @echo "Warning: Building of asciidoc files is disabled, check autoconf logs" +endif + +asciidoc: core.html route.html index.html + +api_ref: + doxygen Doxyfile; + +gendoc: +if GENERATE_DOC +if HAVE_DOXYGEN + $(MAKE) api_ref +else + @echo "Warning: Building of API reference (doxygen) is disabled, check autoconf logs" +endif +if HAVE_ASCIIDOC + $(MAKE) asciidoc +else + @echo "Warning: Building of asciidoc files is disabled, check autoconf logs" +endif +else + @echo "Warning: Building of documentation disabled by user or autoconf" +endif + +clean-local: + rm -f api/* libnl.dict *.html diff --git a/libnl/doc/README b/libnl/doc/README new file mode 100644 index 0000000..dbca8f5 --- /dev/null +++ b/libnl/doc/README @@ -0,0 +1,13 @@ +Requirements to build documentation + +mscgen + http://www.mcternan.me.uk/mscgen/ + +mscgen-filter-1.2 + https://github.com/hwmaier/asciidoc-mscgen-filter + +asciidoc > 8.6.x +doxygen > 1.8.0 + +Building the documentation: +make gendoc diff --git a/libnl/doc/api/.gitignore b/libnl/doc/api/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/libnl/doc/autogen.sh b/libnl/doc/autogen.sh new file mode 100755 index 0000000..a147e93 --- /dev/null +++ b/libnl/doc/autogen.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -e + +BASEDIR="$(dirname "$0")" + +cd "$BASEDIR" || die "Could not change into base directory $BASEDIR" + +autoreconf -fi +rm -rf autom4te.cache diff --git a/libnl/doc/configure.ac b/libnl/doc/configure.ac new file mode 100644 index 0000000..3317d62 --- /dev/null +++ b/libnl/doc/configure.ac @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: LGPL-2.1-only +# +# Copyright (c) 2003-2013 Thomas Graf +# + +AC_INIT(libnl-doc, [3.11.0], [http://www.infradead.org/~tgr/libnl/]) +AC_CONFIG_MACRO_DIR([../m4]) +AC_CONFIG_AUX_DIR([build-aux]) +AM_INIT_AUTOMAKE([foreign]) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES(yes)], []) + +# +# Generating the documentation +# +AC_ARG_ENABLE([doc], + AS_HELP_STRING([--disable-doc], [Do not generate documentation]), + [generate_doc="$enableval"], [generate_doc=auto]) + +if test "x$generate_doc" != "xno"; then + AC_PROG_SED + AC_PROG_EGREP + + AC_CHECK_PROG(HAVE_PYTHON, [python], yes, no) + if test "x$HAVE_PYTHON" = "xno" -a "x$generate_doc" = "xyes"; then + AC_MSG_ERROR([*** python binary required to generate documentation]) + fi + + AC_CHECK_PROG(HAVE_DOXYGEN, [doxygen], yes, no) + if test "x$HAVE_DOXYGEN" = "xno" -a "x$generate_doc" = "xyes"; then + AC_MSG_ERROR([*** doxygen package required to generate documentation]) + fi + + AC_CHECK_PROG(HAVE_DOT, [dot], yes, no) + if test "x$HAVE_DOT" = "xno"; then + if test "x$generate_doc" = "xyes"; then + AC_MSG_ERROR([*** graphviz package required to generate documentation]) + else + AC_MSG_WARN([*** graphviz not found, disabling building of API reference]) + HAVE_DOXYGEN=no + fi + fi + + AC_CHECK_PROG(HAVE_ASCIIDOC, [asciidoc], yes, no) + if test "x$HAVE_ASCIIDOC" = "xno"; then + if test "x$generate_doc" = "xyes"; then + AC_MSG_ERROR([*** asciidoc package required to generate documentation]) + else + AC_MSG_WARN([*** asciidoc not found, disabling building of guides]) + fi + fi + + AC_CHECK_PROG(HAVE_SOURCE_HIGHLIGHT, [source-highlight], yes, no) + if test "x$HAVE_SOURCE_HIGHLIGHT" = "xno"; then + if test "x$generate_doc" = "xyes"; then + AC_MSG_ERROR([*** source-highlight required to generate documentation]) + else + AC_MSG_WARN([*** source-highlight not found, disabling building of guides]) + HAVE_ASCIIDOC=no + fi + fi + + AC_CHECK_PROG(HAVE_MSCGEN, [mscgen], yes, no) + if test "x$HAVE_MSCGEN" = "xno"; then + AC_MSG_WARN([*** mscgen not found, get it at http://www.mcternan.me.uk/mscgen/]) + if test "x$generate_doc" = "xyes"; then + AC_MSG_ERROR([*** mscgen package required to generate documentation]) + else + AC_MSG_WARN([*** Disabling building of guides]) + HAVE_ASCIIDOC=no + HAVE_DOXYGEN=no + fi + fi + + AC_CHECK_PROG(HAVE_PYGMENTIZE, [pygmentize], yes, no) + if test "x$HAVE_PYGMENTIZE" = "xno"; then + if test "x$generate_doc" = "xyes"; then + AC_MSG_ERROR([*** pygmentize package required to generate documentation]) + else + AC_MSG_WARN([*** Disabling building of guides]) + HAVE_ASCIIDOC=no + fi + fi + + link_doc=yes + if test "x$HAVE_DOXYGEN" = "xno"; then + AC_MSG_WARN([*** Disabling API linking due to missing doxygen package]) + link_doc=no + fi +fi + +AM_CONDITIONAL([LINK_DOC], [test "x$link_doc" = "xyes"]) +AM_CONDITIONAL([HAVE_DOXYGEN], [test "x$HAVE_DOXYGEN" = "xyes"]) +AM_CONDITIONAL([HAVE_ASCIIDOC], [test "x$HAVE_ASCIIDOC" = "xyes"]) + +AM_CONDITIONAL([GENERATE_DOC], [test "x$generate_doc" != "xno"]) + +AC_CONFIG_FILES([ +Doxyfile +Makefile +]) + +AC_OUTPUT diff --git a/libnl/doc/core.txt b/libnl/doc/core.txt new file mode 100644 index 0000000..56d8749 --- /dev/null +++ b/libnl/doc/core.txt @@ -0,0 +1,3017 @@ +//// + vim.syntax: asciidoc + + Copyright (c) 2011 Thomas Graf +//// + +Netlink Library (libnl) +======================= +Thomas Graf +3.2, May 9 2011: +:numbered: + +== Introduction + +The core library contains the fundamentals required to communicate +over netlink sockets. It deals with connecting and disconnecting of +sockets, sending and receiving of data, construction and parsing of +messages, provides a customizable receiving state machine, and +provides an abstract data type framework which eases the implementation +of object based netlink protocols where objects are added, removed, or +modified using a netlink based protocol. + +.Library Hierarchy + +The suite is split into multiple libraries: + +image:library_overview.png["Library Hierarchy"] + +link:core.html[Netlink Library] (libnl):: +Socket handling, sending and receiving, message construction and parsing, ... + +link:route.html[Routing Family Library] (libnl-route):: +Adresses, links, neighbours, routing, traffic control, neighbour tables, ... + +Netfilter Library (libnl-nf):: +Connection tracking, logging, queueing + +Generic Netlink Library (libnl-genl):: +Controller API, family and command registration + + +=== How To Read This Documentation + +The libraries provide a broad set of APIs of which most applications only +require a small subset of it. Depending on the type of application, some +users may only be interested in the low level netlink messaging API while +others wish to make heavy use of the high level API. + +In any case it is recommended to get familiar with the netlink protocol +first. + +- <> + +The low level APIs are described in: + +- <> +- <> + + +=== Linking to this Library + +.Checking the presence of the library using autoconf + +Projects using autoconf may use +PKG_CHECK_MODULES()+ to check if +a specific version of libnl is available on the system. The example +below also shows how to retrieve the +CFLAGS+ and linking dependencies +required to link against the library. + +The following example shows how to check for a specific version of libnl. If +found, it extends the `CFLAGS` and `LIBS` variable appropriately: + +[source] +---- +PKG_CHECK_MODULES(LIBNL3, libnl-3.0 >= 3.1, [have_libnl3=yes], [have_libnl3=no]) +if (test "${have_libnl3}" = "yes"); then + CFLAGS+="$LIBNL3_CFLAGS" + LIBS+="$LIBNL3_LIBS" +fi +---- + +NOTE: The pkgconfig file is named +libnl-3.0.pc+ for historic reasons, it also + covers library versions >= 3.1. + +.Header Files + +The main header file is ``. Additional headers may need to +be included in your sources depending on the subsystems and components your +program makes use of. + +[source,c] +----- +#include +#include +#include +----- + +.Version Dependent Code + +If your code wishes to be capable to link against multiple versions of libnl +you may have direct the compiler to only include portions on the code depending +on the version of libnl that it is compiled against. + +[source,c] +----- +#include + +#if LIBNL_VER_NUM >= LIBNL_VER(3,1) + /* include code if compiled with libnl version >= 3.1 */ +#endif +----- + +.Linking +----- +$ gcc myprogram.c -o myprogram $(pkgconfig --cflags --libs libnl-3.0) +----- + +=== Debugging + +The library has been compiled with debugging statements enabled it will +print debug information to +stderr+ if the environment variable +NLDBG+ +is set to > 0. + +----- +$ NLDBG=2 ./myprogram +----- + +.Debugging Levels +[options="header", width="80%", cols="1,5", align="center"] +|=============================================================== +| Level | Description +| 0 | Debugging disabled (default) +| 1 | Warnings, important events and notifications +| 2 | More or less important debugging messages +| 3 | Repetitive events causing a flood of debugging messages +| 4 | Even less important messages +|=============================================================== + +.Debugging the Netlink Protocol + +It is often useful to peek into the stream of netlink messages exchanged +with other sockets. Setting the environment variable +NLCB=debug+ will +cause the debugging message handlers to be used which in turn print the +netlink messages exchanged in a human readable format to to +stderr+: + +----- +$ NLCB=debug ./myprogram +-- Debug: Sent Message: +-------------------------- BEGIN NETLINK MESSAGE --------------------------- + [HEADER] 16 octets + .nlmsg_len = 20 + .nlmsg_type = 18 + .nlmsg_flags = 773 + .nlmsg_seq = 1301410712 + .nlmsg_pid = 20014 + [PAYLOAD] 16 octets + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +--------------------------- END NETLINK MESSAGE --------------------------- +-- Debug: Received Message: +-------------------------- BEGIN NETLINK MESSAGE --------------------------- + [HEADER] 16 octets + .nlmsg_len = 996 + .nlmsg_type = 16 + .nlmsg_flags = 2 + .nlmsg_seq = 1301410712 + .nlmsg_pid = 20014 + [PAYLOAD] 16 octets + 00 00 04 03 01 00 00 00 49 00 01 00 00 00 00 00 ........I....... + [ATTR 03] 3 octets + 6c 6f 00 lo. + [PADDING] 1 octets + 00 . + [ATTR 13] 4 octets + 00 00 00 00 .... + [ATTR 16] 1 octets + 00 . + [PADDING] 3 octets + 00 00 00 ... + [ATTR 17] 1 octets + 00 . + [...] +--------------------------- END NETLINK MESSAGE --------------------------- + +----- + +[[core_netlink_fundamentals]] +== Netlink Protocol Fundamentals + +The netlink protocol is a socket based IPC mechanism used for +communication between userspace processes and the kernel or between +userspace processes themselves. The netlink protocol is based on BSD +sockets and uses the +AF_NETLINK+ address family. Every netlink +protocol uses its own protocol number (e.g. +NETLINK_ROUTE+, ++NETLINK_NETFILTER+, etc). Its addressing schema is based on a 32 bit +port number, formerly referred to as PID, which uniquely identifies +each peer. + +[[core_addressing]] +=== Addressing + +The netlink address (port) consists of a 32bit integer. Port 0 (zero) +is reserved for the kernel and refers to the kernel side socket of each +netlink protocol family. Other port numbers usually refer to user space +owned sockets, although this is not enforced. + +NOTE: In the beginning, it was common practice to use the process + identifier (PID) as the local port number. This became unpractical + with the introduction of threaded netlink applications and + applications requiring multiple sockets. Therefore libnl generates + unique port numbers based on the process identifier and adds an + offset to it allowing for multiple sockets to be used. The initial + socket will still equal to the process identifier for backwards + compatibility reasons. + +image:addressing.png["Addressing Example"] + +The above figure illustrates three applications and the kernel side +exposing two kernel side sockets. It shows the common netlink use +cases: + + * User space to kernel + * User space to user space + * Listening to kernel multicast notifications + +.User Space to Kernel + +The most common form of netlink usage is for a user space application +to send requests to the kernel and process the reply which is either +an error message or a success notification. + +["mscgen"] +-------- +msc { + App1,App2,Kernel; + App1=>Kernel [label="request (src=11, dst=0)"]; + App1<=Kernel [label="reply (src=0, dst=11)"]; + ...; + App2=>Kernel [label="request (src=21, dst=0)"]; + App2<=Kernel [label="reply (src=0, dst=21)"]; +} +-------- + +.User Space to User Space + +Netlink may also be used as an IPC mechanism to communicate between user +space applications directly. Communication is not limited to two peers, +any number of peers may communicate with each other and multicasting +capabilities allow to reach multiple peers with a single message. + +In order for the sockets to be visible to each other, both sockets must +be created for the same netlink protocol family. + +["mscgen"] +-------- +msc { + App2,App3; + App2=>App3 [label="request (src=22, dst=31)"]; + App2<=App3 [label="reply (src=31, dst=22)"]; + ...; +} +-------- + +.User space listening to kernel notifications + +This form of netlink communication is typically found in user space +daemons that need to act on certain kernel events. Such daemons will +typically maintain a netlink socket subscribed to a multicast group that +is used by the kernel to notify interested user space parties about +specific events. + +["mscgen"] +-------- +msc { + Kernel,App3; + Kernel=>App3 [label="notification (src=0, group=foo)"]; + ...; +} +-------- + +Use of multicasting is preferred over direct addressing due to the +flexibility in exchanging the user space component at any time without +the kernel noticing. + +[[core_msg_format]] +=== Message Format + +A netlink protocol is typically based on messages and consists of the +netlink message header (+struct nlmsghdr+) plus the payload attached +to it. The payload can consist of arbitrary data but usually contains +a fixed size protocol specific header followed by a stream of +attributes. + +.Netlink message header (struct nlmsghdr) + +image:nlmsghdr.png[align="center", alt="Netlink Message Header"] + +Total Length (32bit):: +Total length of the message in bytes including the netlink message header. + +Message Type (16bit):: +The message type specifies the type of payload the message is carrying. +Several standard message types are defined by the netlink protocol. +Additional message types may be defined by each protocol family. See +<> for additional information. + +Message Flags (16bit):: +The message flags may be used to modify the behaviour of a message type. +See section <> for a list of standard message flags. + +Sequence Number (32bit):: +The sequence number is optional and may be used to allow referring to +a previous message, e.g. an error message can refer to the original +request causing the error. + +Port Number (32bit):: +The port number specifies the peer to which the message should be delivered +to. If not specified, the message will be delivered to the first matching +kernel side socket of the same protocol family. + +[[core_msg_types]] +=== Message Types + +Netlink differs between requests, notifications, and replies. Requests +are messages which have the +NLM_F_REQUEST+ flag set and are meant to +request an action from the receiver. A request is typically sent from +a userspace process to the kernel. While not strictly enforced, requests +should carry a sequence number incremented for each request sent. + +Depending on the nature of the request, the receiver may reply to the +request with another netlink message. The sequence number of a reply +must match the sequence number of the request it relates to. + +Notifications are of informal nature and no reply is expected, therefore +the sequence number is typically set to 0. + +["mscgen"] +-------- +msc { + A,B; + A=>B [label="GET (seq=1, NLM_F_REQUEST)"]; + A<=B [label="PUT (seq=1)"]; + ...; + A<=B [label="NOTIFY (seq=0)"]; +} +-------- + + +The type of message is primarly identified by its 16 bit message type set +in the message header. The following standard message types are defined: + +- +NLMSG_NOOP+ - No operation, message must be discarded +- +NLMSG_ERROR+ - Error message or ACK, see <> + respectively <> +- +NLMSG_DONE+ - End of multipart sequence, see <> +- +NLMSG_OVERRUN+ - Overrun notification (Error) + +Every netlink protocol is free to define own message types. Note that +message type values +< NLMSG_MIN_TYPE (0x10)+ are reserved and may +not be used. + +It is common practice to use own message types to implement RPC schemas. +Suppose the goal of the netlink protocol you are implementing is allow +configuration of a particular network device, therefore you want to +provide read/write access to various configuration options. The typical +"netlink way" of doing this would be to define two message types ++MSG_SETCFG+, +MSG_GETCFG+: + +[source,c] +-------- +#define MSG_SETCFG 0x11 +#define MSG_GETCFG 0x12 +-------- + +Sending a +MSG_GETCFG+ request message will typically trigger a reply +with the message type +MSG_SETCFG+ containing the current configuration. +In object oriented terms one would describe this as "the kernel sets +the local copy of the configuration in userspace". + +["mscgen"] +-------- +msc { + A,B; + A=>B [label="MSG_GETCFG (seq=1, NLM_F_REQUEST)"]; + A<=B [label="MSG_SETCFG (seq=1)"]; +} +-------- + +The configuration may be changed by sending a +MSG_SETCFG+ which will +be responded to with either an ACK (see <>) +or an error message (see <>). + +["mscgen"] +-------- +msc { + A,B; + A=>B [label="MSG_SETCFG (seq=1, NLM_F_REQUEST, NLM_F_ACK)"]; + A<=B [label="ACK (seq=1)"]; +} +-------- + +Optionally, the kernel may send out notifications for configuration +changes allowing userspace to listen for changes instead of polling +frequently. Notifications typically reuse an existing message type +and rely on the application using a separate socket to differ between +requests and notifications but you may also specify a separate message +type. + +["mscgen"] +-------- +msc { + A,B; + A<=B [label="MSG_SETCFG (seq=0)"]; +} +-------- + +[[core_multipart]] +==== Multipart Messages + +Although in theory a netlink message can be up to 4GiB in size. The socket +buffers are very likely not large enough to hold message of such sizes. +Therefore it is common to limit messages to one page size (PAGE_SIZE) and +use the multipart mechanism to split large pieces of data into several +messages. A multipart message has the flag +NLM_F_MULTI+ set and the +receiver is expected to continue receiving and parsing until the special +message type +NLMSG_DONE+ is received. + +Multipart messages unlike fragmented ip packets must not be reassembled +even though it is perfectly legal to do so if the protocols wishes to +work this way. Often multipart message are used to send lists or trees +of objects were each multipart message simply carries multiple objects +allow for each message to be parsed independently. + +["mscgen"] +-------- +msc { + A,B; + A=>B [label="GET (seq=1, NLM_F_REQUEST)"]; + A<=B [label="PUT (seq=1, NLM_F_MULTI)"]; + ...; + A<=B [label="PUT (seq=1, NLM_F_MULTI)"]; + A<=B [label="NLMSG_DONE (seq=1)"]; +} +-------- + +[[core_errmsg]] +==== Error Message + +Error messages can be sent in response to a request. Error messages must +use the standard message type +NLMSG_ERROR+. The payload consists of a +error code and the original netlink mesage header of the request. + +image:nlmsgerr.png["Netlink Errror Message header"] + +Error messages should set the sequence number to the sequence number +of the request which caused the error. + +["mscgen"] +-------- +msc { + A,B; + A=>B [label="GET (seq=1, NLM_F_REQUEST)"]; + A<=B [label="NLMSG_ERROR code=EINVAL (seq=1)"]; +} +-------- + +[[core_msg_ack]] +==== ACKs + +A sender can request an ACK message to be sent back for each request +processed by setting the +NLM_F_ACK+ flag in the request. This is typically +used to allow the sender to synchronize further processing until the +request has been processed by the receiver. + +["mscgen"] +-------- +msc { + A,B; + A=>B [label="GET (seq=1, NLM_F_REQUEST | NLM_F_ACK)"]; + A<=B [label="ACK (seq=1)"]; +} +-------- + +ACK messages also use the message type +NLMSG_ERROR+ and payload +format but the error code is set to 0. + +[[core_msg_flags]] +==== Message Flags + +The following standard flags are defined + +[source,c] +-------- +#define NLM_F_REQUEST 1 +#define NLM_F_MULTI 2 +#define NLM_F_ACK 4 +#define NLM_F_ECHO 8 +-------- + +- `NLM_F_REQUEST` - Message is a request, see <>. +- `NLM_F_MULTI` - Multipart message, see <> +- `NLM_F_ACK` - ACK message requested, see <>. +- `NLM_F_ECHO` - Request to echo the request. + +The flag +NLM_F_ECHO+ is similar to the `NLM_F_ACK` flag. It can be +used in combination with `NLM_F_REQUEST` and causes a notification +which is sent as a result of a request to also be sent to the sender +regardless of whether the sender has subscribed to the corresponding +multicast group or not. See <> + +Additional universal message flags are defined which only apply for ++GET+ requests: + +[source,c] +-------- +#define NLM_F_ROOT 0x100 +#define NLM_F_MATCH 0x200 +#define NLM_F_ATOMIC 0x400 +#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) +-------- + +- `NLM_F_ROOT` - Return based on root of tree. +- `NLM_F_MATCH` - Return all matching entries. +- `NLM_F_ATOMIC` - Obsoleted, once used to request an atomic operation. +- `NLM_F_DUMP` - Return a list of all objects + (`NLM_F_ROOT`|`NLM_F_MATCH`). + +Use of these flags is completely optional and many netlink protocols only +make use of the `NLM_F_DUMP` flag which typically requests the receiver +to send a list of all objects in the context of the message type as a +sequence of multipart messages (see <>). + +Another set of flags exist related to `NEW` or `SET` requests. These +flags are mutually exclusive to the `GET` flags: + +[source,c] +-------- +#define NLM_F_REPLACE 0x100 +#define NLM_F_EXCL 0x200 +#define NLM_F_CREATE 0x400 +#define NLM_F_APPEND 0x800 +-------- + +- `NLM_F_REPLACE` - Replace an existing object if it exists. +- `NLM_F_EXCL` - Do not update object if it exists already. +- `NLM_F_CREATE` - Create object if it does not exist yet. +- `NLM_F_APPEND` - Add object at end of list. + +Behaviour of these flags may differ slightly between different netlink +protocols. + +[[core_seq_num]] +=== Sequence Numbers + +Netlink allows the use of sequence numbers to help relate replies to +requests. It should be noted that unlike in protocols such as TCP +there is no strict enforcement of the sequence number. The sole purpose +of sequence numbers is to assist a sender in relating replies to the +corresponding requests. See <> for more information. + +Sequence numbers are managed on a per socket basis, see +<> for more information on how to use sequence numbers. + +[[core_multicast]] +=== Multicast Groups + +TODO + +See <> + +[[core_sockets]] +== Netlink Sockets + +In order to use the netlink protocol, a netlink socket is required. +Each socket defines an independent context for sending and receiving of +messages. An application may make use multiple sockets, e.g. a socket to +send requests and receive the replies and another socket subscribed to a +multicast group to receive notifications. + +=== Socket structure (struct nl_sock) + +The netlink socket and all related attributes including the actual file +descriptor are represented by +struct nl_sock+. + +[source,c] +-------- +#include + +struct nl_sock *nl_socket_alloc(void) +void nl_socket_free(struct nl_sock *sk) +-------- + +The application must allocate an instance of +struct nl_sock+ for each +netlink socket it wishes to use. + +[[core_sk_seq_num]] +=== Sequence Numbers + +The library will automatically take care of sequence number handling +for the application. A sequence number counter is stored in the +socket structure which is used and incremented automatically when a +message needs to be sent which is expected to generate a reply such as +an error or any other message type that needs to be related to the +original message. + +Alternatively, the counter can be used directly via the function +nl_socket_use_seq(). It will return the current value of the counter +and increment it by one afterwards. + +[source,c] +-------- +#include + +unsigned int nl_socket_use_seq(struct nl_sock *sk); +-------- + +Most applications will not want to deal with sequence number handling +themselves though. When using nl_send_auto() the sequence number is +filled in automatically and matched again when a reply is received. See +section <> for more information. + +This behaviour can and must be disabled if the netlink protocol +implemented does not use a request/reply model, e.g. when a socket is +used to receive notification messages. + +[source,c] +-------- +#include + +void nl_socket_disable_seq_check(struct nl_sock *sk); +-------- + +For more information on the theory behind netlink sequence numbers, +see section <>. + +[[core_sk_multicast]] +=== Multicast Group Subscriptions + +Each socket can subscribe to any number of multicast groups of the +netlink protocol it is connected to. The socket will then receive a +copy of each message sent to any of the groups. Multicast groups are +commonly used to implement event notifications. + +Prior to kernel 2.6.14 the group subscription was performed using a +bitmask which limited the number of groups per protocol family to 32. +This outdated interface can still be accessed via the function +nl_join_groups() even though it is not recommended for new code. + +[source,c] +-------- +#include + +void nl_join_groups(struct nl_sock *sk, int bitmask); +-------- + +Starting with 2.6.14 a new method was introduced which supports subscribing +to an almost infinite number of multicast groups. + +[source,c] +-------- +#include + +int nl_socket_add_memberships(struct nl_sock *sk, int group, ...); +int nl_socket_drop_memberships(struct nl_sock *sk, int group, ...); +-------- + +==== Multicast Example + +[source,c] +-------- +#include +#include +#include + +/* + * This function will be called for each valid netlink message received + * in nl_recvmsgs_default() + */ +static int my_func(struct nl_msg *msg, void *arg) +{ + return 0; +} + +struct nl_sock *sk; + +/* Allocate a new socket */ +sk = nl_socket_alloc(); + +/* + * Notifications do not use sequence numbers, disable sequence number + * checking. + */ +nl_socket_disable_seq_check(sk); + +/* + * Define a callback function, which will be called for each notification + * received + */ +nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, my_func, NULL); + +/* Connect to routing netlink protocol */ +nl_connect(sk, NETLINK_ROUTE); + +/* Subscribe to link notifications group */ +nl_socket_add_memberships(sk, RTNLGRP_LINK, 0); + +/* + * Start receiving messages. The function nl_recvmsgs_default() will block + * until one or more netlink messages (notification) are received which + * will be passed on to my_func(). + */ +while (1) + nl_recvmsgs_default(sock); +-------- + +[[core_sk_cb]] +=== Modifiying Socket Callback Configuration + +See <> for more information on +callback hooks and overwriting capabilities. + +Each socket is assigned a callback configuration which controls the +behaviour of the socket. This is f.e. required to have a separate +message receive function per socket. It is perfectly legal to share +callback configurations between sockets though. + +The following functions can be used to access and set the callback +configuration of a socket: + +[source,c] +-------- +#include + +struct nl_cb *nl_socket_get_cb(const struct nl_sock *sk); +void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb); +-------- + +Additionally a shortcut exists to modify the callback configuration +assigned to a socket directly: + +[source,c] +-------- +#include + +int nl_socket_modify_cb(struct nl_sock *sk, enum nl_cb_type type, enum nl_cb_kind kind, + nl_recvmsg_msg_cb_t func, void *arg); +-------- + +.Example: +[source,c] +-------- +#include + +// Call my_input() for all valid messages received in socket sk +nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, my_input, NULL); +-------- + +=== Socket Attributes + +.Local Port + +The local port number uniquely identifies the socket and is used to +address it. A unique local port is generated automatically when the +socket is allocated. It will consist of the Process ID (22 bits) and a +random number (10 bits) thus allowing up to 1024 sockets per process. + +[source,c] +-------- +#include + +uint32_t nl_socket_get_local_port(const struct nl_sock *sk); +void nl_socket_set_local_port(struct nl_sock *sk, uint32_t port); +-------- + +See section <> for more information on port numbers. + +CAUTION: Overwriting the local port is possible but you have to ensure +that the provided value is unique and no other socket in any other +application is using the same value. + +.Peer Port + +A peer port can be assigned to the socket which will result in all +unicast messages sent over the socket to be addresses to the peer. If +no peer is specified, the message is sent to the kernel which will try +to automatically bind the socket to a kernel side socket of the same +netlink protocol family. It is common practice not to bind the socket +to a peer port as typically only one kernel side socket exists per +netlink protocol family. + +[source,c] +-------- +#include + +uint32_t nl_socket_get_peer_port(const struct nl_sock *sk); +void nl_socket_set_peer_port(struct nl_sock *sk, uint32_t port); +-------- + +See section <> for more information on port numbers. + +.File Descriptor + +Netlink uses the BSD socket interface, therefore a file descriptor is +behind each socket and you may use it directly. + +[source,c] +-------- +#include + +int nl_socket_get_fd(const struct nl_sock *sk); +-------- + +If a socket is used to only receive notifications it usually is best +to put the socket in non-blocking mode and periodically poll for new +notifications. + +[source,c] +-------- +#include + +int nl_socket_set_nonblocking(const struct nl_sock *sk); +-------- + +.Send/Receive Buffer Size + +The socket buffer is used to queue netlink messages between sender and +receiver. The size of these buffers specifies the maximum size you +will be able to write() to a netlink socket, i.e. it will indirectly +define the maximum message size. The default is 32KiB. + +[source,c] +-------- +#include + +int nl_socket_set_buffer_size(struct nl_sock *sk, int rx, int tx); +-------- + +[[core_sk_cred]] +.Enable/Disable Credentials + +TODO + +[source,c] +-------- +#include + +int nl_socket_set_passcred(struct nl_sock *sk, int state); +-------- + +.Enable/Disable Auto-ACK Mode + +The following functions allow to enable/disable Auto-ACK mode on a socket. +See <> for more information on what implications that has. +Auto-ACK mode is enabled by default. + +[source,c] +-------- +#include + +void nl_socket_enable_auto_ack(struct nl_sock *sk); +void nl_socket_disable_auto_ack(struct nl_sock *sk); +-------- + +.Enable/Disable Message Peeking + +If enabled, message peeking causes nl_recv() to try and use MSG_PEEK +to retrieve the size of the next message received and allocate a +buffer of that size. Message peeking is enabled by default but can be +disabled using the following function: + +[source,c] +-------- +#include + +void nl_socket_enable_msg_peek(struct nl_sock *sk); +void nl_socket_disable_msg_peek(struct nl_sock *sk); +-------- + +.Enable/Disable Receival of Packet Information + +If enabled, each received netlink message from the kernel will include +an additional struct nl_pktinfo in the control message. The following +function can be used to enable/disable receival of packet information. + +[source,c] +-------- +#include + +int nl_socket_recv_pktinfo(struct nl_sock *sk, int state); +-------- + +CAUTION: Processing of NETLINK_PKTINFO has not been implemented yet. + +[[core_send_recv]] +== Sending and Receiving of Messages / Data + +[[core_send]] +=== Sending Messages + +The standard method of sending a netlink message over a netlink socket +is to use the function nl_send_auto(). It will automatically complete +the netlink message by filling the missing bits and pieces in the +netlink message header and will deal with addressing based on the +options and address set in the netlink socket. The message is then +passed on to nl_send(). + +If the default sending semantics implemented by nl_send() do not suit +the application, it may overwrite the sending function nl_send() by +specifying an own implementation using the function +nl_cb_overwrite_send(). + +[source,c] +-------- + nl_send_auto(sk, msg) + | + |-----> nl_complete_msg(sk, msg) + | + | + | Own send function specified via nl_cb_overwrite_send() + |- - - - - - - - - - - - - - - - - - - - + v v + nl_send(sk, msg) send_func() +-------- + +.Using nl_send() + +If you do not require any of the automatic message completion +functionality you may use nl_send() directly but beware that any +internal calls to nl_send_auto() by the library to send netlink +messages will still use nl_send(). Therefore if you wish to use any +higher level interfaces and the behaviour of nl_send() is to your +dislike then you must overwrite the nl_send() function via +nl_cb_overwrite_send() + +The purpose of nl_send() is to embed the netlink message into an iovec +structure and pass it on to nl_send_iovec(). + +[source,c] +-------- + nl_send(sk, msg) + | + v + nl_send_iovec(sk, msg, iov, iovlen) +-------- + +.Using nl_send_iovec() + +nl_send_iovec() expects a finalized netlink message and fills out the +struct msghdr used for addressing. It will first check if the struct +nl_msg is addressed to a specific peer (see nlmsg_set_dst()). If not, +it will try to fall back to the peer address specified in the socket +(see nl_socket_set_peer_port(). Otherwise the message will be sent +unaddressed and it is left to the kernel to find the correct peer. + +nl_send_iovec() also adds credentials if present and enabled +(see <>). + +The message is then passed on to nl_sendmsg(). + +[source,c] +-------- + nl_send_iovec(sk, msg, iov, iovlen) + | + v + nl_sendmsg(sk, msg, msghdr) +-------- + +.Using nl_sendmsg() + +nl_sendmsg() expects a finalized netlink message and an optional +struct msghdr containing the peer address. It will copy the local +address as defined in the socket (see nl_socket_set_local_port()) into +the netlink message header. + +At this point, construction of the message finished and it is ready to +be sent. + +[source,c] +-------- + nl_sendmsg(sk, msg, msghdr) + |- - - - - - - - - - - - - - - - - - - - v + | NL_CB_MSG_OUT() + |<- - - - - - - - - - - - - - - - - - - -+ + v + sendmsg() +-------- + +Before sending the application has one last chance to modify the +message. It is passed to the NL_CB_MSG_OUT callback function which +may inspect or modify the message and return an error code. If this +error code is NL_OK the message is sent using sendmsg() resulting in +the number of bytes written being returned. Otherwise the message +sending process is aborted and the error code specified by the +callback function is returned. See <> for more information +on how to set callbacks. + +.Sending Raw Data with nl_sendto() + +If you wish to send raw data over a netlink socket, the following +function will pass on any buffer provided to it directly to sendto(): + +[source,c] +-------- +#include + +int nl_sendto(struct nl_sock *sk, void *buf, size_t size); +-------- + +.Sending of Simple Messages + +A special interface exists for sending of trivial messages. The function +expects the netlink message type, optional netlink message flags, and an +optional data buffer and data length. +[source,c] +-------- +#include + +int nl_send_simple(struct nl_sock *sk, int type, int flags, + void *buf, size_t size); +-------- + +The function will construct a netlink message header based on the message +type and flags provided and append the data buffer as message payload. The +newly constructed message is sent with nl_send_auto(). + +The following example will send a netlink request message causing the +kernel to dump a list of all network links to userspace: + +[source,c] +-------- +#include + +struct nl_sock *sk; +struct rtgenmsg rt_hdr = { + .rtgen_family = AF_UNSPEC, +}; + +sk = nl_socket_alloc(); +nl_connect(sk, NETLINK_ROUTE); + +nl_send_simple(sock, RTM_GETLINK, NLM_F_DUMP, &rt_hdr, sizeof(rt_hdr)); +-------- + +[[core_recv]] +=== Receiving Messages + +The easiest method to receive netlink messages is to call nl_recvmsgs_default(). +It will receive messages based on the semantics defined in the socket. The +application may customize these in detail although the default behaviour will +probably suit most applications. + +nl_recvmsgs_default() will also be called internally by the library whenever +it needs to receive and parse a netlink message. + +The function will fetch the callback configuration stored in the socket and +call nl_recvmsgs(): + +[source,c] +-------- + nl_recvmsgs_default(sk) + | + | cb = nl_socket_get_cb(sk) + v + nl_recvmsgs(sk, cb) +-------- + +.Using nl_recvmsgs() + +nl_recvmsgs() implements the actual receiving loop, it blocks until a +netlink message has been received unless the socket has been put into +non-blocking mode. + +For the unlikely scenario that certain required receive characteristics +can not be achieved by fine tuning the internal recvmsgs function using +the callback configuration (see <>) the application may provide +a complete own implementation of it and overwrite all calls to nl_recvmsgs() +with the function nl_cb_overwrite_recvmsgs(). + +[source,c] +-------- + nl_recvmsgs(sk, cb) + | + | Own recvmsgs function specified via nl_cb_overwrite_recvmsgs() + |- - - - - - - - - - - - - - - - - - - - + v v + internal_recvmsgs() my_recvmsgs() +-------- + +[[core_recv_character]] +.Receive Characteristics + +If the application does not provide its own recvmsgs() implementation +with the function nl_cb_overwrite_recvmsgs() the following characteristics +apply while receiving data from a netlink socket: + +[source,c] +-------- + internal_recvmsgs() + | ++-------------->| Own recv function specified with nl_cb_overwrite_recv() +| |- - - - - - - - - - - - - - - - +| v v +| nl_recv() my_recv() +| |<- - - - - - - - - - - - - - -+ +| |<-------------+ +| v | More data to parse? (nlmsg_next()) +| Parse Message | +| |--------------+ +| v ++------- NLM_F_MULTI set? + | + v + (SUCCESS) +-------- + +The function nl_recv() is invoked first to receive data from the +netlink socket. This function may be overwritten by the application +by an own implementation using the function nl_cb_overwrite_recv(). +This may be useful if the netlink byte stream is in fact not received +from a socket directly but is read from a file or another source. + +If data has been read, it will be attempted to parse the data. This +will be done repeatedly until the parser returns NL_STOP, an error was +returned or all data has been parsed. + +In case the last message parsed successfully was a multipart message +(see <>) and the parser did not +quit due to either an error or NL_STOP nl_recv() respectively the +applications own implementation will be called again and the parser +starts all over. + +See <> for information on how to extract valid +netlink messages from the parser and on how to control the behaviour +of it. + +[[core_parse_character]] +.Parsing Characteristics + +The internal parser is invoked for each netlink message received from +a netlink socket. It is typically fed by nl_recv() (see +<>). + +The parser will first ensure that the length of the data stream +provided is sufficient to contain a netlink message header and that +the message length as specified in the message header does not exceed +it. + +If this criteria is met, a new struct nl_msg is allocated and the +message is passed on to the the callback function NL_CB_MSG_IN if one +is set. Like any other callback function, it may return NL_SKIP to +skip the current message but continue parsing the next message or +NL_STOP to stop parsing completely. + +The next step is to check the sequence number of the message against +the currently expected sequence number. The application may provide +its own sequence number checking algorithm by setting the callback +function NL_CB_SEQ_CHECK to its own implementation. In fact, calling +nl_socket_disable_seq_check() to disable sequence number checking will +do nothing more than set the NL_CB_SEQ_CHECK hook to a function which +always returns NL_OK. + +Another callback hook NL_CB_SEND_ACK exists which is called if the +message has the NLM_F_ACK flag set. Although I am not aware of any +userspace netlink socket doing this, the application may want to send +an ACK message back to the sender (see <>). + +[source,c] +-------- + parse() + | + v + nlmsg_ok() --> Ignore + | + |- - - - - - - - - - - - - - - v + | NL_CB_MSG_IN() + |<- - - - - - - - - - - - - - -+ + | + |- - - - - - - - - - - - - - - v + Sequence Check NL_CB_SEQ_CHECK() + |<- - - - - - - - - - - - - - -+ + | + | Message has NLM_F_ACK set + |- - - - - - - - - - - - - - - v + | NL_CB_SEND_ACK() + |<- - - - - - - - - - - - - - -+ + | + Handle Message Type +-------- + +[[core_auto_ack]] +=== Auto-ACK Mode + +TODO + +== Message Parsing & Construction + +=== Message Format + +See <> for an introduction to the netlink +protocol and its message format. + +.Alignment + +Most netlink protocols enforce a strict alignment policy for all +boundaries. The alignment value is defined by NLMSG_ALIGNTO and is +fixed to 4 bytes. Therefore all netlink message headers, begin of +payload sections, protocol specific headers, and attribute sections +must start at an offset which is a multiple of NLMSG_ALIGNTO. + +[source,c] +-------- +#include + +int nlmsg_size(int payloadlen); +int nlmsg_total_size(int payloadlen); +-------- + +The library provides a set of function to handle alignment +requirements automatically. The function nlmsg_total_size() returns +the total size of a netlink message including the padding to ensure +the next message header is aligned correctly. + +[source,c] +-------- + <----------- nlmsg_total_size(len) ------------> + <----------- nlmsg_size(len) ------------> + +-------------------+- - -+- - - - - - - - +- - -+-------------------+- - - + | struct nlmsghdr | Pad | Payload | Pad | struct nlsmghdr | + +-------------------+- - -+- - - - - - - - +- - -+-------------------+- - - + <---- NLMSG_HDRLEN -----> <- NLMSG_ALIGN(len) -> <---- NLMSG_HDRLEN --- +-------- + +If you need to know if padding needs to be added at the end of a +message, nlmsg_padlen() returns the number of padding bytes that need +to be added for a specific payload length. + +[source,c] +-------- +#include +int nlmsg_padlen(int payloadlen); +-------- + +=== Parsing a Message + +The library offers two different methods of parsing netlink messages. +It offers a low level interface for applications which want to do all +the parsing manually. This method is described below. Alternatively +the library also offers an interface to implement a parser as part of +a cache operations set which is especially useful when your protocol +deals with objects of any sort such as network links, routes, etc. +This high level interface is described in <>. + +.Splitting a byte stream into separate messages + +What you receive from a netlink socket is typically a stream of +messages. You will be given a buffer and its length, the buffer may +contain any number of netlink messages. + +The first message header starts at the beginning of message stream. +Any subsequent message headers are access by calling nlmsg_next() on +the previous header. + +[source,c] +-------- +#include + +struct nlmsghdr *nlmsg_next(struct nlmsghdr *hdr, int *remaining); +-------- + +The function nlmsg_next() will automatically substract the size of the +previous message from the remaining number of bytes. + +Please note, there is no indication in the previous message whether +another message follows or not. You must assume that more messages +follow until all bytes of the message stream have been processed. + +To simplify this, the function nlmsg_ok() exists which returns true if +another message fits into the remaining number of bytes in the message +stream. nlmsg_valid_hdr() is similar, it checks whether a specific +netlink message contains at least a minimum of payload. + +[source,c] +-------- +#include + +int nlmsg_valid_hdr(const struct nlmsghdr *hdr, int payloadlen); +int nlmsg_ok(const struct nlmsghdr *hdr, int remaining); +-------- + +A typical use of these functions looks like this: + +[source,c] +-------- +#include + +void my_parse(void *stream, int length) +{ + struct nlmsghdr *hdr = stream; + + while (nlmsg_ok(hdr, length)) { + // Parse message here + hdr = nlmsg_next(hdr, &length); + } +} +-------- + +CAUTION: nlmsg_ok() only returns true if the *complete* message including + the message payload fits into the remaining buffer length. It will + return false if only a part of it fits. + +The above can also be written using the iterator nlmsg_for_each(): + +[source,c] +-------- +#include + +struct nlmsghdr *hdr; + +nlmsg_for_each(hdr, stream, length) { + /* do something with message */ +} +-------- + +.Message Payload + +The message payload is appended to the message header and is guaranteed +to start at a multiple of +NLMSG_ALIGNTO+. Padding at the end of the +message header is added if necessary to ensure this. The function +nlmsg_data() will calculate the necessary offset based on the message +and returns a pointer to the start of the message payload. + +[source,c] +-------- +#include + +void *nlmsg_data(const struct nlmsghdr *nlh); +void *nlmsg_tail(const struct nlmsghdr *nlh); +int nlmsg_datalen(const struct nlmsghdr *nlh); +-------- + +The length of the message payload is returned by nlmsg_datalen(). + +[source,c] +-------- + <--- nlmsg_datalen(nlh) ---> + +-------------------+- - -+----------------------------+- - -+ + | struct nlmsghdr | Pad | Payload | Pad | + +-------------------+- - -+----------------------------+- - -+ +nlmsg_data(nlh) ---------------^ ^ +nlmsg_tail(nlh) --------------------------------------------------^ +-------- + +The payload may consist of arbitrary data but may have strict alignment +and formatting rules depending on the actual netlink protocol. + +[[core_msg_attr]] +.Message Attributes + +Most netlink protocols use netlink attributes. It not only makes the +protocol self documenting but also gives flexibility in expanding the +protocol at a later point. New attributes can be added at any time and +older attributes can be obsoleted by newer ones without breaking +binary compatibility of the protocol. + +[source,c] +-------- + <---------------------- payload -------------------------> + <----- hdrlen ----> <- nlmsg_attrlen(nlh, hdrlen) -> + +-------------------+- - -+----- ------------+- - -+--------------------------------+- - -+ + | struct nlmsghdr | Pad | Protocol Header | Pad | Attributes | Pad | + +-------------------+- - -+-------------------+- - -+--------------------------------+- - -+ +nlmsg_attrdata(nlh, hdrlen) -----------------------------^ +-------- + +The function nlmsg_attrdata() returns a pointer to the begin of the +attributes section. The length of the attributes section is returned +by the function nlmsg_attrlen(). + +[source,c] +-------- +#include + +struct nlattr *nlmsg_attrdata(const struct nlmsghdr *hdr, int hdrlen); +int nlmsg_attrlen(const struct nlmsghdr *hdr, int hdrlen); +-------- + +See <> for more information on how to use netlink attributes. + +.Parsing a Message the Easy Way + +The function nlmsg_parse() validate a complete netlink message in one +step. If +hdrlen > 0+ it will first call nlmsg_valid_hdr() to check +if the protocol header fits into the message. If there is more payload +to parse, it will assume it to be attributes and parse the payload +accordingly. The function behaves exactly like nla_parse() when +parsing attributes, see <>. + +[source,c] +-------- +int nlmsg_parse(struct nlmsghdr *hdr, int hdrlen, struct nlattr **attrs, + int maxtype, struct nla_policy *policy); +-------- + +The function nlmsg_validate() is based on nla_validate() and behaves +exactly the same as nlmsg_parse() except that it only validates and +will not fill anarray with pointers to each attribute. + +[source,c] +-------- +int nlmsg_validate(struct nlmsghdr *hdr, int hdrlen, intmaxtype, + struct nla_policy *policy); +-------- + +See <> for an example and more information on +attribute parsing. + +=== Construction of a Message + +See <> for information on the netlink message format +and alignment requirements. + +Message construction is based on struct nl_msg which uses an internal +buffer to store the actual netlink message. struct nl_msg +does not+ +point to the netlink message header. Use nlmsg_hdr() to retrieve a +pointer to the netlink message header. + +At allocation time, a maximum message size is specified. It defaults +to a page (PAGE_SIZE). The application constructing the message will +reserve space out of this maximum message size repeatedly for each +header or attribute added. This allows construction of messages across +various layers of code where lower layers do not need to know about +the space requirements of upper layers. + ++Why is setting the maximum message size necessary?+ This +question is often raised in combination with the proposed solution of +reallocating the message payload buffer on the fly using realloc(). +While it is possible to reallocate the buffer during construction +using nlmsg_expand() it will make all pointers into the message buffer +become stale. This breaks usage of nlmsg_hdr(), nla_nest_start(), and +nla_nest_end() and is therefore not acceptable as default behaviour. + +.Allocating struct nl_msg + +The first step in constructing a new netlink message it to allocate a +`struct nl_msg` to hold the message header and payload. Several +functions exist to simplify various tasks. + +[source,c] +-------- +#include + +struct nl_msg *nlmsg_alloc(void); +void nlmsg_free(struct nl_msg *msg); +-------- + +The function nlmsg_alloc() is the default message allocation function. +It allocates a new message using the default maximum message size which +equals to one page (PAGE_SIZE). The application can change the default +size for messages by calling nlmsg_set_default_size(): + +[source,c] +-------- +void nlmsg_set_default_size(size_t); +-------- + +CAUTION: Calling nlmsg_set_default_size() does not change the maximum + message size of already allocated messages. + +[source,c] +-------- +struct nl_msg *nlmsg_alloc_size(size_t max); +-------- + +Instead of changing the default message size, the function +nlmsg_alloc_size() can be used to allocate a message with an individual +maximum message size. + + +If the netlink message header is already known at allocation time, the +application may sue nlmsg_inherit(). It will allocate a message using +the default maximum message size and copy the header into the message. +Calling nlmsg_inherit with +set+ to NULL is equivalent to calling +nlmsg_alloc(). + +[source,c] +-------- +struct nl_msg *nlmsg_inherit(struct nlmsghdr *hdr); +-------- + +Alternatively nlmsg_alloc_simple() takes a netlink message type and +netlink message flags. It is equivalent to nlmsg_inherit() except that it +takes the two common header fields as arguments instead of a complete +header. + +[source,c] +-------- +#include + +struct nl_msg *nlmsg_alloc_simple(int nlmsg_type, int flags); +-------- + +.Appending the netlink message header + +After allocating struct nl_msg, the netlink message header needs to be +added unless one of the function nlmsg_alloc_simple() or nlmsg_inherit() +have been used for allocation in which case this step will replace the +netlink message header already in place. + +[source,c] +-------- +#include + +struct nlmsghdr *nlmsg_put(struct nl_msg *msg, uint32_t port, uint32_t seqnr, + int nlmsg_type, int payload, int nlmsg_flags); +-------- + +The function nlmsg_put() will build a netlink message header out of ++nlmsg_type+, +nlmsg_flags+, +seqnr+, and +port+ and copy it into the +netlink message. +seqnr+ can be set to +NL_AUTO_SEQ+ to indicate +that the next possible sequence number should be used automatically. +To use this feature, the message must be sent using the function +nl_send_auto(). Like +port+, the argument +seqnr+ can be set to ++NL_AUTO_PORT+ indicating that the local port assigned to the socket +should be used as source port. This is generally a good idea unless +you are replying to a request. See <> +for more information on how to fill the header. + +NOTE: The argument +payload+ can be used by the application to reserve + room for additional data after the header. A value of > 0 is + equivalent to calling +nlmsg_reserve(msg, payload, NLMSG_ALIGNTO)+. + See <> for more information on reserving room for + data. + +.Example +[source,c] +-------- +#include + +struct nlmsghdr *hdr; +struct nl_msg *msg; +struct myhdr { + uint32_t foo1, foo2; +} shdr = { 10, 20 }; + +/* Allocate a message with the default maximum message size */ +msg = nlmsg_alloc(); + +/* + * Add header with message type MY_MSGTYPE, the flag NLM_F_CREATE, + * let library fill port and sequence number, and reserve room for + * struct myhdr + */ +hdr = nlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, MY_MSGTYPE, sizeof(shdr), NLM_F_CREATE); + +/* Copy own header into newly reserved payload section */ +memcpy(nlmsg_data(hdr), &shdr, sizeof(shdr)); + +/* + * The message will now look like this: + * +-------------------+- - -+----------------+- - -+ + * | struct nlmsghdr | Pad | struct myhdr | Pad | + * +-------------------+-----+----------------+- - -+ + * nlh -^ / \ + * +--------+---------+ + * | foo1 | foo2 | + * +--------+---------+ + */ +-------- + +[[core_msg_reserve]] +.Reserving room at the end of the message + +Most functions described later on will automatically take care of +reserving room for the data that is added to the end of the netlink +message. In some situations it may be required for the application +to reserve room directly though. + +[source,c] +-------- +#include + +void *nlmsg_reserve(struct nl_msg *msg, size_t len, int pad); +-------- + +The function nlmsg_reserve() reserves +len+ bytes at the end of the +netlink message and returns a pointer to the start of the reserved area. +The +pad+ argument can be used to request +len+ to be aligned to any +number of bytes prior to reservation. + +The following example requests to reserve a 17 bytes area at the end of +message aligned to 4 bytes. Therefore a total of 20 bytes will be +reserved. + +[source,c] +-------- +#include + +void *buf = nlmsg_reserve(msg, 17, 4); +-------- + +NOTE: `nlmsg_reserve()` will *not* align the start of the buffer. Any + alignment requirements must be provided by the owner of the + previous message section. + +.Appending data at the end of the message + +The function `nlmsg_append()` appends `len` bytes at the end of the +message, padding it if requested and necessary. + +[source,c] +-------- +#include + +int nlmsg_append(struct nl_msg *msg, void *data, size_t len, int pad); +-------- + +It is equivalent to calling `nlmsg_reserve()` and `memcpy()`ing the +data into the freshly reserved data section. + +NOTE: `nlmsg_append()` will *not* align the start of the data. Any + alignment requirements must be provided by the owner of the + previous message section. + +.Adding attributes to a message + +Construction of attributes and addition of attributes to the message is +covered in section <>. + +[[core_attr]] +== Attributes + +Any form of payload should be encoded as netlink attributes whenever +possible. Use of attributes allows to extend any netlink protocol in +the future without breaking binary compatibility. F.e. Suppose your +device may currently be using 32 bit counters for statistics but years +later the device switches to maintaining 64 bit counters to account +for faster network hardware. If your protocol is using attributes the +move to 64 bit counters is trivial and only involves in sending an +additional attribute containing the 64 bit variants while still +providing the old legacy 32 bit counters. If your protocol is not using +attributes you will not be able to switch data types without breaking +all existing users of the protocol. + +The concept of nested attributes also allows for subsystems of your +protocol to implement and maintain their own attribute schemas. Suppose +a new generation of network device is introduced which requires a +completely new set of configuration settings which was unthinkable when +the netlink protocol was initially designed. Using attributes the new +generation of devices may define a new attribute and fill it with its +own new structure of attributes which extend or even obsolete the old +attributes. + +Therefore, _always_ use attributes even if you are almost certain that +the message format will never ever change in the future. + +[[core_attr_format]] +=== Attribute Format + +Netlink attributes allow for any number of data chunks of arbitrary +length to be attached to a netlink message. See <> +for more information on where attributes are stored in the message. + +The format of the attributes data returned by nlmsg_attrdata() is as +follows: + +[source,c] +-------- + <----------- nla_total_size(payload) -----------> + <---------- nla_size(payload) -----------> + +-----------------+- - -+- - - - - - - - - +- - -+-----------------+- - - + | struct nlattr | Pad | Payload | Pad | struct nlattr | + +-----------------+- - -+- - - - - - - - - +- - -+-----------------+- - - + <---- NLA_HDRLEN -----> <--- NLA_ALIGN(len) ---> <---- NLA_HDRLEN --- +-------- + +Every attribute must start at an offset which is a multiple of ++NLA_ALIGNTO+ (4 bytes). If you need to know whether an attribute needs +to be padded at the end, the function nla_padlen() returns the number +of padding bytes that will or need to be added. + +image:attribute_hdr.png["Netlink Attribute Header"] + +Every attribute is encoded with a type and length field, both 16 bits, +stored in the attribute header (struct nlattr) preceding the attribute +payload. The length of an attribute is used to calculate the offset to +the next attribute. + +[[core_attr_parse]] +=== Parsing Attributes + +[[core_attr_parse_split]] +.Splitting an Attributes Stream into Attributes + +Although most applications will use one of the functions from the +nlmsg_parse() family (See <>) an interface exists +to split the attributes stream manually. + +As described in <> the attributes section contains a +infinite sequence or stream of attributes. The pointer returned by +nlmsg_attrdata() (See <>) points to the first attribute +header. Any subsequent attribute is accessed with the function nla_next() +based on the previous header. + +[source,c] +-------- +#include + +struct nlattr *nla_next(const struct nlattr *attr, int *remaining); +-------- + +The semantics are equivalent to nlmsg_next() and thus nla_next() will also +subtract the size of the previous attribute from the remaining number of +bytes in the attributes stream. + +Like messages, attributes do not contain an indicator whether another +attribute follows or not. The only indication is the number of bytes left +in the attribute stream. The function nla_ok() exists to determine whether +another attribute fits into the remaining number of bytes or not. + +[source,c] +-------- +#include + +int nla_ok(const struct nlattr *attr, int remaining); +-------- + +A typical use of nla_ok() and nla_next() looks like this: + +.nla_ok()/nla_next() usage +[source,c] +-------- +#include +#include + +struct nlattr *hdr = nlmsg_attrdata(msg, 0); +int remaining = nlmsg_attrlen(msg, 0); + +while (nla_ok(hdr, remaining)) { + /* parse attribute here */ + hdr = nla_next(hdr, &remaining); +}; +-------- + +NOTE: `nla_ok()` only returns true if the *complete* attributes + including the attribute payload fits into the remaining number + of bytes. + +.Accessing Attribute Header and Payload + +Once the individual attributes have been sorted out by either splitting +the attributes stream or using another interface the attribute header +and payload can be accessed. + +[source,c] +-------- + <- nla_len(hdr) -> + +-----------------+- - -+- - - - - - - - - +- - -+ + | struct nlattr | Pad | Payload | Pad | + +-----------------+- - -+- - - - - - - - - +- - -+ +nla_data(hdr) ---------------^ +-------- + +The functions nla_len() and nla_type() can be used to access the attribute +header. nla_len() will return the length of the payload not including +eventual padding bytes. nla_type returns the attribute type. + +[source,c] +-------- +#include + +int nla_len(const struct nlattr *hdr); +int nla_type(const struct nlattr *hdr); +-------- + +The function nla_data() will return a pointer to the attribute +payload. Please note that due to +NLA_ALIGNTO+ being 4 bytes it may +not be safe to cast and dereference the pointer for any datatype +larger than 32 bit depending on the architecture the application is +run on. + +[source,c] +-------- +#include + +void *nla_data(const struct nlattr *hdr); +-------- + +[NOTE] +Never rely on the size of a payload being what you expect it to be. +_Always_ verify the payload size and make sure that it matches your +expectations. See <> + +[[core_attr_validation]] +.Attribute Validation + +When receiving netlink attributes, the receiver has certain expectations +on how the attributes should look like. These expectations must be +defined to make sure the sending side meets our expectations. For this +purpose, an attribute validation interface exists which must be used +prior to accessing any payload. + +All functions providing attribute validation functionality are based +on struct nla_policy: + +[source,c] +-------- +struct nla_policy { + uint16_t type; + uint16_t minlen; + uint16_t maxlen; +}; +-------- + +The +type+ member specifies the datatype of the attribute, e.g. ++NLA_U32+, +NLA_STRING+, +NLA_FLAG+. The default is +NLA_UNSPEC+. The ++minlen+ member defines the minmum payload length of an attribute to +be considered a valid attribute. The value for +minlen+ is implicit +for most basic datatypes such as integers or flags. The +maxlen+ +member can be used to define a maximum payload length for an +attribute to still be considered valid. + +NOTE: Specifying a maximum payload length is not recommended when + encoding structures in an attribute as it will prevent any + extension of the structure in the future. Something that is + frequently done in netlink protocols and does not break + backwards compatibility. + +One of the functions which use struct nla_policy is nla_validate(). +The function expects an array of struct nla_policy and will access the +array using the attribute type as index. If an attribute type is out +of bounds the attribute is assumed to be valid. This is intentional +behaviour to allow older applications not yet aware of recently +introduced attributes to continue functioning. + +[source,c] +-------- +#include + +int nla_validate(struct nlattr *head, int len, int maxtype, struct nla_policy *policy); +-------- + +The function nla_validate() returns 0 if all attributes are valid, +otherwise a validation failure specific error code is returned. + +Most applications will rarely use nla_validate() directly but use +nla_parse() instead which takes care of validation in the same way but +also parses the the attributes in the same step. See +<> for an example and more information. + +The validation process in detail: + +. If attribute type is 0 or exceeds +maxtype+ attribute is + considered valid, 0 is returned. +. If payload length is < +minlen+, +-NLE_ERANGE+ is returned. +. If +maxlen+ is defined and payload exceeds it, +-NLE_ERANGE+ + is returned. +. Datatype specific requirements rules, see <> +. If all is ok, 0 is returned. + +[[core_attr_parse_easy]] +.Parsing Attributes the Easy Way + +Most applications will not want to deal with splitting attribute +streams themselves as described in <> +A much easier method is to use nla_parse(). + +[source,c] +-------- +#include + +int nla_parse(struct nlattr **attrs, int maxtype, struct nlattr *head, + int len, struct nla_policy *policy); +-------- + +The function nla_parse() will iterate over a stream of attributes, +validate each attribute as described in <> +If the validation of all attributes succeeds, a pointer to each attribute +is stored in the +attrs+ array at `attrs[nla_type(attr)]`. + +As an alernative to nla_parse() the function nlmsg_parse() can be used +to parse the message and its attributes in one step. See +<> for information on how to use these functions. + +.Example: + +The following example demonstrates how to parse a netlink message sent +over a netlink protocol which does not use protocol headers. The example +does enforce an attribute policy however, the attribute MY_ATTR_FOO must +be a 32 bit integer, and the attribute MY_ATTR_BAR must be a string with +a maximum length of 16 characters. + +[source,c] +--------- +#include +#include + +enum { + MY_ATTR_FOO = 1, + MY_ATTR_BAR, + __MY_ATTR_MAX, +}; + +#define MY_ATTR_MAX (__MY_ATTR_MAX - 1) + +static struct nla_policy my_policy[MY_ATTR_MAX+1] = { + [MY_ATTR_FOO] = { .type = NLA_U32 }, + [MY_ATTR_BAR] = { .type = NLA_STRING, + .maxlen = 16 }, +}; + +void parse_msg(struct nlmsghdr *nlh) +{ + struct nlattr *attrs[MY_ATTR_MAX+1]; + + if (nlmsg_parse(nlh, 0, attrs, MY_ATTR_MAX, my_policy) < 0) + /* error */ + + if (attrs[MY_ATTR_FOO]) { + /* MY_ATTR_FOO is present in message */ + printf("value: %u\n", nla_get_u32(attrs[MY_ATTR_FOO])); + } +} +--------- + +.Locating a Single Attribute + +An application only interested in a single attribute can use one of the +functions nla_find() or nlmsg_find_attr(). These function will iterate +over all attributes, search for a matching attribute and return a pointer +to the corresponding attribute header. + +[source,c] +-------- +#include + +struct nlattr *nla_find(struct nlattr *head, int len, int attrtype); +-------- + +[source,c] +-------- +#include + +struct nlattr *nlmsg_find_attr(struct nlmsghdr *hdr, int hdrlen, int attrtype); +-------- + +NOTE: `nla_find()` and `nlmsg_find_attr()` will *not* search in nested + attributes recursively, see <>. + +==== Iterating over a Stream of Attributes + +In some situations it does not make sense to assign a unique attribute +type to each attribute in the attribute stream. For example a list may +be transferred using a stream of attributes and even if the attribute type +is incremented for each attribute it may not make sense to use the +nlmsg_parse() or nla_parse() function to fill an array. + +Therefore methods exist to iterate over a stream of attributes: + +[source,c] +-------- +#include + +nla_for_each_attr(attr, head, len, remaining) +-------- + +nla_for_each_attr() is a macro which can be used in front of a code +block: + +[source,c] +-------- +#include + +struct nalttr *nla; +int rem; + +nla_for_each_attr(nla, attrstream, streamlen, rem) { + /* validate & parse attribute */ +} + +if (rem > 0) + /* unparsed attribute data */ +-------- + +[[core_attr_constr]] +=== Attribute Construction + +The interface to add attributes to a netlink message is based on the +regular message construction interface. It assumes that the message +header and an eventual protocol header has been added to the message +already. + +[source,c] +-------- +struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int len); +-------- + +The function nla_reserve() adds an attribute header at the end of the +message and reserves room for +len+ bytes of payload. The function +returns a pointer to the attribute payload section inside the message. +Padding is added at the end of the attribute to ensure the next +attribute is properly aligned. + +[source,c] +-------- +int nla_put(struct nl_msg *msg, int attrtype, int attrlen, const void *data); +-------- + +The function nla_put() is based on nla_reserve() but takes an additional +pointer +data+ pointing to a buffer containing the attribute payload. +It will copy the buffer into the message automatically. + +.Example: + +[source,c] +-------- +struct my_attr_struct { + uint32_t a; + uint32_t b; +}; + +int my_put(struct nl_msg *msg) +{ + struct my_attr_struct obj = { + .a = 10, + .b = 20, + }; + + return nla_put(msg, ATTR_MY_STRUCT, sizeof(obj), &obj); +} +-------- + +See <> for datatype specific attribute construction +functions. + +.Exception Based Attribute Construction + +Like in the kernel API an exception based construction interface is +provided. The behaviour of the macros is identical to their regular +function counterparts except that in case of an error, the target +`nla_put_failure` is jumped. + +.Example: +[source,c] +-------- +#include +#include + +void construct_attrs(struct nl_msg *msg) +{ + NLA_PUT_STRING(msg, MY_ATTR_FOO1, "some text"); + NLA_PUT_U32(msg, MY_ATTR_FOO1, 0x1010); + NLA_PUT_FLAG(msg, MY_ATTR_FOO3, 1); + + return 0; + +nla_put_failure: + /* NLA_PUT* macros jump here in case of an error */ + return -EMSGSIZE; +} +-------- + +See <> for more information on the datatype specific +exception based variants. + +[[core_attr_types]] +=== Attribute Data Types + +A number of basic data types have been defined to simplify access and +validation of attributes. The datatype is not encoded in the +attribute, therefore the sender and receiver are required to use the +same definition on what attribute is of what type. + +[options="header", cols="1m,5"] +|================================================ +| Type | Description +| NLA_UNSPEC | Unspecified attribute +| NLA_U{8\|16\|32} | Integers +| NLA_STRING | String +| NLA_FLAG | Flag +| NLA_NESTED | Nested attribute +|================================================ + +Besides simplified access to the payload of such datatypes, the major +advantage is the automatic validation of each attribute based on a +policy. The validation ensures safe access to the payload by checking +for minimal payload size and can also be used to enforce maximum +payload size for some datatypes. + +==== Integer Attributes + +The most frequently used datatypes are integers. Integers come in four +different sizes: +[horizontal] +NLA_U8:: 8bit integer +NLA_U16:: 16bit integer +NLA_U32:: 32bit integer +NLA_U64:: 64bit integer + +Note that due to the alignment requirements of attributes the integer +attribute +NLA_u8+ and +NLA_U16+ will not result in space savings in +the netlink message. Their use is intended to limit the range of +values. + +.Parsing Integer Attributes + +[source,c] +-------- +#include + +uint8_t nla_get_u8(struct nlattr *hdr); +uint16_t nla_get_u16(struct nlattr *hdr); +uint32_t nla_get_u32(struct nlattr *hdr); +uint64_t nla_get_u64(struct nlattr *hdr); +-------- + +Example: + +[source,c] +-------- +if (attrs[MY_ATTR_FOO]) + uint32_t val = nla_get_u32(attrs[MY_ATTR_FOO]); +-------- + +.Constructing Integer Attributes + +[source,c] +-------- +#include + +int nla_put_u8(struct nl_msg *msg, int attrtype, uint8_t value); +int nla_put_u16(struct nl_msg *msg, int attrtype, uint16_t value); +int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value); +int nla_put_u64(struct nl_msg *msg, int attrtype, uint64_t value); +-------- + +Exception based: + +[source,c] +-------- +NLA_PUT_U8(msg, attrtype, value) +NLA_PUT_U16(msg, attrtype, value) +NLA_PUT_U32(msg, attrtype, value) +NLA_PUT_U64(msg, attrtype, value) +-------- + +.Validation + +Use +NLA_U8+, +NLA_U16+, +NLA_U32+, or +NLA_U64+ to define the type of +integer when filling out a struct nla_policy array. It will +automatically enforce the correct minimum payload length policy. + +Validation does not differ between signed and unsigned integers, only +the size matters. If the application wishes to enforce particular value +ranges it must do so itself. + +[source,c] +-------- +static struct nla_policy my_policy[ATTR_MAX+1] = { + [ATTR_FOO] = { .type = NLA_U32 }, + [ATTR_BAR] = { .type = NLA_U8 }, +}; +-------- + +The above is equivalent to: +[source,c] +-------- +static struct nla_policy my_policy[ATTR_MAX+1] = { + [ATTR_FOO] = { .minlen = sizeof(uint32_t) }, + [ATTR_BAR] = { .minlen = sizeof(uint8_t) }, +}; +-------- + +==== String Attributes + +The string datatype represents a NUL termianted character string of +variable length. It is not intended for binary data streams. + +The payload of string attributes can be accessed with the function +nla_get_string(). nla_strdup() calls strdup() on the payload and returns +the newly allocated string. + +[source,c] +-------- +#include + +char *nla_get_string(struct nlattr *hdr); +char *nla_strdup(struct nlattr *hdr); +-------- + +String attributes are constructed with the function +nla_put_string()+ +respectively +NLA_PUT_STRING()+. The length of the payload will be +strlen()+1, the trailing NUL byte is included. + +[source,c] +-------- +int nla_put_string(struct nl_msg *msg, int attrtype, const char *data); + +NLA_PUT_STRING(msg, attrtype, data) +-------- + +For validation purposes the type +NLA_STRING+ can be used in ++struct nla_policy+ definitions. It implies a minimum payload length +of 1 byte and checks for a trailing NUL byte. Optionally the +maxlen+ +member defines the maximum length of a character string (including the +trailing NUL byte). + +[source,c] +-------- +static struct nla_policy my_policy[] = { + [ATTR_FOO] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, +}; +-------- + +==== Flag Attributes + +The flag attribute represents a boolean datatype. The presence of the +attribute implies a value of +true+, the absence of the attribute +implies the value +false+. Therefore the payload length of flag +attributes is always 0. + +[source,c] +-------- +int nla_get_flag(struct nlattr *hdr); +int nla_put_flag(struct nl_msg *msg, int attrtype); +-------- + +The type +NLA_FLAG+ is used for validation purposes. It implies a ++maxlen+ value of 0 and thus enforces a maximum payload length of 0. + +.Example: +[source,c] +-------- +/* nla_put_flag() appends a zero sized attribute to the message. */ +nla_put_flag(msg, ATTR_FLAG); + +/* There is no need for a receival function, the presence is the value. */ +if (attrs[ATTR_FLAG]) + /* flag is present */ +-------- + +[[core_attr_nested]] +==== Nested Attributes + +As described in <>, attributes can be nested allowing for +complex tree structures of attributes. It is commonly used to delegate +the responsibility of a subsection of the message to a subsystem. +Nested attributes are also commonly used for transmitting list of objects. + +When nesting attributes, the nested attributes are included as payload +of a container attribute. + +NOTE: When validating the attributes using nlmsg_validate(), + nlmsg_parse(), nla_validate(), or nla_parse() only the + attributes on the first level are being validated. None of these + functions will validate attributes recursively. Therefore you + must explicitly call nla_validate() or use nla_parse_nested() + for each level of nested attributes. + +The type +NLA_NESTED+ should be used when defining nested attributes +in a struct nla_policy definition. It will not enforce any minimum +payload length unless +minlen+ is specified explicitly. This is +because some netlink protocols implicitly allow empty container +attributes. + +[source,c] +-------- +static struct nla_policy my_policy[] = { + [ATTR_OPTS] = { .type = NLA_NESTED }, +}; +-------- + +.Parsing of Nested Attributes + +The function nla_parse_nested() is used to parse nested attributes. +Its behaviour is identical to nla_parse() except that it takes a +struct nlattr as argument and will use the payload as stream of +attributes. + +[source,c] +-------- +if (attrs[ATTR_OPTS]) { + struct nlattr *nested[NESTED_MAX+1]; + struct nla_policy nested_policy[] = { + [NESTED_FOO] = { .type = NLA_U32 }, + }; + + if (nla_parse_nested(nested, NESTED_MAX, attrs[ATTR_OPTS], nested_policy) < 0) + /* error */ + + if (nested[NESTED_FOO]) + uint32_t val = nla_get_u32(nested[NESTED_FOO]); +} +-------- + +.Construction of Nested Attributes + +Attributes are nested by surrounding them with calls to nla_nest_start() +and nla_nest_end(). nla_nest_start() will add an attribute header to +the message but no actual payload. All data added to the message from +this point on will be part of the container attribute until nla_nest_end() +is called which "closes" the attribute, correcting its payload length to +include all data length. + +[source,c] +-------- +int put_opts(struct nl_msg *msg) +{ + struct nlattr *opts; + + if (!(opts = nla_nest_start(msg, ATTR_OPTS))) + goto nla_put_failure; + + NLA_PUT_U32(msg, NESTED_FOO, 123); + NLA_PUT_STRING(msg, NESTED_BAR, "some text"); + + nla_nest_end(msg, opts); + return 0; + +nla_put_failure: + nla_nest_cancel(msg, opts); + return -EMSGSIZE; +} +-------- + +==== Unspecified Attribute + +This is the default attribute type and used when none of the basic +datatypes is suitable. It represents data of arbitrary type and length. + +See <> for a more information on +a special interface allowing the allocation of abstract address object +based on netlink attributes which carry some form of network address. + +See <> for more information +on how to allocate abstract data objects based on netlink attributes. + +Use the function nla_get() and nla_put() to access the payload and +construct attributes. See <> +for an example. + +=== Examples + +==== Constructing a Netlink Message with Attributes + +[source,c] +-------- +struct nl_msg *build_msg(int ifindex, struct nl_addr *lladdr, int mtu) +{ + struct nl_msg *msg; + struct nlattr *info, *vlan; + struct ifinfomsg ifi = { + .ifi_family = AF_INET, + .ifi_index = ifindex, + }; + + /* Allocate a default sized netlink message */ + if (!(msg = nlmsg_alloc_simple(RTM_SETLINK, 0))) + return NULL; + + /* Append the protocol specific header (struct ifinfomsg)*/ + if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0) + goto nla_put_failure + + /* Append a 32 bit integer attribute to carry the MTU */ + NLA_PUT_U32(msg, IFLA_MTU, mtu); + + /* Append an unspecific attribute to carry the link layer address */ + NLA_PUT_ADDR(msg, IFLA_ADDRESS, lladdr); + + /* Append a container for nested attributes to carry link information */ + if (!(info = nla_nest_start(msg, IFLA_LINKINFO))) + goto nla_put_failure; + + /* Put a string attribute into the container */ + NLA_PUT_STRING(msg, IFLA_INFO_KIND, "vlan"); + + /* + * Append another container inside the open container to carry + * vlan specific attributes + */ + if (!(vlan = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + /* add vlan specific info attributes here... */ + + /* Finish nesting the vlan attributes and close the second container. */ + nla_nest_end(msg, vlan); + + /* Finish nesting the link info attribute and close the first container. */ + nla_nest_end(msg, info); + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +} +------ + +==== Parsing a Netlink Message with Attributes + +[source,c] +-------- +int parse_message(struct nlmsghdr *hdr) +{ + /* + * The policy defines two attributes: a 32 bit integer and a container + * for nested attributes. + */ + struct nla_policy attr_policy[] = { + [ATTR_FOO] = { .type = NLA_U32 }, + [ATTR_BAR] = { .type = NLA_NESTED }, + }; + struct nlattr *attrs[ATTR_MAX+1]; + int err; + + /* + * The nlmsg_parse() function will make sure that the message contains + * enough payload to hold the header (struct my_hdr), validates any + * attributes attached to the messages and stores a pointer to each + * attribute in the attrs[] array accessible by attribute type. + */ + if ((err = nlmsg_parse(hdr, sizeof(struct my_hdr), attrs, ATTR_MAX, + attr_policy)) < 0) + goto errout; + + if (attrs[ATTR_FOO]) { + /* + * It is safe to directly access the attribute payload without + * any further checks since nlmsg_parse() enforced the policy. + */ + uint32_t foo = nla_get_u32(attrs[ATTR_FOO]); + } + + if (attrs[ATTR_BAR]) { + struct *nested[NESTED_MAX+1]; + + /* + * Attributes nested in a container can be parsed the same way + * as top level attributes. + */ + err = nla_parse_nested(nested, NESTED_MAX, attrs[ATTR_BAR], + nested_policy); + if (err < 0) + goto errout; + + // Process nested attributes here. + } + + err = 0; +errout: + return err; +} +-------- + +[[core_cb]] +== Callback Configurations + +Callback hooks and overwriting capabilities are provided in various places +inside the library to control the behaviour of several functions. All the +callback and overwrite functions are packed together in struct nl_cb which +is attached to a netlink socket or passed on to functions directly. + +=== Callback Hooks + +Callback hooks are spread across the library to provide entry points for +message processing and to take action upon certain events. + +Callback functions may return the following return codes: +[options="header", cols="1m,4"] +|======================================================================== +| Return Code | Description +| NL_OK | Proceed. +| NL_SKIP | Skip message currently being processed and continue + parsing the receive buffer. +| NL_STOP | Stop parsing and discard all remaining data in the + receive buffer. +|======================================================================== + +.Default Callback Implementations + +The library provides three sets of default callback implementations: +* +NL_CB_DEFAULT+ This is the default set. It implements the default behaviour. + See the table below for more information on the return codes of each + function. +* +NL_CB_VERBOSE+ This set is based on the default set but will cause an + error message to be printed to stderr for error messages, invalid + messages, message overruns and unhandled valid messages. The + +arg+ pointer in nl_cb_set() and nl_cb_err() can be used to + provide a FILE * which overwrites stderr. +* +NL_CB_DEBUG+ This set is intended for debugging purposes. It is + based on the verbose set but will decode and dump each message sent + or received to the console. + +.Message Processing Callbacks + +.nl_sendmsg() callback hooks: +[cols="2m,4e,1m", options="header"] +|============================================================================ +| Callback ID | Description | Default Return Value +| NL_CB_MSG_OUT | Each message sent | NL_OK +|============================================================================ + +Any function called by NL_CB_MSG_OUT may return a negative error code to +prevent the message from being sent and the error code being returned. + +nl_recvmsgs() callback hooks (ordered by priority): +[cols="2m,4e,1m", options="header"] +|============================================================================ +| Callback ID | Description | Default Return Value +| NL_CB_MSG_IN | Each message received | NL_OK +| NL_CB_SEQ_CHECK | May overwrite sequence check algo | NL_OK +| NL_CB_INVALID | Invalid messages | NL_STOP +| NL_CB_SEND_ACK | Messages with NLM_F_ACK flag set | NL_OK +| NL_CB_FINISH | Messages of type NLMSG_DONE | NL_STOP +| NL_CB_SKIPPED | Messages of type NLMSG_NOOP | NL_SKIP +| NL_CB_OVERRUN | Messages of type NLMSG_OVERRUN | NL_STOP +| NL_CB_ACK | ACK Messages | NL_STOP +| NL_CB_VALID | Each valid message | NL_OK +|============================================================================ + +Any of these functions may return NL_OK, NL_SKIP, or NL_STOP. + +Message processing callback functions are set with nl_cb_set(): +[source,c] +-------- +#include + +int nl_cb_set(struct nl_cb *cb, enum nl_cb_type type, enum nl_cb_kind kind, + nl_recvmsg_msg_cb_t func, void *arg); + +typedef int (*nl_recvmsg_msg_cb_t)(struct nl_msg *msg, void *arg); +-------- + +.Callback for Error Messages + +A special function prototype is used for the error message callback hook: + +[source,c] +-------- +#include + +int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind, nl_recvmsg_err_cb_t func, void *arg); + +typedef int(* nl_recvmsg_err_cb_t)(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg); +-------- + +.Example: Setting up a callback set +[source,c] +-------- +#include + +/* Allocate a callback set and initialize it to the verbose default set */ +struct nl_cb *cb = nl_cb_alloc(NL_CB_VERBOSE); + +/* Modify the set to call my_func() for all valid messages */ +nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, my_func, NULL); + +/* + * Set the error message handler to the verbose default implementation + * and direct it to print all errors to the given file descriptor. + */ +FILE *file = fopen(...); +nl_cb_err(cb, NL_CB_VERBOSE, NULL, file); +-------- + +=== Overwriting of Internal Functions + +When the library needs to send or receive netlink messages in high level +interfaces it does so by calling its own low level API. In the case the +default characteristics are not sufficient for the application, it may +overwrite several internal function calls with own implementations. + +.Overwriting recvmsgs() + +See <> for more information on +how and when recvmsgs() is called internally. + +[source,c] +-------- +#include + +void nl_cb_overwrite_recvmsgs(struct nl_cb *cb, + int (*func)(struct nl_sock *sk, struct nl_cb *cb)); +-------- + +The following criterias must be met if a recvmsgs() implementation is +supposed to work with high level interfaces: + +- MUST respect the callback configuration +cb+, therefore: + - MUST call +NL_CB_VALID+ for all valid messages, passing on + - MUST call +NL_CB_ACK+ for all ACK messages + - MUST correctly handle multipart messages, calling NL_CB_VALID for + each message until a NLMSG_DONE message is received. +- MUST report error code if a NLMSG_ERROR or NLMSG_OVERRUN mesasge is + received. + +.Overwriting nl_recv() + +Often it is sufficient to overwrite `nl_recv()` which is responsible +from receiving the actual data from the socket instead of replacing +the complete `recvmsgs()` logic. + +See <> for more +information on how and when `nl_recv()` is called internally. + +[source,c] +-------- +#include + +void nl_cb_overwrite_recv(struct nl_cb *cb, + int (*func)(struct nl_sock * sk, + struct sockaddr_nl *addr, + unsigned char **buf, + struct ucred **cred)); +-------- + +The following criteras must be met for an own `nl_recv()` +implementation: + + - *MUST* return the number of bytes read or a negative error code if + an error occurred. The function may also return 0 to indicate that + no data has been read. + - *MUST* set `*buf` to a buffer containing the data read. It must be + safe for the caller to access the number of bytes read returned as + return code. + - *MAY* fill out `*addr` with the netlink address of the peer the + data has been received from. + - *MAY* set `*cred` to a newly allocated struct ucred containing + credentials. + +.Overwriting nl_send() + +See <> for more information on +how and when nl_send() is called internally. + +[source,c] +-------- +#include + +void nl_cb_overwrite_send(struct nl_cb *cb, int (*func)(struct nl_sock *sk, + struct nl_msg *msg)); +-------- + +Own implementations must send the netlink message and return 0 on success +or a negative error code. + +[[core_cache]] +== Cache System + +=== Allocation of Caches + +Almost all subsystem provide a function to allocate a new cache +of some form. The function usually looks like this: +[source,c] +-------- +struct nl_cache *_alloc_cache(struct nl_sock *sk); +-------- + +These functions allocate a new cache for the own object type, +initializes it properly and updates it to represent the current +state of their master, e.g. a link cache would include all +links currently configured in the kernel. + +Some of the allocation functions may take additional arguments +to further specify what will be part of the cache. + +All such functions return a newly allocated cache or NULL +in case of an error. + +=== Cache Manager + +The purpose of a cache manager is to keep track of caches and +automatically receive event notifications to keep the caches +up to date with the kernel state. Each manager has exactly one +netlink socket assigned which limits the scope of each manager +to exactly one netlink family. Therefore all caches committed +to a manager must be part of the same netlink family. Due to the +nature of a manager, it is not possible to have a cache maintain +two instances of the same cache type. The socket is subscribed +to the event notification group of each cache and also put into +non-blocking mode. Functions exist to poll() on the socket to +wait for new events to be received. + + +---- + App libnl Kernel + | | + +-----------------+ [ notification, link change ] + | | Cache Manager | | [ (IFF_UP | IFF_RUNNING) ] + | | | + | | +------------+| | | [ notification, new addr ] + <-------|---| route/link |<-------(async)--+ [ 10.0.1.1/32 dev eth1 ] + | | +------------+| | | + | +------------+| | + <---|---|---| route/addr |<------|-(async)--------------+ + | +------------+| + | | +------------+| | + <-------|---| ... || + | | +------------+| | + +-----------------+ + | | +---- + +.Creating a new cache manager + +[source,c] +---- +struct nl_cache_mngr *mngr; + +// Allocate a new cache manager for RTNETLINK and automatically +// provide the caches added to the manager. +err = nl_cache_mngr_alloc(NULL, NETLINK_ROUTE, NL_AUTO_PROVIDE, &mngr); +---- + +.Keep track of a cache + +[source,c] +---- +struct nl_cache *cache; + +// Create a new cache for links/interfaces and ask the manager to +// keep it up to date for us. This will trigger a full dump request +// to initially fill the cache. +cache = nl_cache_mngr_add(mngr, "route/link"); +----- + +.Make the manager receive updates + +[source,c] +---- +// Give the manager the ability to receive updates, will call poll() +// with a timeout of 5 seconds. +if (nl_cache_mngr_poll(mngr, 5000) > 0) { + // Manager received at least one update, dump cache? + nl_cache_dump(cache, ...); +} +---- + +.Release cache manager + +[source,c] +---- +nl_cache_mngr_free(mngr); +---- + +== Abstract Data Types + +A few high level abstract data types which are used by a majority netlink +protocols are implemented in the core library. More may be added in the +future if the need arises. + +=== Abstract Address + +Most netlink protocols deal with networking related topics and thus +dealing with network addresses is a common task. + +Currently the following address families are supported: + +[options="compact"] + * `AF_INET` + * `AF_INET6` + * `AF_LLC` + * `AF_DECnet` + * `AF_UNSPEC` + +[[core_addr_alloc]] +.Address Allocation + +The function nl_addr_alloc() allocates a new empty address. The ++maxsize+ argument defines the maximum length of an address in bytes. +The size of an address is address family specific. If the address +family and address data are known at allocation time the function +nl_addr_build() can be used alternatively. You may also clone +an address by calling nl_addr_clone() + +[source,c] +-------- +#include + +struct nl_addr *nl_addr_alloc(size_t maxsize); +struct nl_addr *nl_addr_clone(struct nl_addr *addr); +struct nl_addr *nl_addr_build(int family, void *addr, size_t size); +-------- + +If the address is transported in a netlink attribute, the function +nl_addr_alloc_attr() allocates a new address based on the payload +of the attribute provided. The +family+ argument is used to specify +the address family of the address, set to +AF_UNSPEC+ if unknown. + +[source,c] +-------- +#include + +struct nl_addr *nl_addr_alloc_attr(struct nlattr *attr, int family); +-------- + +If the address is provided by a user, it is usually stored in a human +readable format. The function nl_addr_parse() parses a character +string representing an address and allocates a new address based on +it. + +[source,c] +-------- +#include + +int nl_addr_parse(const char *addr, int hint, struct nl_addr **result); +-------- + +If parsing succeeds the function returns 0 and the allocated address +is stored in +*result+. + +NOTE: Make sure to return the reference to an address using + `nl_addr_put()` after usage to allow memory being freed. + +.Example: Transform character string to abstract address +[source,c] +----- +struct nl_addr *a = nl_addr_parse("::1", AF_UNSPEC); +printf("Address family: %s\n", nl_af2str(nl_addr_get_family(a))); +nl_addr_put(a); +a = nl_addr_parse("11:22:33:44:55:66", AF_UNSPEC); +printf("Address family: %s\n", nl_af2str(nl_addr_get_family(a))); +nl_addr_put(a); +----- + +.Address References + +Abstract addresses use reference counting to account for all users of +a particular address. After the last user has returned the reference +the address is freed. + +If you pass on an address object to another function and you are not +sure how long it will be used, make sure to call nl_addr_get() to +acquire an additional reference and have that function or code path +call nl_addr_put() as soon as it has finished using the address. + +[source,c] +-------- +#include + +struct nl_addr *nl_addr_get(struct nl_addr *addr); +void nl_addr_put(struct nl_addr *addr); +int nl_addr_shared(struct nl_addr *addr); +-------- + +You may call nl_addr_shared() at any time to check if you are the only +user of an address. + +.Address Attributes + +The address is usually set at allocation time. If it was unknown at that +time it can be specified later by calling nl_addr_set_family() and is +accessed with the function nl_addr_get_family(). + +[source,c] +-------- +#include + +void nl_addr_set_family(struct nl_addr *addr, int family); +int nl_addr_get_family(struct nl_addr *addr); +-------- + +The same is true for the actual address data. It is typically present +at allocation time. For exceptions it can be specified later or +overwritten with the function `nl_addr_set_binary_addr()`. Beware that +the length of the address may not exceed `maxlen` specified at +allocation time. The address data is returned by the function +`nl_addr_get_binary_addr()` and its length by the function +`nl_addr_get_len()`. + +[source,c] +-------- +#include + +int nl_addr_set_binary_addr(struct nl_addr *addr, void *data, size_t size); +void *nl_addr_get_binary_addr(struct nl_addr *addr); +unsigned int nl_addr_get_len(struct nl_addr *addr); +-------- + +If you only want to check if the address data consists of all zeros +the function `nl_addr_iszero()` is a shortcut to that. + +[source,c] +-------- +#include + +int nl_addr_iszero(struct nl_addr *addr); +-------- + +==== Address Prefix Length + +Although this functionality is somewhat specific to routing it has +been implemented here. Addresses can have a prefix length assigned +which implies that only the first n bits are of importance. This is +f.e. used to implement subnets. + +Use set functions `nl_addr_set_prefixlen()` and +`nl_addr_get_prefixlen()` to work with prefix lengths. + +[source,c] +-------- +#include + +void nl_addr_set_prefixlen(struct nl_addr *addr, int n); +unsigned int nl_addr_get_prefixlen(struct nl_addr *addr); +-------- + +NOTE: The default prefix length is set to (address length * 8) + +.Address Helpers + +Several functions exist to help when dealing with addresses. The +function `nl_addr_cmp()` compares two addresses and returns an integer +less than, equal to or greater than zero without considering the +prefix length at all. If you want to consider the prefix length, use +the function `nl_addr_cmp_prefix()`. + +[source,c] +-------- +#include + +int nl_addr_cmp(struct nl_addr *addr, struct nl_addr *addr); +int nl_addr_cmp_prefix(struct nl_addr *addr, struct nl_addr *addr); +-------- + +If an abstract address needs to presented to the user it should be +done in a human readable format which differs depending on the address +family. The function `nl_addr2str()` takes care of this by calling the +appropriate conversion functions internally. It expects a `buf` of +length `size` to write the character string into and returns a pointer +to `buf` for easy `printf()` usage. + +[source,c] +-------- +#include + +char *nl_addr2str(struct nl_addr *addr, char *buf, size_t size); +-------- + +If the address family is unknown, the address data will be printed in +hexadecimal format `AA:BB:CC:DD:...` + +Often the only way to figure out the address family is by looking at +the length of the address. The function `nl_addr_guess_family()` does +just this and returns the address family guessed based on the address +size. + +[source,c] +-------- +#include + +int nl_addr_guess_family(struct nl_addr *addr); +-------- + +Before allocating an address you may want to check if the character +string actually represents a valid address of the address family you +are expecting. The function `nl_addr_valid()` can be used for that, it +returns 1 if the supplied `addr` is a valid address in the context of +`family`. See `inet_pton(3)`, `dnet_pton(3)` for more information on +valid address formats. + +[source,c] +-------- +#include + +int nl_addr_valid(char *addr, int family); +-------- + +=== Abstract Data + +The abstract data type is a trivial datatype with the primary purpose +to simplify usage of netlink attributes of arbitrary length. + +[[core_data_alloc]] +.Allocation of a Data Object +The function `nl_data_alloc()` alloctes a new abstract data object and +fills it with the provided data. `nl_data_alloc_attr()` does the same +but bases the data on the payload of a netlink attribute. New data +objects can also be allocated by cloning existing ones by using +`nl_data_clone()`. + +[source,c] +-------- +struct nl_data *nl_data_alloc(void *buf, size_t size); +struct nl_data *nl_data_alloc_attr(struct nlattr *attr); +struct nl_data *nl_data_clone(struct nl_data *data); +void nl_data_free(struct nl_data *data); +-------- + +.Access to Data + +The function `nl_data_get()` returns a pointer to the data, the size +of data is returned by `nl_data_get_size()`. + +[source,c] +-------- +void *nl_data_get(struct nl_data *data); +size_t nl_data_get_size(struct nl_data *data); +-------- + +.Data Helpers + +The function nl_data_append() reallocates the internal data buffers +and appends the specified `buf` to the existing data. + +[source,c] +-------- +int nl_data_append(struct nl_data *data, void *buf, size_t size); +-------- + +CAUTION: Any call to `nl_data_append()` invalidates all pointers + returned by `nl_data_get()` of the same data object. + +[source,c] +-------- +int nl_data_cmp(struct nl_data *data, struct nl_data *data); +-------- diff --git a/libnl/doc/doxygen-link.py b/libnl/doc/doxygen-link.py new file mode 100755 index 0000000..1f424fc --- /dev/null +++ b/libnl/doc/doxygen-link.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +from __future__ import print_function +import re +import sys + + +rc_script = re.compile(r"\s*(.*\S)?\s*") + + +def parse_dict(filename): + links = {} + for line in open(filename, "r"): + m = re.match("^([^=]+)=([^\n]+)$", line) + if not m: + continue + name = m.group(1) + value = m.group(2) + + # strip leading and trailing whitespace + m = rc_script.match(name) + if m: + name = m.group(1) + + # skip special names + if name == "": + continue + if name == "\\": + continue + + links[name] = '' + name + "" + return links + + +links = parse_dict(sys.argv[1]) + + +def translate(match): + return links[match.group(1)] + + +# match for all names, with word boundaries \b +rc = re.compile(r"\b(" + "|".join(map(re.escape, sorted(links, reverse=True))) + r")\b") + +for line in open(sys.argv[2], "r"): + if links: + line = rc.sub(translate, line) + print(line, end="") diff --git a/libnl/doc/gen-tags.sh b/libnl/doc/gen-tags.sh new file mode 100755 index 0000000..862ec09 --- /dev/null +++ b/libnl/doc/gen-tags.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# +# Based on a script found on the englinemtn-devel mailinglist +# written by Carsten Haitzler +# + +for f in api/group__*.html +do + bf=$(basename $f) + + grep -oE "href=\"$bf#[a-z0-9]+\">[^<]+" $f | + sed 's/href="\([^"]*\)">\([^<]*\)<\/a>/\2=api\/\1/' +done diff --git a/libnl/doc/images/addressing.png b/libnl/doc/images/addressing.png new file mode 100644 index 0000000..9dcaaff Binary files /dev/null and b/libnl/doc/images/addressing.png differ diff --git a/libnl/doc/images/attribute_hdr.png b/libnl/doc/images/attribute_hdr.png new file mode 100644 index 0000000..0e6cfda Binary files /dev/null and b/libnl/doc/images/attribute_hdr.png differ diff --git a/libnl/doc/images/classful_qdisc.png b/libnl/doc/images/classful_qdisc.png new file mode 100644 index 0000000..7e77350 Binary files /dev/null and b/libnl/doc/images/classful_qdisc.png differ diff --git a/libnl/doc/images/classless_qdisc.png b/libnl/doc/images/classless_qdisc.png new file mode 100644 index 0000000..bcf2c1c Binary files /dev/null and b/libnl/doc/images/classless_qdisc.png differ diff --git a/libnl/doc/images/classless_qdisc_nbands.png b/libnl/doc/images/classless_qdisc_nbands.png new file mode 100644 index 0000000..14cb026 Binary files /dev/null and b/libnl/doc/images/classless_qdisc_nbands.png differ diff --git a/libnl/doc/images/icons/README b/libnl/doc/images/icons/README new file mode 100644 index 0000000..f12b2a7 --- /dev/null +++ b/libnl/doc/images/icons/README @@ -0,0 +1,5 @@ +Replaced the plain DocBook XSL admonition icons with Jimmac's DocBook +icons (http://jimmac.musichall.cz/ikony.php3). I dropped transparency +from the Jimmac icons to get round MS IE and FOP PNG incompatibilies. + +Stuart Rackham diff --git a/libnl/doc/images/icons/callouts/1.png b/libnl/doc/images/icons/callouts/1.png new file mode 100644 index 0000000..7d47343 Binary files /dev/null and b/libnl/doc/images/icons/callouts/1.png differ diff --git a/libnl/doc/images/icons/callouts/10.png b/libnl/doc/images/icons/callouts/10.png new file mode 100644 index 0000000..997bbc8 Binary files /dev/null and b/libnl/doc/images/icons/callouts/10.png differ diff --git a/libnl/doc/images/icons/callouts/11.png b/libnl/doc/images/icons/callouts/11.png new file mode 100644 index 0000000..ce47dac Binary files /dev/null and b/libnl/doc/images/icons/callouts/11.png differ diff --git a/libnl/doc/images/icons/callouts/12.png b/libnl/doc/images/icons/callouts/12.png new file mode 100644 index 0000000..31daf4e Binary files /dev/null and b/libnl/doc/images/icons/callouts/12.png differ diff --git a/libnl/doc/images/icons/callouts/13.png b/libnl/doc/images/icons/callouts/13.png new file mode 100644 index 0000000..14021a8 Binary files /dev/null and b/libnl/doc/images/icons/callouts/13.png differ diff --git a/libnl/doc/images/icons/callouts/14.png b/libnl/doc/images/icons/callouts/14.png new file mode 100644 index 0000000..64014b7 Binary files /dev/null and b/libnl/doc/images/icons/callouts/14.png differ diff --git a/libnl/doc/images/icons/callouts/15.png b/libnl/doc/images/icons/callouts/15.png new file mode 100644 index 0000000..0d65765 Binary files /dev/null and b/libnl/doc/images/icons/callouts/15.png differ diff --git a/libnl/doc/images/icons/callouts/2.png b/libnl/doc/images/icons/callouts/2.png new file mode 100644 index 0000000..5d09341 Binary files /dev/null and b/libnl/doc/images/icons/callouts/2.png differ diff --git a/libnl/doc/images/icons/callouts/3.png b/libnl/doc/images/icons/callouts/3.png new file mode 100644 index 0000000..ef7b700 Binary files /dev/null and b/libnl/doc/images/icons/callouts/3.png differ diff --git a/libnl/doc/images/icons/callouts/4.png b/libnl/doc/images/icons/callouts/4.png new file mode 100644 index 0000000..adb8364 Binary files /dev/null and b/libnl/doc/images/icons/callouts/4.png differ diff --git a/libnl/doc/images/icons/callouts/5.png b/libnl/doc/images/icons/callouts/5.png new file mode 100644 index 0000000..4d7eb46 Binary files /dev/null and b/libnl/doc/images/icons/callouts/5.png differ diff --git a/libnl/doc/images/icons/callouts/6.png b/libnl/doc/images/icons/callouts/6.png new file mode 100644 index 0000000..0ba694a Binary files /dev/null and b/libnl/doc/images/icons/callouts/6.png differ diff --git a/libnl/doc/images/icons/callouts/7.png b/libnl/doc/images/icons/callouts/7.png new file mode 100644 index 0000000..472e96f Binary files /dev/null and b/libnl/doc/images/icons/callouts/7.png differ diff --git a/libnl/doc/images/icons/callouts/8.png b/libnl/doc/images/icons/callouts/8.png new file mode 100644 index 0000000..5e60973 Binary files /dev/null and b/libnl/doc/images/icons/callouts/8.png differ diff --git a/libnl/doc/images/icons/callouts/9.png b/libnl/doc/images/icons/callouts/9.png new file mode 100644 index 0000000..a0676d2 Binary files /dev/null and b/libnl/doc/images/icons/callouts/9.png differ diff --git a/libnl/doc/images/icons/caution.png b/libnl/doc/images/icons/caution.png new file mode 100644 index 0000000..9a8c515 Binary files /dev/null and b/libnl/doc/images/icons/caution.png differ diff --git a/libnl/doc/images/icons/example.png b/libnl/doc/images/icons/example.png new file mode 100644 index 0000000..1199e86 Binary files /dev/null and b/libnl/doc/images/icons/example.png differ diff --git a/libnl/doc/images/icons/home.png b/libnl/doc/images/icons/home.png new file mode 100644 index 0000000..37a5231 Binary files /dev/null and b/libnl/doc/images/icons/home.png differ diff --git a/libnl/doc/images/icons/important.png b/libnl/doc/images/icons/important.png new file mode 100644 index 0000000..be685cc Binary files /dev/null and b/libnl/doc/images/icons/important.png differ diff --git a/libnl/doc/images/icons/next.png b/libnl/doc/images/icons/next.png new file mode 100644 index 0000000..64e126b Binary files /dev/null and b/libnl/doc/images/icons/next.png differ diff --git a/libnl/doc/images/icons/note.png b/libnl/doc/images/icons/note.png new file mode 100644 index 0000000..7c1f3e2 Binary files /dev/null and b/libnl/doc/images/icons/note.png differ diff --git a/libnl/doc/images/icons/prev.png b/libnl/doc/images/icons/prev.png new file mode 100644 index 0000000..3e8f12f Binary files /dev/null and b/libnl/doc/images/icons/prev.png differ diff --git a/libnl/doc/images/icons/tip.png b/libnl/doc/images/icons/tip.png new file mode 100644 index 0000000..f087c73 Binary files /dev/null and b/libnl/doc/images/icons/tip.png differ diff --git a/libnl/doc/images/icons/up.png b/libnl/doc/images/icons/up.png new file mode 100644 index 0000000..2db1ce6 Binary files /dev/null and b/libnl/doc/images/icons/up.png differ diff --git a/libnl/doc/images/icons/warning.png b/libnl/doc/images/icons/warning.png new file mode 100644 index 0000000..d41edb9 Binary files /dev/null and b/libnl/doc/images/icons/warning.png differ diff --git a/libnl/doc/images/ifinfomsg.png b/libnl/doc/images/ifinfomsg.png new file mode 100644 index 0000000..fb94cf7 Binary files /dev/null and b/libnl/doc/images/ifinfomsg.png differ diff --git a/libnl/doc/images/library_overview.png b/libnl/doc/images/library_overview.png new file mode 100644 index 0000000..dd9d5fe Binary files /dev/null and b/libnl/doc/images/library_overview.png differ diff --git a/libnl/doc/images/nlmsgerr.png b/libnl/doc/images/nlmsgerr.png new file mode 100644 index 0000000..58e53d5 Binary files /dev/null and b/libnl/doc/images/nlmsgerr.png differ diff --git a/libnl/doc/images/nlmsghdr.png b/libnl/doc/images/nlmsghdr.png new file mode 100644 index 0000000..dd39b9c Binary files /dev/null and b/libnl/doc/images/nlmsghdr.png differ diff --git a/libnl/doc/images/qdisc_default.png b/libnl/doc/images/qdisc_default.png new file mode 100644 index 0000000..a7ba167 Binary files /dev/null and b/libnl/doc/images/qdisc_default.png differ diff --git a/libnl/doc/images/qdisc_mq.png b/libnl/doc/images/qdisc_mq.png new file mode 100644 index 0000000..c6318b2 Binary files /dev/null and b/libnl/doc/images/qdisc_mq.png differ diff --git a/libnl/doc/images/tc_obj.png b/libnl/doc/images/tc_obj.png new file mode 100644 index 0000000..bfc8145 Binary files /dev/null and b/libnl/doc/images/tc_obj.png differ diff --git a/libnl/doc/images/tc_overview.png b/libnl/doc/images/tc_overview.png new file mode 100644 index 0000000..ce23e67 Binary files /dev/null and b/libnl/doc/images/tc_overview.png differ diff --git a/libnl/doc/index.txt b/libnl/doc/index.txt new file mode 100644 index 0000000..c207b44 --- /dev/null +++ b/libnl/doc/index.txt @@ -0,0 +1,22 @@ +Documentation Overview - libnl Suite +==================================== + +== Libraries + +image:library_overview.png["Library Hierarchy"] + +link:core.html[Netlink Library] (libnl):: +Socket handling, sending and receiving, message construction and parsing, ... + +link:route.html[Routing Family Library] (libnl-route):: +Adresses, links, neighbours, routing, traffic control, neighbour tables, ... + +Netfilter Library (libnl-nf):: +Connection tracking, logging, queueing + +Generic Netlink Library (libnl-genl):: +Controller API, family and command registration + +== Python Packages + - netlink.core + - netlink.route.link diff --git a/libnl/doc/libnl.css b/libnl/doc/libnl.css new file mode 100644 index 0000000..8589450 --- /dev/null +++ b/libnl/doc/libnl.css @@ -0,0 +1,1156 @@ +/* The standard CSS for doxygen */ + +body, table, div, p, dl { + font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif; + font-size: 13px; + line-height: 1.3; +} + +/* @group Heading Levels */ + +h1 { + font-size: 150%; +} + +.title { + font-size: 150%; + font-weight: bold; + margin: 10px 2px; +} + +h2 { + font-size: 120%; +} + +h3 { + font-size: 100%; +} + +h1, h2, h3, h4, h5, h6 { + -webkit-transition: text-shadow 0.5s linear; + -moz-transition: text-shadow 0.5s linear; + -ms-transition: text-shadow 0.5s linear; + -o-transition: text-shadow 0.5s linear; + transition: text-shadow 0.5s linear; + margin-right: 15px; +} + +h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { + text-shadow: 0 0 15px cyan; +} + +dt { + font-weight: bold; +} + +div.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; +} + +p.startli, p.startdd, p.starttd { + margin-top: 2px; +} + +p.endli { + margin-bottom: 0px; +} + +p.enddd { + margin-bottom: 4px; +} + +p.endtd { + margin-bottom: 2px; +} + +/* @end */ + +caption { + font-weight: bold; +} + +span.legend { + font-size: 70%; + text-align: center; +} + +h3.version { + font-size: 90%; + text-align: center; +} + +div.qindex, div.navtab{ + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; +} + +div.qindex, div.navpath { + width: 100%; + line-height: 140%; +} + +div.navtab { + margin-right: 15px; +} + +/* @group Link Styling */ + +a { + color: #3D578C; + font-weight: normal; + text-decoration: none; +} + +.contents a:visited { + color: #4665A2; +} + +a:hover { + text-decoration: underline; +} + +a.qindex { + font-weight: bold; +} + +a.qindexHL { + font-weight: bold; + background-color: #9CAFD4; + color: #ffffff; + border: 1px double #869DCA; +} + +.contents a.qindexHL:visited { + color: #ffffff; +} + +a.el { + font-weight: bold; +} + +a.elRef { +} + +a.code, a.code:visited { + color: #4665A2; +} + +a.codeRef, a.codeRef:visited { + color: #4665A2; +} + +/* @end */ + +dl.el { + margin-left: -1cm; +} + +pre.fragment { + border: 1px solid #C4CFE5; + background-color: #FBFCFD; + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + font-size: 9pt; + line-height: 125%; + font-family: monospace, fixed; + font-size: 105%; +} + +div.fragment { + padding: 4px; + margin: 4px; + background-color: #FBFCFD; + border: 1px solid #C4CFE5; +} + +div.line { + font-family: monospace, fixed; + font-size: 13px; + min-height: 13px; + line-height: 1.0; + text-wrap: unrestricted; + white-space: -moz-pre-wrap; /* Moz */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + white-space: pre-wrap; /* CSS3 */ + word-wrap: break-word; /* IE 5.5+ */ + text-indent: -53px; + padding-left: 53px; + padding-bottom: 0px; + margin: 0px; + -webkit-transition-property: background-color, box-shadow; + -webkit-transition-duration: 0.5s; + -moz-transition-property: background-color, box-shadow; + -moz-transition-duration: 0.5s; + -ms-transition-property: background-color, box-shadow; + -ms-transition-duration: 0.5s; + -o-transition-property: background-color, box-shadow; + -o-transition-duration: 0.5s; + transition-property: background-color, box-shadow; + transition-duration: 0.5s; +} + +div.line.glow { + background-color: cyan; + box-shadow: 0 0 10px cyan; +} + + +span.lineno { + padding-right: 4px; + text-align: right; + border-right: 2px solid #0F0; + background-color: #E8E8E8; + white-space: pre; +} +span.lineno a { + background-color: #D8D8D8; +} + +span.lineno a:hover { + background-color: #C8C8C8; +} + +div.ah { + background-color: black; + font-weight: bold; + color: #ffffff; + margin-bottom: 3px; + margin-top: 3px; + padding: 0.2em; + border: solid thin #333; + border-radius: 0.5em; + -webkit-border-radius: .5em; + -moz-border-radius: .5em; + box-shadow: 2px 2px 3px #999; + -webkit-box-shadow: 2px 2px 3px #999; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); + background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); +} + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; +} + +div.groupText { + margin-left: 16px; + font-style: italic; +} + +body { + background-color: white; + color: black; + margin: 0; +} + +div.contents { + margin-top: 10px; + margin-left: 12px; + margin-right: 8px; +} + +td.indexkey { + background-color: #EBEFF6; + font-weight: bold; + border: 1px solid #C4CFE5; + margin: 2px 0px 2px 0; + padding: 2px 10px; + white-space: nowrap; + vertical-align: top; +} + +td.indexvalue { + background-color: #EBEFF6; + border: 1px solid #C4CFE5; + padding: 2px 10px; + margin: 2px 0px; +} + +tr.memlist { + background-color: #EEF1F7; +} + +p.formulaDsp { + text-align: center; +} + +img.formulaDsp { + +} + +img.formulaInl { + vertical-align: middle; +} + +div.center { + text-align: center; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; +} + +div.center img { + border: 0px; +} + +address.footer { + text-align: right; + padding-right: 12px; +} + +img.footer { + border: 0px; + vertical-align: middle; +} + +/* @group Code Colorization */ + +span.keyword { + color: #008000 +} + +span.keywordtype { + color: #604020 +} + +span.keywordflow { + color: #e08000 +} + +span.comment { + color: #800000 +} + +span.preprocessor { + color: #806020 +} + +span.stringliteral { + color: #002080 +} + +span.charliteral { + color: #008080 +} + +span.vhdldigit { + color: #ff00ff +} + +span.vhdlchar { + color: #000000 +} + +span.vhdlkeyword { + color: #700070 +} + +span.vhdllogic { + color: #ff0000 +} + +blockquote { + background-color: #F7F8FB; + border-left: 2px solid #9CAFD4; + margin: 0 24px 0 4px; + padding: 0 12px 0 16px; +} + +/* @end */ + +/* +.search { + color: #003399; + font-weight: bold; +} + +form.search { + margin-bottom: 0px; + margin-top: 0px; +} + +input.search { + font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #e8eef2; +} +*/ + +td.tiny { + font-size: 75%; +} + +.dirtab { + padding: 4px; + border-collapse: collapse; + border: 1px solid #A3B4D7; +} + +th.dirtab { + background: #EBEFF6; + font-weight: bold; +} + +hr { + height: 0px; + border: none; + border-top: 1px solid #4A6AAA; +} + +hr.footer { + height: 1px; +} + +/* @group Member Descriptions */ + +table.memberdecls { + border-spacing: 0px; + padding: 0px; +} + +.memberdecls td { + -webkit-transition-property: background-color, box-shadow; + -webkit-transition-duration: 0.5s; + -moz-transition-property: background-color, box-shadow; + -moz-transition-duration: 0.5s; + -ms-transition-property: background-color, box-shadow; + -ms-transition-duration: 0.5s; + -o-transition-property: background-color, box-shadow; + -o-transition-duration: 0.5s; + transition-property: background-color, box-shadow; + transition-duration: 0.5s; +} + +.memberdecls td.glow { + background-color: cyan; + box-shadow: 0 0 15px cyan; +} + +.mdescLeft, .mdescRight, +.memItemLeft, .memItemRight, +.memTemplItemLeft, .memTemplItemRight, .memTemplParams { + background-color: #F9FAFC; + border: none; + margin: 4px; + padding: 1px 0 0 8px; +} + +.mdescLeft, .mdescRight { + padding: 0px 8px 4px 8px; + color: #555; +} + +.memItemLeft, .memItemRight, .memTemplParams { + border-top: 1px solid #C4CFE5; +} + +.memItemLeft, .memTemplItemLeft { + white-space: nowrap; +} + +.memItemRight { + width: 100%; +} + +.memTemplParams { + color: #4665A2; + white-space: nowrap; +} + +/* @end */ + +/* @group Member Details */ + +/* Styles for detailed member documentation */ + +.memtemplate { + font-size: 80%; + color: #4665A2; + font-weight: normal; + margin-left: 9px; +} + +.memnav { + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} + +.mempage { + width: 100%; +} + +.memitem { + padding: 0; + margin-bottom: 10px; + margin-right: 5px; + -webkit-transition: box-shadow 0.5s linear; + -moz-transition: box-shadow 0.5s linear; + -ms-transition: box-shadow 0.5s linear; + -o-transition: box-shadow 0.5s linear; + transition: box-shadow 0.5s linear; + display: table !important; + width: 100%; +} + +.memitem.glow { + box-shadow: 0 0 15px cyan; +} + +.memname { + font-weight: bold; + margin-left: 6px; +} + +.memname td { + vertical-align: bottom; +} + +.memproto, dl.reflist dt { + border-top: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 6px 0px 6px 0px; + color: #253555; + font-weight: bold; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + background-image:url('nav_f.png'); + background-repeat:repeat-x; + background-color: #E2E8F2; + /* opera specific markup */ + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + border-top-right-radius: 4px; + border-top-left-radius: 4px; + /* firefox specific markup */ + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + -moz-border-radius-topright: 4px; + -moz-border-radius-topleft: 4px; + /* webkit specific markup */ + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + -webkit-border-top-right-radius: 4px; + -webkit-border-top-left-radius: 4px; + +} + +.memdoc, dl.reflist dd { + border-bottom: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 6px 10px 2px 10px; + background-color: #FBFCFD; + border-top-width: 0; + background-image:url('nav_g.png'); + background-repeat:repeat-x; + background-color: #FFFFFF; + /* opera specific markup */ + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + /* firefox specific markup */ + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-bottomright: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + /* webkit specific markup */ + -webkit-border-bottom-left-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); +} + +dl.reflist dt { + padding: 5px; +} + +dl.reflist dd { + margin: 0px 0px 10px 0px; + padding: 5px; +} + +.paramkey { + text-align: right; +} + +.paramtype { + white-space: nowrap; +} + +.paramname { + color: #602020; + white-space: nowrap; +} +.paramname em { + font-style: normal; +} +.paramname code { + line-height: 14px; +} + +.params, .retval, .exception, .tparams { + margin-left: 0px; + padding-left: 0px; +} + +.params .paramname, .retval .paramname { + font-weight: bold; + vertical-align: top; +} + +.params .paramtype { + font-style: italic; + vertical-align: top; +} + +.params .paramdir { + font-family: "courier new",courier,monospace; + vertical-align: top; +} + +table.mlabels { + border-spacing: 0px; +} + +td.mlabels-left { + width: 100%; + padding: 0px; +} + +td.mlabels-right { + vertical-align: bottom; + padding: 0px; + white-space: nowrap; +} + +span.mlabels { + margin-left: 8px; +} + +span.mlabel { + background-color: #728DC1; + border-top:1px solid #5373B4; + border-left:1px solid #5373B4; + border-right:1px solid #C4CFE5; + border-bottom:1px solid #C4CFE5; + text-shadow: none; + color: white; + margin-right: 4px; + padding: 2px 3px; + border-radius: 3px; + font-size: 7pt; + white-space: nowrap; +} + + + +/* @end */ + +/* these are for tree view when not used as main index */ + +div.directory { + margin: 10px 0px; + border-top: 1px solid #A8B8D9; + border-bottom: 1px solid #A8B8D9; + width: 100%; +} + +.directory table { + border-collapse:collapse; +} + +.directory td { + margin: 0px; + padding: 0px; + vertical-align: top; +} + +.directory td.entry { + white-space: nowrap; + padding-right: 6px; +} + +.directory td.entry a { + outline:none; +} + +.directory td.entry a img { + border: none; +} + +.directory td.desc { + width: 100%; + padding-left: 6px; + padding-right: 6px; + border-left: 1px solid rgba(0,0,0,0.05); +} + +.directory tr.even { + padding-left: 6px; + background-color: #F7F8FB; +} + +.directory img { + vertical-align: -30%; +} + +.directory .levels { + white-space: nowrap; + width: 100%; + text-align: right; + font-size: 9pt; +} + +.directory .levels span { + cursor: pointer; + padding-left: 2px; + padding-right: 2px; + color: #3D578C; +} + +div.dynheader { + margin-top: 8px; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +address { + font-style: normal; + color: #2A3D61; +} + +table.doxtable { + border-collapse:collapse; + margin-top: 4px; + margin-bottom: 4px; +} + +table.doxtable td, table.doxtable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; +} + +table.doxtable th { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; +} + +table.fieldtable { + width: 100%; + margin-bottom: 10px; + border: 1px solid #A8B8D9; + border-spacing: 0px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); +} + +.fieldtable td, .fieldtable th { + padding: 3px 7px 2px; +} + +.fieldtable td.fieldtype, .fieldtable td.fieldname { + white-space: nowrap; + border-right: 1px solid #A8B8D9; + border-bottom: 1px solid #A8B8D9; + vertical-align: top; +} + +.fieldtable td.fielddoc { + border-bottom: 1px solid #A8B8D9; + width: 100%; +} + +.fieldtable tr:last-child td { + border-bottom: none; +} + +.fieldtable th { + background-image:url('nav_f.png'); + background-repeat:repeat-x; + background-color: #E2E8F2; + font-size: 90%; + color: #253555; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; + -moz-border-radius-topleft: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: 1px solid #A8B8D9; +} + + +.tabsearch { + top: 0px; + left: 10px; + height: 36px; + background-image: url('tab_b.png'); + z-index: 101; + overflow: hidden; + font-size: 13px; +} + +.navpath ul +{ + font-size: 11px; + background-image:url('tab_b.png'); + background-repeat:repeat-x; + height:30px; + line-height:30px; + color:#8AA0CC; + border:solid 1px #C2CDE4; + overflow:hidden; + margin:0px; + padding:0px; +} + +.navpath li +{ + list-style-type:none; + float:left; + padding-left:10px; + padding-right:15px; + background-image:url('bc_s.png'); + background-repeat:no-repeat; + background-position:right; + color:#364D7C; +} + +.navpath li.navelem a +{ + height:32px; + display:block; + text-decoration: none; + outline: none; +} + +.navpath li.navelem a:hover +{ + color:#6884BD; +} + +.navpath li.footer +{ + list-style-type:none; + float:right; + padding-left:10px; + padding-right:15px; + background-image:none; + background-repeat:no-repeat; + background-position:right; + color:#364D7C; + font-size: 8pt; +} + + +div.summary +{ + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; +} + +div.summary a +{ + white-space: nowrap; +} + +div.ingroups +{ + font-size: 8pt; + width: 50%; + text-align: left; +} + +div.ingroups a +{ + white-space: nowrap; +} + +div.header +{ + background-image:url('nav_h.png'); + background-repeat:repeat-x; + background-color: #F9FAFC; + margin: 0px; + border-bottom: 1px solid #C4CFE5; +} + +div.headertitle +{ + padding: 5px 5px 5px 7px; +} + +dl +{ + padding: 0 0 0 10px; +} + +/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug */ +dl.section +{ + margin-left: 0px; + padding-left: 0px; +} + +dl.note +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #D0C000; +} + +dl.warning, dl.attention +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #FF0000; +} + +dl.pre, dl.post, dl.invariant +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #00D000; +} + +dl.deprecated +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #505050; +} + +dl.todo +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #00C0E0; +} + +dl.test +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #3030E0; +} + +dl.bug +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #C08050; +} + +dl.section dd { + margin-bottom: 6px; +} + + +#projectlogo +{ + text-align: center; + vertical-align: bottom; + border-collapse: separate; +} + +#projectlogo img +{ + border: 0px none; +} + +#projectname +{ + font: 300% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 2px 0px; +} + +#projectbrief +{ + font: 120% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#projectnumber +{ + font: 50% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#titlearea +{ + padding: 0px; + margin: 0px; + width: 100%; + border-bottom: 1px solid #5373B4; +} + +.image +{ + text-align: center; +} + +.dotgraph +{ + text-align: center; +} + +.mscgraph +{ + text-align: center; +} + +.caption +{ + font-weight: bold; +} + +div.zoom +{ + border: 1px solid #90A5CE; +} + +dl.citelist { + margin-bottom:50px; +} + +dl.citelist dt { + color:#334975; + float:left; + font-weight:bold; + margin-right:10px; + padding:5px; +} + +dl.citelist dd { + margin:2px 0; + padding:5px 0; +} + +div.toc { + padding: 14px 25px; + background-color: #F4F6FA; + border: 1px solid #D8DFEE; + border-radius: 7px 7px 7px 7px; + float: right; + height: auto; + margin: 0 20px 10px 10px; + width: 200px; +} + +div.toc li { + background: url("bdwn.png") no-repeat scroll 0 5px transparent; + font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif; + margin-top: 5px; + padding-left: 10px; + padding-top: 2px; +} + +div.toc h3 { + font: bold 12px/1.2 Arial,FreeSans,sans-serif; + color: #4665A2; + border-bottom: 0 none; + margin: 0; +} + +div.toc ul { + list-style: none outside none; + border: medium none; + padding: 0px; +} + +div.toc li.level1 { + margin-left: 0px; +} + +div.toc li.level2 { + margin-left: 15px; +} + +div.toc li.level3 { + margin-left: 30px; +} + +div.toc li.level4 { + margin-left: 45px; +} + +.inherit_header { + font-weight: bold; + color: gray; + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.inherit_header td { + padding: 6px 0px 2px 5px; +} + +.inherit { + display: none; +} + +tr.heading h2 { + margin-top: 12px; + margin-bottom: 4px; +} + +@media print +{ + #top { display: none; } + #side-nav { display: none; } + #nav-path { display: none; } + body { overflow:visible; } + h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } + .summary { display: none; } + .memitem { page-break-inside: avoid; } + #doc-content + { + margin-left:0 !important; + height:auto !important; + width:auto !important; + overflow:inherit; + display:inline; + } +} + diff --git a/libnl/doc/resolve-asciidoc-refs.py b/libnl/doc/resolve-asciidoc-refs.py new file mode 100755 index 0000000..d661c56 --- /dev/null +++ b/libnl/doc/resolve-asciidoc-refs.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +from __future__ import print_function + +import re +import sys + +refs = {} +complete_file = "" + +for line in open(sys.argv[1], "r"): + complete_file += line + +for m in re.findall("\\[\\[(.+)\\]\\]\n=+ ([^\n]+)", complete_file): + ref, title = m + refs["<<" + ref + ">>"] = "<<" + ref + ", " + title + ">>" + + +def translate(match): + try: + return refs[match.group(0)] + except KeyError: + return "" + + +rc = re.compile("|".join(map(re.escape, sorted(refs, reverse=True)))) +for line in open(sys.argv[1], "r"): + print(rc.sub(translate, line), end="") diff --git a/libnl/doc/route.txt b/libnl/doc/route.txt new file mode 100644 index 0000000..5fe049a --- /dev/null +++ b/libnl/doc/route.txt @@ -0,0 +1,2134 @@ +//// + vim.syntax: asciidoc + + Copyright (c) 2011 Thomas Graf +//// + +Routing Family Netlink Library (libnl-route) +============================================ +Thomas Graf +3.1, Aug 11 2011: + +== Introduction + +This library provides APIs to the kernel interfaces of the routing family. + + +NOTE: Work in progress. + +== Addresses + +[[route_link]] +== Links (Network Devices) + +The link configuration interface is part of the +NETLINK_ROUTE+ protocol +family and implements the following netlink message types: + +- View and modify the configuration of physical and virtual network devices. +- Create and delete virtual network devices (e.g. dummy devices, VLAN devices, + tun devices, bridging devices, ...) +- View and modify per link network configuration settings (e.g. + +net.ipv6.conf.eth0.accept_ra+, +net.ipv4.conf.eth1.forwarding+, ...) + +.Naming Convention (network device, link, interface) + +In networking several terms are commonly used to refer to network devices. +While they have distinct meanings they have been used interchangeably in +the past. Within the Linux kernel, the term _network device_ or _netdev_ is +commonly used In user space the term _network interface_ is very common. +The routing netlink protocol uses the term _link_ and so does the _iproute2_ +utility and most routing daemons. + +=== Netlink Protocol + +This section describes the protocol semantics of the netlink based link +configuration interface. The following messages are defined: + +[options="header", cols="1,2,2"] +|============================================================================== +| Message Type | User -> Kernel | Kernel -> User +| +RTM_NEWLINK+ | Create or update virtual network device +| Reply to +RTM_GETLINK+ request or notification of link added or updated +| +RTM_DELLINK+ | Delete virtual network device +| Notification of link deleted or disappeared +| +RTM_GETLINK+ | Retrieve link configuration and statistics | +| +RTM_SETLINK+ | Modify link configuration | +|============================================================================== + +See link:core.html#core_msg_types[Netlink Library - Message Types] for more +information on common semantics of these message types. + +==== Link Message Format + +All netlink link messages share a common header (+struct ifinfomsg+) which +is appended after the netlink header (+struct nlmsghdr+). + +image:ifinfomsg.png["Link Message Header"] + +The meaning of each field may differ depending on the message type. A ++struct ifinfomsg+ is defined in ++ to represent the +header. + +Address Family (8bit):: +The address family is usually set to +AF_UNSPEC+ but may be specified in ++RTM_GETLINK+ requests to limit the returned links to a specific address +family. + +Link Layer Type (16bit):: +Currently only used in kernel->user messages to report the link layer type +of a link. The value corresponds to the +ARPHRD_*+ defines found in +++. Translation from/to strings can be done using the +functions nl_llproto2str()/nl_str2llproto(). + +Link Index (32bit):: +Carries the interface index and is used to identify existing links. + +Flags (32bit):: +In kernel->user messages the value of this field represents the current +state of the link flags. In user->kernel messages this field is used to +change flags or set the initial flag state of new links. Note that in order +to change a flag, the flag must also be set in the _Flags Change Mask_ field. + +Flags Change Mask (32bit):: +The primary use of this field is to specify a mask of flags that should be +changed based on the value of the _Flags_ field. A special meaning is given +to this field when present in link notifications, see TODO. + +Attributes (variable):: +All link message types may carry netlink attributes. They are defined in the +header file and share the prefix +IFLA_+. + +==== Link Message Types + +.RTM_GETLINK (user->kernel) + +Lookup link by 1. interface index or 2. link name (+IFLA_IFNAME+) and return +a single +RTM_NEWLINK+ message containing the link configuration and statistics +or a netlink error message if no such link was found. + +*Parameters:* + +* *Address family* +** If the address family is set to +PF_BRIDGE+, only bridging devices will be + returned. +** If the address family is set to +PF_INET6+, only ipv6 enabled devices will + be returned. + +*Flags:* + +* +NLM_F_DUMP+ If set, all links will be returned in form of a multipart + message. + +*Returns:* + +* +EINVAL+ if neither interface nor link name are set +* +ENODEV+ if no link was found +* +ENOBUFS+ if allocation failed + +.RTM_NEWLINK (user->kernel) + +Creates a new or updates an existing link. Only virtual links may be created +but all links may be updated. + +*Flags:* + +- +NLM_F_CREATE+ Create link if it does not exist +- +NLM_F_EXCL+ Return +EEXIST+ if link already exists + +*Returns:* + +- +EINVAL+ malformed message or invalid configuration parameters +- +EAFNOSUPPORT+ if an address family specific configuration (+IFLA_AF_SPEC+) + is not supported. +- +EOPNOTSUPP+ if the link does not support modification of parameters +- +EEXIST+ if +NLM_F_EXCL+ was set and the link exists alraedy +- +ENODEV+ if the link does not exist and +NLM_F_CREATE+ is not set + +.RTM_NEWLINK (kernel->user) + +This message type is used in reply to a +RTM_GETLINK+ request and carries +the configuration and statistics of a link. If multiple links need to +be sent, the messages will be sent in form of a multipart message. + +The message type is also used for notifications sent by the kernel to the +multicast group +RTNLGRP_LINK+ to inform about various link events. It is +therefore recommended to always use a separate link socket for link +notifications in order to separate between the two message types. + +TODO: document how to detect different notifications + +.RTM_DELLINK (user->kernel) + +Lookup link by 1. interface index or 2. link name (+IFLA_IFNAME+) and delete +the virtual link. + +*Returns:* + +* +EINVAL+ if neither interface nor link name are set +* +ENODEV+ if no link was found +* +ENOTSUPP+ if the operation is not supported (not a virtual link) + +.RTM_DELLINK (kernel->user) + +Notification sent by the kernel to the multicast group +RTNLGRP_LINK+ when + +a. a network device was unregistered (change == ~0) +b. a bridging device was deleted (address family will be +PF_BRIDGE+) + +=== Get / List + +[[link_list]] +==== Get list of links + +To retrieve the list of links in the kernel, allocate a new link cache +using +rtnl_link_alloc_cache()+ to hold the links. It will automatically +construct and send a +RTM_GETLINK+ message requesting a dump of all links +from the kernel and feed the returned +RTM_NEWLINK+ to the internal link +message parser which adds the returned links to the cache. + +[source,c] +----- +#include + +int rtnl_link_alloc_cache(struct nl_sock *sk, int family, struct nl_cache **result) +----- + +The cache will contain link objects (+struct rtnl_link+, see <>) +and can be accessed using the standard cache functions. By setting the ++family+ parameter to an address family other than +AF_UNSPEC+, the resulting +cache will only contain links supporting the specified address family. + +The following direct search functions are provided to search by interface +index and by link name: + +[source,c] +----- +#include + +struct rtnl_link *rtnl_link_get(struct nl_cache *cache, int ifindex); +struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *cache, const char *name); +----- + +.Example: Link Cache + +[source,c] +----- +struct nl_cache *cache; +struct rtnl_link *link; + +if (rtnl_link_alloc_cache(sock, AF_UNSPEC, &cache)) < 0) + /* error */ + +if (!(link = rtnl_link_get_by_name(cache, "eth1"))) + /* link does not exist */ + +/* do something with link */ + +rtnl_link_put(link); +nl_cache_put(cache); +----- + +[[link_direct_lookup]] +==== Lookup Single Link (Direct Lookup) + +If only a single link is of interest, the link can be looked up directly +without the use of a link cache using the function +rtnl_link_get_kernel()+. + +[source,c] +----- +#include + +int rtnl_link_get_kernel(struct nl_sock *sk, int ifindex, const char *name, struct rtnl_link **result); +----- + +It will construct and send a +RTM_GETLINK+ request using the parameters +provided and wait for a +RTM_NEWLINK+ or netlink error message sent in +return. If the link exists, the link is returned as link object +(see <>). + +.Example: Direct link lookup +[source,c] +----- +struct rtnl_link *link; + +if (rtnl_link_get_kernel(sock, 0, "eth1", &link) < 0) + /* error */ + +/* do something with link */ + +rtnl_link_put(link); +----- + +NOTE: While using this function can save a substantial amount of bandwidth + on the netlink socket, the result will not be cached, subsequent calls + to rtnl_link_get_kernel() will always trigger sending a +RTM_GETLINK+ + request. + +[[link_translate_ifindex]] +==== Translating interface index to link name + +Applications which require to translate interface index to a link name or +vice versa may use the following functions to do so. Both functions require +a filled link cache to work with. + +[source,c] +----- +char *rtnl_link_i2name (struct nl_cache *cache, int ifindex, char *dst, size_t len); +int rtnl_link_name2i (struct nl_cache *cache, const char *name); +----- + +=== Add / Modify + +Several types of virtual link can be added on the fly using the function ++rtnl_link_add()+. + +[source,c] +----- +#include + +int rtnl_link_add(struct nl_sock *sk, struct rtnl_link *link, int flags); +----- + +=== Delete + +The deletion of virtual links such as VLAN devices or dummy devices is done +using the function +rtnl_link_delete()+. The link passed on to the function +can be a link from a link cache or it can be construct with the minimal +attributes needed to identify the link. + +[source,c] +----- +#include + +int rtnl_link_delete(struct nl_sock *sk, const struct rtnl_link *link); +----- + +The function will construct and send a +RTM_DELLINK+ request message and +returns any errors returned by the kernel. + +.Example: Delete link by name +[source,c] +----- +struct rtnl_link *link; + +if (!(link = rtnl_link_alloc())) + /* error */ + +rtnl_link_set_name(link, "my_vlan"); + +if (rtnl_link_delete(sock, link) < 0) + /* error */ + +rtnl_link_put(link); +----- + +[[link_object]] +=== Link Object + +A link is represented by the structure +struct rtnl_link+. Instances may be +created with the function +rtnl_link_alloc()+ or via a link cache (see +<>) and are freed again using the function +rtnl_link_put()+. + +[source,c] +----- +#include + +struct rtnl_link *rtnl_link_alloc(void); +void rtnl_link_put(struct rtnl_link *link); +----- + +[[link_attr_name]] +==== Name +The name serves as unique, human readable description of the link. By +default, links are named based on their type and then enumerated, e.g. +eth0, eth1, ethn but they may be renamed at any time. + +Kernels >= 2.6.11 support identification by link name. + +[source,c] +----- +#include + +void rtnl_link_set_name(struct rtnl_link *link, const char *name); +char *rtnl_link_get_name(struct rtnl_link *link); +----- + +*Accepted link name format:* +[^ /]*+ (maximum length: 15 characters) + +[[link_attr_ifindex]] +==== Interface Index (Identifier) +The interface index is an integer uniquely identifying a link. If present +in any link message, it will be used to identify an existing link. + +[source,c] +----- +#include + +void rtnl_link_set_ifindex(struct rtnl_link *link, int ifindex); +int rtnl_link_get_ifindex(struct rtnl_link *link); +----- + +[[link_attr_group]] +==== Group +Each link can be assigned a numeric group identifier to group a bunch of links +together and apply a set of changes to a group instead of just a single link. + + +[source,c] +----- +#include + +void rtnl_link_set_group(struct rtnl_link *link, uint32_t group); +uint32_t rtnl_link_get_group(struct rtnl_link *link); +----- + +[[link_attr_address]] +==== Link Layer Address +The link layer address (e.g. MAC address). + +[source,c] +----- +#include + +void rtnl_link_set_addr(struct rtnl_link *link, struct nl_addr *addr); +struct nl_addr *rtnl_link_get_addr(struct rtnl_link *link); +----- + +[[link_attr_permaddr]] +==== Permanent address +The permanent link layer address (e.g. MAC address). + +[source,c] +----- +#include + +struct nl_addr *rtnl_link_get_perm_addr(struct rtnl_link *link); +----- + +[[link_attr_broadcast]] +==== Broadcast Address +The link layer broadcast address + +[source,c] +----- +#include + +void rtnl_link_set_broadcast(struct rtnl_link *link, struct nl_addr *addr); +struct nl_addr *rtnl_link_get_broadcast(struct rtnl_link *link); +----- + +[[link_attr_mtu]] +==== MTU (Maximum Transmission Unit) +The maximum transmission unit specifies the maximum packet size a network +device can transmit or receive. This value may be lower than the capability +of the physical network device. + +[source,c] +----- +#include + +void rtnl_link_set_mtu(struct rtnl_link *link, unsigned int mtu); +unsigned int rtnl_link_get_mtu(struct rtnl_link *link); +----- + +[[link_attr_flags]] +==== Flags +The flags of a link enable or disable various link features or inform about +the state of the link. + +[source,c] +----- +#include + +void rtnl_link_set_flags(struct rtnl_link *link, unsigned int flags); +void rtnl_link_unset_flags(struct rtnl_link *link, unsigned int flags); +unsigned int rtnl_link_get_flags(struct rtnl_link *link); +----- + +[options="compact"] +[horizontal] +IFF_UP:: Link is up (administratively) +IFF_RUNNING:: Link is up and carrier is OK (RFC2863 OPER_UP) +IFF_LOWER_UP:: Link layer is operational +IFF_DORMANT:: Driver signals dormant +IFF_BROADCAST:: Link supports broadcasting +IFF_MULTICAST:: Link supports multicasting +IFF_ALLMULTI:: Link supports multicast routing +IFF_DEBUG:: Tell driver to do debugging (currently unused) +IFF_LOOPBACK:: Link loopback network +IFF_POINTOPOINT:: Point-to-point link +IFF_NOARP:: ARP is not supported +IFF_PROMISC:: Status of promiscuous mode +IFF_MASTER:: Master of a load balancer (bonding) +IFF_SLAVE:: Slave to a master link +IFF_PORTSEL:: Driver supports setting media type (only used by ARM ethernet) +IFF_AUTOMEDIA:: Link selects port automatically (only used by ARM ethernet) +IFF_ECHO:: Echo sent packets (testing feature, CAN only) +IFF_DYNAMIC:: Unused (BSD compatibility) +IFF_NOTRAILERS:: Unused (BSD compatibility) + +To translate a link flag to a link flag name or vice versa: + +[source,c] +----- +#include + +char *rtnl_link_flags2str(int flags, char *buf, size_t size); +int rtnl_link_str2flags(const char *flag_name); +----- + +[[link_attr_txqlen]] +==== Transmission Queue Length + +The transmission queue holds packets before packets are delivered to +the driver for transmission. It is usually specified in number of +packets but the unit may be specific to the link type. + +[source,c] +----- +#include + +void rtnl_link_set_txqlen(struct rtnl_link *link, unsigned int txqlen); +unsigned int rtnl_link_get_txqlen(struct rtnl_link *link); +----- + +[[link_attr_operstate]] +==== Operational Status +The operational status has been introduced to provide extended information +on the link status. Traditionally the link state has been described using +the link flags +IFF_UP, IFF_RUNNING, IFF_LOWER_UP+, and +IFF_DORMANT+ which +was no longer sufficient for some link types. + +[source,c] +----- +#include + +void rtnl_link_set_operstate(struct rtnl_link *link, uint8_t state); +uint8_t rtnl_link_get_operstate(struct rtnl_link *link); +----- + +[options="compact"] +[horizontal] +IF_OPER_UNKNOWN:: Unknown state +IF_OPER_NOTPRESENT:: Link not present +IF_OPER_DOWN:: Link down +IF_OPER_LOWERLAYERDOWN:: L1 down +IF_OPER_TESTING:: Testing +IF_OPER_DORMANT:: Dormant +IF_OPER_UP:: Link up + +Translation of operational status code to string and vice versa: + +[source,c] +----- +#include + +char *rtnl_link_operstate2str(uint8_t state, char *buf, size_t size); +int rtnl_link_str2operstate(const char *name); +----- + +[[link_attr_mode]] +==== Mode +Currently known link modes are: + +[options="compact"] +[horizontal] +IF_LINK_MODE_DEFAULT:: Default link mode +IF_LINK_MODE_DORMANT:: Limit upward transition to dormant + +[source,c] +----- +#include + +void rtnl_link_set_linkmode(struct rtnl_link *link, uint8_t mode); +uint8_t rtnl_link_get_linkmode(struct rtnl_link *link); +----- + +Translation of link mode to string and vice versa: + +[source,c] +----- +char *rtnl_link_mode2str(uint8_t mode, char *buf, size_t len); +uint8_t rtnl_link_str2mode(const char *name); +----- + +[[link_attr_alias]] +==== IfAlias +Alternative name for the link, primarly used for SNMP IfAlias. + +[source,c] +----- +#include + +const char *rtnl_link_get_ifalias(struct rtnl_link *link); +void rtnl_link_set_ifalias(struct rtnl_link *link, const char *alias); +----- + +*Length limit:* 256 + +[[link_attr_arptype]] +==== Hardware Type + +[source,c] +----- +#include +#include + +void rtnl_link_set_arptype(struct rtnl_link *link, unsigned int arptype); +unsigned int rtnl_link_get_arptype(struct rtnl_link *link); +---- + +Translation of hardware type to character string and vice versa: + +[source,c] +----- +#include + +char *nl_llproto2str(int arptype, char *buf, size_t len); +int nl_str2llproto(const char *name); +----- + +[[link_attr_qdisc]] +==== Qdisc +The name of the queueing discipline used by the link is of informational +nature only. It is a read-only attribute provided by the kernel and cannot +be modified. The set function is provided solely for the purpose of creating +link objects to be used for comparison. + +For more information on how to modify the qdisc of a link, see section +<>. + +[source,c] +----- +#include + +void rtnl_link_set_qdisc(struct rtnl_link *link, const char *name); +char *rtnl_link_get_qdisc(struct rtnl_link *link); +----- + +[[link_attr_promiscuity]] +==== Promiscuity +The number of subsystem currently depending on the link being promiscuous mode. +A value of 0 indicates that the link is not in promiscuous mode. It is a +read-only attribute provided by the kernel and cannot be modified. The set +function is provided solely for the purpose of creating link objects to be +used for comparison. + +[source,c] +----- +#include + +void rtnl_link_set_promiscuity(struct rtnl_link *link, uint32_t count); +uint32_t rtnl_link_get_promiscuity(struct rtnl_link *link); +----- + +[[link_num_rxtx_queues]] +==== RX/TX Queues +The number of RX/TX queues the link provides. The attribute is writable but +will only be considered when creating a new network device via netlink. + +[source,c] +----- +#include + +void rtnl_link_set_num_tx_queues(struct rtnl_link *link, uint32_t nqueues); +uint32_t rtnl_link_get_num_tx_queues(struct rtnl_link *link); + +void rtnl_link_set_num_rx_queues(struct rtnl_link *link, uint32_t nqueues); +uint32_t rtnl_link_get_num_rx_queues(struct rtnl_link *link); +----- + +[[link_attr_weight]] +==== Weight +This attribute is unused and obsoleted in all recent kernels. + + +[[link_modules]] +=== Modules + +[[link_bonding]] +==== Bonding + +.Example: Add bonding link +[source,c] +----- +#include + +struct rtnl_link *link; + +link = rtnl_link_bond_alloc(); +rtnl_link_set_name(link, "my_bond"); + +/* requires admin privileges */ +if (rtnl_link_add(sk, link, NLM_F_CREATE) < 0) + /* error */ + +rtnl_link_put(link); +----- + +[[link_vlan]] +==== VLAN + +[source,c] +----- +extern char * rtnl_link_vlan_flags2str(int, char *, size_t); +extern int rtnl_link_vlan_str2flags(const char *); + +extern int rtnl_link_vlan_set_id(struct rtnl_link *, int); +extern int rtnl_link_vlan_get_id(struct rtnl_link *); + +extern int rtnl_link_vlan_set_flags(struct rtnl_link *, + unsigned int); +extern int rtnl_link_vlan_unset_flags(struct rtnl_link *, + unsigned int); +extern unsigned int rtnl_link_vlan_get_flags(struct rtnl_link *); + +extern int rtnl_link_vlan_set_ingress_map(struct rtnl_link *, + int, uint32_t); +extern uint32_t * rtnl_link_vlan_get_ingress_map(struct rtnl_link *); + +extern int rtnl_link_vlan_set_egress_map(struct rtnl_link *, + uint32_t, int); +extern struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *, + int *); +----- + +.Example: Add a VLAN device +[source,c] +----- +struct rtnl_link *link; +int master_index; + +/* lookup interface index of eth0 */ +if (!(master_index = rtnl_link_name2i(link_cache, "eth0"))) + /* error */ + +/* allocate new link object of type vlan */ +link = rtnl_link_vlan_alloc(); + +/* set eth0 to be our master device */ +rtnl_link_set_link(link, master_index); + +rtnl_link_vlan_set_id(link, 10); + +if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) + /* error */ + +rtnl_link_put(link); +----- + +[[link_macvlan]] +==== MACVLAN + +[source,c] +----- +extern struct rtnl_link *rtnl_link_macvlan_alloc(void); + +extern int rtnl_link_is_macvlan(struct rtnl_link *); + +extern char * rtnl_link_macvlan_mode2str(int, char *, size_t); +extern int rtnl_link_macvlan_str2mode(const char *); + +extern char * rtnl_link_macvlan_flags2str(int, char *, size_t); +extern int rtnl_link_macvlan_str2flags(const char *); + +extern int rtnl_link_macvlan_set_mode(struct rtnl_link *, + uint32_t); +extern uint32_t rtnl_link_macvlan_get_mode(struct rtnl_link *); + +extern int rtnl_link_macvlan_set_flags(struct rtnl_link *, + uint16_t); +extern int rtnl_link_macvlan_unset_flags(struct rtnl_link *, + uint16_t); +extern uint16_t rtnl_link_macvlan_get_flags(struct rtnl_link *); +----- + +.Example: Add a MACVLAN device +[source,c] +----- +struct rtnl_link *link; +int master_index; +struct nl_addr* addr; + +/* lookup interface index of eth0 */ +if (!(master_index = rtnl_link_name2i(link_cache, "eth0"))) + /* error */ + +/* allocate new link object of type macvlan */ +link = rtnl_link_macvlan_alloc(); + +/* set eth0 to be our master device */ +rtnl_link_set_link(link, master_index); + +/* set address of virtual interface */ +addr = nl_addr_build(AF_LLC, ether_aton("00:11:22:33:44:55"), ETH_ALEN); +rtnl_link_set_addr(link, addr); +nl_addr_put(addr); + +/* set mode of virtual interface */ +rtnl_link_macvlan_set_mode(link, rtnl_link_macvlan_str2mode("bridge")); + +if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) + /* error */ + +rtnl_link_put(link); +----- + +[[link_macvtap]] +==== MACVTAP + +[source,c] +----- +extern struct rtnl_link *rtnl_link_macvtap_alloc(void); + +extern int rtnl_link_is_macvtap(struct rtnl_link *); + +extern char * rtnl_link_macvtap_mode2str(int, char *, size_t); +extern int rtnl_link_macvtap_str2mode(const char *); + +extern char * rtnl_link_macvtap_flags2str(int, char *, size_t); +extern int rtnl_link_macvtap_str2flags(const char *); + +extern int rtnl_link_macvtap_set_mode(struct rtnl_link *, + uint32_t); +extern uint32_t rtnl_link_macvtap_get_mode(struct rtnl_link *); + +extern int rtnl_link_macvtap_set_flags(struct rtnl_link *, + uint16_t); +extern int rtnl_link_macvtap_unset_flags(struct rtnl_link *, + uint16_t); +extern uint16_t rtnl_link_macvtap_get_flags(struct rtnl_link *); +----- + +.Example: Add a MACVTAP device +[source,c] +----- +struct rtnl_link *link; +int master_index; +struct nl_addr* addr; + +/* lookup interface index of eth0 */ +if (!(master_index = rtnl_link_name2i(link_cache, "eth0"))) + /* error */ + +/* allocate new link object of type macvtap */ +link = rtnl_link_macvtap_alloc(); + +/* set eth0 to be our master device */ +rtnl_link_set_link(link, master_index); + +/* set address of virtual interface */ +addr = nl_addr_build(AF_LLC, ether_aton("00:11:22:33:44:55"), ETH_ALEN); +rtnl_link_set_addr(link, addr); +nl_addr_put(addr); + +/* set mode of virtual interface */ +rtnl_link_macvtap_set_mode(link, rtnl_link_macvtap_str2mode("bridge")); + +if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) + /* error */ + +rtnl_link_put(link); +----- + +[[link_vxlan]] +==== VXLAN + +[source,c] +----- +extern struct rtnl_link *rtnl_link_vxlan_alloc(void); + +extern int rtnl_link_is_vxlan(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_id(struct rtnl_link *, uint32_t); +extern int rtnl_link_vxlan_get_id(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_vxlan_set_group(struct rtnl_link *, struct nl_addr *); +extern int rtnl_link_vxlan_get_group(struct rtnl_link *, struct nl_addr **); + +extern int rtnl_link_vxlan_set_link(struct rtnl_link *, uint32_t); +extern int rtnl_link_vxlan_get_link(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_vxlan_set_local(struct rtnl_link *, struct nl_addr *); +extern int rtnl_link_vxlan_get_local(struct rtnl_link *, struct nl_addr **); + +extern int rtnl_link_vxlan_set_ttl(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_ttl(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_tos(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_tos(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_learning(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_learning(struct rtnl_link *); +extern int rtnl_link_vxlan_enable_learning(struct rtnl_link *); +extern int rtnl_link_vxlan_disable_learning(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_ageing(struct rtnl_link *, uint32_t); +extern int rtnl_link_vxlan_get_ageing(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_vxlan_set_limit(struct rtnl_link *, uint32_t); +extern int rtnl_link_vxlan_get_limit(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_vxlan_set_port_range(struct rtnl_link *, + struct ifla_vxlan_port_range *); +extern int rtnl_link_vxlan_get_port_range(struct rtnl_link *, + struct ifla_vxlan_port_range *); + +extern int rtnl_link_vxlan_set_proxy(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_proxy(struct rtnl_link *); +extern int rtnl_link_vxlan_enable_proxy(struct rtnl_link *); +extern int rtnl_link_vxlan_disable_proxy(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_rsc(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_rsc(struct rtnl_link *); +extern int rtnl_link_vxlan_enable_rsc(struct rtnl_link *); +extern int rtnl_link_vxlan_disable_rsc(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_l2miss(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_l2miss(struct rtnl_link *); +extern int rtnl_link_vxlan_enable_l2miss(struct rtnl_link *); +extern int rtnl_link_vxlan_disable_l2miss(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_l3miss(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_l3miss(struct rtnl_link *); +extern int rtnl_link_vxlan_enable_l3miss(struct rtnl_link *); +extern int rtnl_link_vxlan_disable_l3miss(struct rtnl_link *); +----- + +.Example: Add a VXLAN device +[source,c] +----- +struct rtnl_link *link; +struct nl_addr* addr; + +/* allocate new link object of type vxlan */ +link = rtnl_link_vxlan_alloc(); + +/* set interface name */ +rtnl_link_set_name(link, "vxlan128"); + +/* set VXLAN network identifier */ +if ((err = rtnl_link_vxlan_set_id(link, 128)) < 0) + /* error */ + +/* set multicast address to join */ +if ((err = nl_addr_parse("239.0.0.1", AF_INET, &addr)) < 0) + /* error */ + +if ((err = rtnl_link_set_group(link, addr)) < 0) + /* error */ + +nl_addr_put(addr); + +if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) + /* error */ + +rtnl_link_put(link); +----- + +[[link_ipip]] +==== IPIP + +[source,c] +----- +extern struct rtnl_link *rtnl_link_ipip_alloc(void); +extern int rtnl_link_ipip_add(struct nl_sock *sk, const char *name); + +extern int rtnl_link_ipip_set_link(struct rtnl_link *link, uint32_t index); +extern uint32_t rtnl_link_ipip_get_link(struct rtnl_link *link); + +extern int rtnl_link_ipip_set_local(struct rtnl_link *link, uint32_t addr); +extern uint32_t rtnl_link_ipip_get_local(struct rtnl_link *link); + +extern int rtnl_link_ipip_set_remote(struct rtnl_link *link, uint32_t addr); +extern uint32_t rtnl_link_ipip_get_remote(struct rtnl_link *link); + +extern int rtnl_link_ipip_set_ttl(struct rtnl_link *link, uint8_t ttl); +extern uint8_t rtnl_link_ipip_get_ttl(struct rtnl_link *link); + +extern int rtnl_link_ipip_set_tos(struct rtnl_link *link, uint8_t tos); +extern uint8_t rtnl_link_ipip_get_tos(struct rtnl_link *link); + +extern int rtnl_link_ipip_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc); +extern uint8_t rtnl_link_ipip_get_pmtudisc(struct rtnl_link *link); + +extern int rtnl_link_ipip_set_fwmark(struct rtnl_link *link, uint32_t fwmark); +extern int rtnl_link_ipip_get_fwmark(struct rtnl_link *link, uint32_t *fwmark); + +----- + +.Example: Add an ipip tunnel device +[source,c] +----- +struct rtnl_link *link +struct in_addr addr + +/* allocate new link object of type ipip */ +if(!(link = rtnl_link_ipip_alloc())) + /* error */ + +/* set ipip tunnel name */ +if ((err = rtnl_link_set_name(link, "ipip-tun")) < 0) + /* error */ + +/* set link index */ +if ((err = rtnl_link_ipip_set_link(link, if_index)) < 0) + /* error */ + +/* set local address */ +inet_pton(AF_INET, "192.168.254.12", &addr.s_addr); +if ((err = rtnl_link_ipip_set_local(link, addr.s_addr)) < 0) + /* error */ + +/* set remote address */ +inet_pton(AF_INET, "192.168.254.13", &addr.s_addr +if ((err = rtnl_link_ipip_set_remote(link, addr.s_addr)) < 0) + /* error */ + +/* set tunnel ttl */ +if ((err = rtnl_link_ipip_set_ttl(link, 64)) < 0) + /* error */ + +if((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) + /* error */ + +rtnl_link_put(link); +----- + +[[link_ipgre]] +==== IPGRE + +[source,c] +----- +extern struct rtnl_link *rtnl_link_ipgre_alloc(void); +extern int rtnl_link_ipgre_add(struct nl_sock *sk, const char *name); + +extern int rtnl_link_ipgre_set_link(struct rtnl_link *link, uint32_t index); +extern uint32_t rtnl_link_ipgre_get_link(struct rtnl_link *link); + +extern int rtnl_link_ipgre_set_iflags(struct rtnl_link *link, uint16_t iflags); +extern uint16_t rtnl_link_get_iflags(struct rtnl_link *link); + +extern int rtnl_link_ipgre_set_oflags(struct rtnl_link *link, uint16_t oflags); +extern uint16_t rtnl_link_get_oflags(struct rtnl_link *link); + +extern int rtnl_link_ipgre_set_ikey(struct rtnl_link *link, uint32_t ikey); +extern uint32_t rtnl_link_get_ikey(struct rtnl_link *link); + +extern int rtnl_link_ipgre_set_okey(struct rtnl_link *link, uint32_t okey); +extern uint32_t rtnl_link_get_okey(struct rtnl_link *link) + +extern int rtnl_link_ipgre_set_local(struct rtnl_link *link, uint32_t addr); +extern uint32_t rtnl_link_ipgre_get_local(struct rtnl_link *link); + +extern int rtnl_link_ipgre_set_remote(struct rtnl_link *link, uint32_t addr); +extern uint32_t rtnl_link_ipgre_get_remote(struct rtnl_link *link); + +extern int rtnl_link_ipgre_set_ttl(struct rtnl_link *link, uint8_t ttl); +extern uint8_t rtnl_link_ipgre_get_ttl(struct rtnl_link *link); + +extern int rtnl_link_ipgre_set_tos(struct rtnl_link *link, uint8_t tos); +extern uint8_t rtnl_link_ipgre_get_tos(struct rtnl_link *link); + +extern int rtnl_link_ipgre_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc); +extern uint8_t rtnl_link_ipgre_get_pmtudisc(struct rtnl_link *link); + +extern int rtnl_link_ipgre_set_fwmark(struct rtnl_link *link, uint32_t fwmark); +extern int rtnl_link_ipgre_get_fwmark(struct rtnl_link *link, uint32_t *fwmark); + +----- + +.Example: Add an ipgre tunnel device +[source,c] +----- +struct rtnl_link *link +struct in_addr addr + +/* allocate new link object of type ipgre */ +if(!(link = rtnl_link_ipgre_alloc())) + /* error */ + +/* set ipgre tunnel name */ +if ((err = rtnl_link_set_name(link, "ipgre-tun")) < 0) + /* error */ + +/* set link index */ +if ((err = rtnl_link_ipgre_set_link(link, if_index)) < 0) + /* error */ + +/* set local address */ +inet_pton(AF_INET, "192.168.254.12", &addr.s_addr); +if ((err = rtnl_link_ipgre_set_local(link, addr.s_addr)) < 0) + /* error */ + +/* set remote address */ +inet_pton(AF_INET, "192.168.254.13", &addr.s_addr +if ((err = rtnl_link_ipgre_set_remote(link, addr.s_addr)) < 0) + /* error */ + +/* set tunnel ttl */ +if ((err = rtnl_link_ipgre_set_ttl(link, 64)) < 0) + /* error */ + +if((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) + /* error */ + +rtnl_link_put(link); +----- + +[[link_sit]] +==== SIT + +[source,c] +----- +extern struct rtnl_link *rtnl_link_sit_alloc(void); +extern int rtnl_link_sit_add(struct nl_sock *sk, const char *name); + +extern int rtnl_link_sit_set_link(struct rtnl_link *link, uint32_t index); +extern uint32_t rtnl_link_sit_get_link(struct rtnl_link *link); + +extern int rtnl_link_sit_set_iflags(struct rtnl_link *link, uint16_t iflags); +extern uint16_t rtnl_link_get_iflags(struct rtnl_link *link); + +extern int rtnl_link_sit_set_oflags(struct rtnl_link *link, uint16_t oflags); +extern uint16_t rtnl_link_get_oflags(struct rtnl_link *link); + +extern int rtnl_link_sit_set_ikey(struct rtnl_link *link, uint32_t ikey); +extern uint32_t rtnl_link_get_ikey(struct rtnl_link *link); + +extern int rtnl_link_sit_set_okey(struct rtnl_link *link, uint32_t okey); +extern uint32_t rtnl_link_get_okey(struct rtnl_link *link) + +extern int rtnl_link_sit_set_local(struct rtnl_link *link, uint32_t addr); +extern uint32_t rtnl_link_sit_get_local(struct rtnl_link *link); + +extern int rtnl_link_sit_set_remote(struct rtnl_link *link, uint32_t addr); +extern uint32_t rtnl_link_sit_get_remote(struct rtnl_link *link); + +extern int rtnl_link_sit_set_ttl(struct rtnl_link *link, uint8_t ttl); +extern uint8_t rtnl_link_sit_get_ttl(struct rtnl_link *link); + +extern int rtnl_link_sit_set_tos(struct rtnl_link *link, uint8_t tos); +extern uint8_t rtnl_link_sit_get_tos(struct rtnl_link *link); + +extern int rtnl_link_sit_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc); +extern uint8_t rtnl_link_sit_get_pmtudisc(struct rtnl_link *link); + +extern int rtnl_link_sit_set_fwmark(struct rtnl_link *link, uint32_t fwmark); +extern int rtnl_link_sit_get_fwmark(struct rtnl_link *link, uint32_t *fwmark); + +----- + +.Example: Add a sit tunnel device +[source,c] +----- +struct rtnl_link *link +struct in_addr addr + +/* allocate new link object of type sit */ +if(!(link = rtnl_link_sit_alloc())) + /* error */ + +/* set sit tunnel name */ +if ((err = rtnl_link_set_name(link, "sit-tun")) < 0) + /* error */ + +/* set link index */ +if ((err = rtnl_link_sit_set_link(link, if_index)) < 0) + /* error */ + +/* set local address */ +inet_pton(AF_INET, "192.168.254.12", &addr.s_addr); +if ((err = rtnl_link_sit_set_local(link, addr.s_addr)) < 0) + /* error */ + +/* set remote address */ +inet_pton(AF_INET, "192.168.254.13", &addr.s_addr +if ((err = rtnl_link_sit_set_remote(link, addr.s_addr)) < 0) + /* error */ + +/* set tunnel ttl */ +if ((err = rtnl_link_sit_set_ttl(link, 64)) < 0) + /* error */ + +if((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) + /* error */ + +rtnl_link_put(link); +----- + + +[[link_ipvti]] +==== IPVTI + +[source,c] +----- +extern struct rtnl_link *rtnl_link_ipvti_alloc(void); +extern int rtnl_link_ipvti_add(struct nl_sock *sk, const char *name); + +extern int rtnl_link_ipvti_set_link(struct rtnl_link *link, uint32_t index); +extern uint32_t rtnl_link_ipvti_get_link(struct rtnl_link *link); + +extern int rtnl_link_ipvti_set_ikey(struct rtnl_link *link, uint32_t ikey); +extern uint32_t rtnl_link_ipvti_get_ikey(struct rtnl_link *link); + +extern int rtnl_link_ipvti_set_okey(struct rtnl_link *link, uint32_t okey); +extern uint32_t rtnl_link_ipvti_get_okey(struct rtnl_link *link) + +extern int rtnl_link_ipvti_set_local(struct rtnl_link *link, uint32_t addr); +extern uint32_t rtnl_link_ipvti_get_local(struct rtnl_link *link); + +extern int rtnl_link_ipvti_set_remote(struct rtnl_link *link, uint32_t addr); +extern uint32_t rtnl_link_ipvti_get_remote(struct rtnl_link *link); + +extern int rtnl_link_ipvti_set_fwmark(struct rtnl_link *link, uint32_t fwmark); +extern int rtnl_link_ipvti_get_fwmark(struct rtnl_link *link, uint32_t *fwmark); + +----- + +.Example: Add an ipvti tunnel device +[source,c] +----- +struct rtnl_link *link +struct in_addr addr + +/* allocate new link object of type ipvti */ +if(!(link = rtnl_link_ipvti_alloc())) + /* error */ + +/* set ipvti tunnel name */ +if ((err = rtnl_link_set_name(link, "ipvti-tun")) < 0) + /* error */ + +/* set link index */ +if ((err = rtnl_link_ipvti_set_link(link, if_index)) < 0) + /* error */ + +/* set local address */ +inet_pton(AF_INET, "192.168.254.12", &addr.s_addr); +if ((err = rtnl_link_ipvti_set_local(link, addr.s_addr)) < 0) + /* error */ + +/* set remote address */ +inet_pton(AF_INET, "192.168.254.13", &addr.s_addr +if ((err = rtnl_link_ipvti_set_remote(link, addr.s_addr)) < 0) + /* error */ + +if((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) + /* error */ + +rtnl_link_put(link); +----- + +[[link_ip6tnl]] +==== IP6TNL + +[source,c] +----- +extern struct rtnl_link *rtnl_link_ip6_tnl_alloc(void); +extern int rtnl_link_ip6_tnl_add(struct nl_sock *sk, const char *name); + +extern int rtnl_link_ip6_tnl_set_link(struct rtnl_link *link, uint32_t index); +extern uint32_t rtnl_link_ip6_tnl_get_link(struct rtnl_link *link); + +extern int rtnl_link_ip6_tnl_set_local(struct rtnl_link *link, struct in6_addr *); +extern int rtnl_link_ip6_tnl_get_local(struct rtnl_link *link, struct in6_addr *); + +extern int rtnl_link_ip6_tnl_set_remote(struct rtnl_link *link, struct in6_addr *); +extern int rtnl_link_ip6_tnl_get_remote(struct rtnl_link *link, struct in6_addr *); + +extern int rtnl_link_ip6_tnl_set_ttl(struct rtnl_link *link, uint8_t ttl); +extern uint8_t rtnl_link_ip6_tnl_get_ttl(struct rtnl_link *link); + +extern int rtnl_link_ip6_tnl_set_tos(struct rtnl_link *link, uint8_t tos); +extern uint8_t rtnl_link_ip6_tnl_get_tos(struct rtnl_link *link); + +extern int rtnl_link_ip6_tnl_set_encaplimit(struct rtnl_link *link, uint8_t encap_limit); +extern uint8_t rtnl_link_ip6_tnl_get_encaplimit(struct rtnl_link *link); + +extern int rtnl_link_ip6_tnl_set_flags(struct rtnl_link *link, uint32_t flags); +extern uint32_t rtnl_link_ip6_tnl_get_flags(struct rtnl_link *link); + +extern uint32_t rtnl_link_ip6_tnl_get_flowinfo(struct rtnl_link *link); +extern int rtnl_link_ip6_tnl_set_flowinfo(struct rtnl_link *link, uint32_t flowinfo); + +extern int rtnl_link_ip6_tnl_set_proto(struct rtnl_link *link, uint8_t proto); +extern uint8_t rtnl_link_ip6_tnl_get_proto(struct rtnl_link *link); + +extern int rtnl_link_ip6_tnl_set_fwmark(struct rtnl_link *link, uint32_t fwmark); +extern int rtnl_link_ip6_tnl_get_fwmark(struct rtnl_link *link, uint32_t *fwmark); + +----- + +.Example: Add an ip6tnl tunnel device +[source,c] +----- +struct rtnl_link *link +struct in6_addr addr + +link = rtnl_link_ip6_tnl_alloc(); + +rtnl_link_set_name(link, "ip6tnl-tun"); +rtnl_link_ip6_tnl_set_link(link, if_index); + +inet_pton(AF_INET6, "2607:f0d0:1002:51::4", &addr); +rtnl_link_ip6_tnl_set_local(link, &addr); + +inet_pton(AF_INET6, "2607:f0d0:1002:52::5", &addr); +rtnl_link_ip6_tnl_set_remote(link, &addr); + +rtnl_link_add(sk, link, NLM_F_CREATE); +rtnl_link_put(link); + +----- + +[[link_ip6gre]] +==== IP6GRE + +[source,c] +---- +extern int rtnl_link_is_ip6gre(struct rtnl_link *link); + +extern struct rtnl_link *rtnl_link_ip6gre_alloc(void); +extern int rtnl_link_ip6gre_add(struct nl_sock *sk, const char *name); + +extern int rtnl_link_ip6gre_set_link(struct rtnl_link *link, uint32_t index); +extern int rtnl_link_ip6gre_get_link(struct rtnl_link *link, uint32_t *index); + +extern int rtnl_link_ip6gre_set_iflags(struct rtnl_link *link, uint16_t iflags); +extern int rtnl_link_ip6gre_get_iflags(struct rtnl_link *link, uint16_t *iflags); + +extern int rtnl_link_ip6gre_set_oflags(struct rtnl_link *link, uint16_t oflags); +extern int rtnl_link_ip6gre_get_oflags(struct rtnl_link *link, uint16_t *oflags); + +extern int rtnl_link_ip6gre_set_ikey(struct rtnl_link *link, uint32_t ikey); +extern int rtnl_link_ip6gre_get_ikey(struct rtnl_link *link, uint32_t *ikey); + +extern int rtnl_link_ip6gre_set_okey(struct rtnl_link *link, uint32_t okey); +extern int rtnl_link_ip6gre_get_okey(struct rtnl_link *link, uint32_t *okey); + +extern int rtnl_link_ip6gre_set_local(struct rtnl_link *link, struct in6_addr *local); +extern int rtnl_link_ip6gre_get_local(struct rtnl_link *link, struct in6_addr *local); + +extern int rtnl_link_ip6gre_set_remote(struct rtnl_link *link, struct in6_addr *remote); +extern int rtnl_link_ip6gre_get_remote(struct rtnl_link *link, struct in6_addr *remote); + +extern int rtnl_link_ip6gre_set_ttl(struct rtnl_link *link, uint8_t ttl); +extern int rtnl_link_ip6gre_get_ttl(struct rtnl_link *link, uint8_t *ttl); + +extern int rtnl_link_ip6gre_set_encaplimit(struct rtnl_link *link, uint8_t encaplimit); +extern int rtnl_link_ip6gre_get_encaplimit(struct rtnl_link *link, uint8_t *encaplimit); + +extern int rtnl_link_ip6gre_set_flowinfo(struct rtnl_link *link, uint32_t flowinfo); +extern int rtnl_link_ip6gre_get_flowinfo(struct rtnl_link *link, uint32_t *flowinfo); + +extern int rtnl_link_ip6gre_set_flags(struct rtnl_link *link, uint32_t flags); +extern int rtnl_link_ip6gre_get_flags(struct rtnl_link *link, uint32_t *flags); + +extern int rtnl_link_ip6gre_set_fwmark(struct rtnl_link *link, uint32_t fwmark); +extern int rtnl_link_ip6gre_get_fwmark(struct rtnl_link *link, uint32_t *fwmark); + +---- + +.Example: Add an ip6gre tunnel device +[source,c] +---- +struct rtnl_link *link +struct in6_addr addr + +link = rtnl_link_ip6gre_alloc(); + +rtnl_link_set_name(link, "ip6gre-tun"); +rtnl_link_ip6gre_set_link(link, if_index); + +inet_pton(AF_INET6, "2607:f0d0:1002:51::4", &addr); +rtnl_link_ip6gre_set_local(link, &addr); + +inet_pton(AF_INET6, "2607:f0d0:1002:52::5", &addr); +rtnl_link_ip6gre_set_remote(link, &addr); + +rtnl_link_add(sk, link, NLM_F_CREATE); +rtnl_link_put(link); + +----- + +[[link_ip6vti]] +==== IP6VTI + +[source,c] +---- +int rtnl_link_is_ip6vti(struct rtnl_link *link); + +extern struct rtnl_link *rtnl_link_ip6vti_alloc(void); +extern int rtnl_link_ip6vti_add(struct nl_sock *sk, const char *name); + +extern int rtnl_link_ip6vti_set_link(struct rtnl_link *link, uint32_t index); +extern int rtnl_link_ip6vti_get_link(struct rtnl_link *link, uint32_t *index); + +extern int rtnl_link_ip6vti_set_ikey(struct rtnl_link *link, uint32_t ikey); +extern int rtnl_link_ip6vti_get_ikey(struct rtnl_link *link, uint32_t *ikey); + +extern int rtnl_link_ip6vti_set_okey(struct rtnl_link *link, uint32_t okey); +extern int rtnl_link_ip6vti_get_okey(struct rtnl_link *link, uint32_t *okey); + +extern int rtnl_link_ip6vti_set_local(struct rtnl_link *link, struct in6_addr *local); +extern int rtnl_link_ip6vti_get_local(struct rtnl_link *link, struct in6_addr *remote); + +extern int rtnl_link_ip6vti_set_remote(struct rtnl_link *link, struct in6_addr *remote); +extern int rtnl_link_ip6vti_get_remote(struct rtnl_link *link, struct in6_addr *remote); + +extern int rtnl_link_ip6vti_set_fwmark(struct rtnl_link *link, uint32_t fwmark); +extern int rtnl_link_ip6vti_get_fwmark(struct rtnl_link *link, uint32_t *fwmark); + +---- + +.Example: Add an ip6vti tunnel device +[source,c] +---- +struct rtnl_link *link +struct in6_addr addr + +link = rtnl_link_ip6vti_alloc(); + +rtnl_link_set_name(link, "ip6vti-tun"); +rtnl_link_ip6vti_set_link(link, if_index); + +inet_pton(AF_INET6, "2607:f0d0:1002:51::4", &addr); +rtnl_link_ip6vti_set_local(link, &addr); + +inet_pton(AF_INET6, "2607:f0d0:1002:52::5", &addr); +rtnl_link_ip6vti_set_remote(link, &addr); + +rtnl_link_add(sk, link, NLM_F_CREATE); +rtnl_link_put(link); + +----- + +[[link_xfrmi]] +==== XFRMI + +[source,c] +----- +extern struct rtnl_link *rtnl_link_xfrmi_alloc(void); + +extern int rtnl_link_xfrmi_set_link(struct rtnl_link *link, uint32_t index); +extern uint32_t rtnl_link_xfrmi_get_link(struct rtnl_link *link); + +extern int rtnl_link_xfrmi_set_if_id(struct rtnl_link *link, uint32_t if_id); +extern uint32_t rtnl_link_xfrmi_get_if_id(struct rtnl_link *link); + +----- + +.Example: Add a xfrmi device +[source,c] +----- +struct rtnl_link *link +struct in_addr addr + +/* allocate new link object of type xfrmi */ +if(!(link = rtnl_link_xfrmi_alloc())) + /* error */ + +/* set xfrmi name */ +if ((err = rtnl_link_set_name(link, "ipsec0")) < 0) + /* error */ + +/* set link index */ +if ((err = rtnl_link_xfrmi_set_link(link, if_index)) < 0) + /* error */ + +/* set if_id */ +if ((err = rtnl_link_xfrmi_set_if_id(link, 16)) < 0) + /* error */ + +if((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) + /* error */ + +rtnl_link_put(link); +----- + +== Neighbouring + +== Routing + +[[route_tc]] +== Traffic Control + +The traffic control architecture allows the queueing and +prioritization of packets before they are enqueued to the network +driver. To a limited degree it is also possible to take control of +network traffic as it enters the network stack. + +The architecture consists of three different types of modules: + +- *Queueing disciplines (qdisc)* provide a mechanism to enqueue packets + in different forms. They may be used to implement fair queueing, + prioritization of differentiated services, enforce bandwidth + limitations, or even to simulate network behaviour such as packet + loss and packet delay. Qdiscs can be classful in which case they + allow traffic classes described in the next paragraph to be attached + to them. + +- *Traffic classes (class)* are supported by several qdiscs to build + a tree structure for different types of traffic. Each class may be + assigned its own set of attributes such as bandwidth limits or + queueing priorities. Some qdiscs even allow borrowing of bandwidth + between classes. + +- *Classifiers (cls)* are used to decide which qdisc/class the packet + should be enqueued to. Different types of classifiers exists, + ranging from classification based on protocol header values to + classification based on packet priority or firewall marks. + Additionally most classifiers support *extended matches (ematch)* + which allow extending classifiers by a set of matcher modules, and + *actions* which allow classifiers to take actions such as mangling, + mirroring, or even rerouting of packets. + +.Default Qdisc + +The default qdisc used on all network devices is `pfifo_fast`. +Network devices which do not require a transmit queue such as the +loopback device do not have a default qdisc attached. The `pfifo_fast` +qdisc provides three bands to prioritize interactive traffic over bulk +traffic. Classification is based on the packet priority (diffserv). + +image:qdisc_default.png["Default Qdisc"] + +.Multiqueue Default Qdisc + +If the network device provides multiple transmit queues the `mq` +qdisc is used by default. It will automatically create a separate +class for each transmit queue available and will also replace +the single per device tx lock with a per queue lock. + +image:qdisc_mq.png["Multiqueue default Qdisc"] + +.Example of a customized classful qdisc setup + +The following figure illustrates a possible combination of different +queueing and classification modules to implement quality of service +needs. + +image:tc_overview.png["Classful Qdisc diagram"] + +=== Traffic Control Object + +Each type traffic control module (qdisc, class, classifier) is +represented by its own structure. All of them are based on the traffic +control object represented by `struct rtnl_tc` which itself is based +on the generic object `struct nl_object` to make it cacheable. The +traffic control object contains all attributes, implementation details +and statistics that are shared by all of the traffic control object +types. + +image:tc_obj.png["struct rtnl_tc hierarchy"] + +It is not possible to allocate a `struct rtnl_tc` object, instead the +actual tc object types must be allocated directly using +`rtnl_qdisc_alloc()`, `rtnl_class_alloc()`, `rtnl_cls_alloc()` and +then casted to `struct rtnl_tc` using the `TC_CAST()` macro. + +.Usage Example: Allocation, Casting, Freeing +[source,c] +----- +#include +#include + +struct rtnl_qdisc *qdisc; + +/* Allocation of a qdisc object */ +qdisc = rtnl_qdisc_alloc(); + +/* Cast the qdisc to a tc object using TC_CAST() to use rtnl_tc_ functions. */ +rtnl_tc_set_mpu(TC_CAST(qdisc), 64); + +/* Free the qdisc object */ +rtnl_qdisc_put(qdisc); +----- + +[[tc_attr]] +==== Attributes + +Handle:: +The handle uniquely identifies a tc object and is used to refer +to other tc objects when constructing tc trees. ++ +[source,c] +----- +void rtnl_tc_set_handle(struct rtnl_tc *tc, uint32_t handle); +uint32_t rtnl_tc_get_handle(struct rtnl_tc *tc); +----- + +Interface Index:: +The interface index specifies the network device the traffic object +is attached to. The function `rtnl_tc_set_link()` should be preferred +when setting the interface index. It stores the reference to the link +object in the tc object and allows retrieving the `mtu` and `linktype` +automatically. ++ +[source,c] +----- +void rtnl_tc_set_ifindex(struct rtnl_tc *tc, int ifindex); +void rtnl_tc_set_link(struct rtnl_tc *tc, struct rtnl_link *link); +int rtnl_tc_get_ifindex(struct rtnl_tc *tc); +----- + +Link Type:: +The link type specifies the kind of link that is used by the network +device (e.g. ethernet, ATM, ...). It is derived automatically when +the network device is specified with `rtnl_tc_set_link()`. +The default fallback is `ARPHRD_ETHER` (ethernet). ++ +[source,c] +----- +void rtnl_tc_set_linktype(struct rtnl_tc *tc, uint32_t type); +uint32_t rtnl_tc_get_linktype(struct rtnl_tc *tc); +----- + +Kind:: +The kind character string specifies the type of qdisc, class, +classifier. Setting the kind results in the module specific +structure being allocated. Therefore it is imperative to call +`rtnl_tc_set_kind()` before using any type specific API functions +such as `rtnl_htb_set_rate()`. ++ +[source,c] +----- +int rtnl_tc_set_kind(struct rtnl_tc *tc, const char *kind); +char *rtnl_tc_get_kind(struct rtnl_tc *tc); +----- + +MPU:: +The Minimum Packet Unit specifies the minimum packet size which will +be transmitted +ever be seen by this traffic control object. This value is used for +rate calculations. Not all object implementations will make use of +this value. The default value is 0. ++ +[source,c] +----- +void rtnl_tc_set_mpu(struct rtnl_tc *tc, uint32_t mpu); +uint32_t rtnl_tc_get_mpu(struct rtnl_tc *tc); +----- + +MTU:: +The Maximum Transmission Unit specifies the maximum packet size which +will be transmitted. The value is derived from the link specified +with `rtnl_tc_set_link()` if not overwritten with `rtnl_tc_set_mtu()`. +If no link and MTU is specified, the value defaults to 1500 +(ethernet). ++ +[source,c] +----- +void rtnl_tc_set_mtu(struct rtnl_tc *tc, uint32_t mtu); +uint32_t rtnl_tc_get_mtu(struct rtnl_tc *tc); +----- + +Overhead:: +The overhead specifies the additional overhead per packet caused by +the network layer. This value can be used to correct packet size +calculations if the packet size on the wire does not match the packet +size seen by the kernel. The default value is 0. ++ +[source,c] +----- +void rtnl_tc_set_overhead(struct rtnl_tc *tc, uint32_t overhead); +uint32_t rtnl_tc_get_overhead(struct rtnl_tc *tc); +----- + +Parent:: +Specifies the parent traffic control object. The parent is identified +by its handle. Special values are: +- `TC_H_ROOT`: attach tc object directly to network device (root + qdisc, root classifier) +- `TC_H_INGRESS`: same as `TC_H_ROOT` but on the ingress side of the + network stack. ++ +[source,c] +----- +void rtnl_tc_set_parent(struct rtnl_tc *tc, uint32_t parent); +uint32_t rtnl_tc_get_parent(struct rtnl_tc *tc); +----- + +Statistics:: +Generic statistics, see <> for additional information. ++ +[source,c] +----- +uint64_t rtnl_tc_get_stat(struct rtnl_tc *tc, enum rtnl_tc_stat id); +----- + +[[tc_stats]] +==== Accessing Statistics + +The traffic control object holds a set of generic statistics. Not all +traffic control modules will make use of all of these statistics. Some +modules may provide additional statistics via their own APIs. + +.Statistic identifiers `(enum rtnl_tc_stat)` +[cols="m,,", options="header", frame="topbot"] +|==================================================================== +| ID | Type | Description +| RTNL_TC_PACKETS | Counter | Total # of packets transmitted +| RTNL_TC_BYTES | Counter | Total # of bytes transmitted +| RTNL_TC_RATE_BPS | Rate | Current bytes/s rate +| RTNL_TC_RATE_PPS | Rate | Current packets/s rate +| RTNL_TC_QLEN | Rate | Current length of the queue +| RTNL_TC_BACKLOG | Rate | # of packets currently backlogged +| RTNL_TC_DROPS | Counter | # of packets dropped +| RTNL_TC_REQUEUES | Counter | # of packets requeued +| RTNL_TC_OVERLIMITS | Counter | # of packets that exceeded the limit +|==================================================================== + +NOTE: `RTNL_TC_RATE_BPS` and `RTNL_TC_RATE_PPS` only return meaningful + values if a rate estimator has been configured. + +.Usage Example: Retrieving tc statistics +[source,c] +------- +#include + +uint64_t drops, qlen; + +drops = rtnl_tc_get_stat(TC_CAST(qdisc), RTNL_TC_DROPS); +qlen = rtnl_tc_get_stat(TC_CAST(qdisc), RTNL_TC_QLEN); +------- + +==== Rate Table Calculations +TODO + +[[tc_qdisc]] +=== Queueing Discipline (qdisc) + +.Classless Qdisc + +The queueing discipline (qdisc) is used to implement fair queueing, +prioritization or rate control. It provides a _enqueue()_ and +_dequeue()_ operation. Whenever a network packet leaves the networking +stack over a network device, be it a physical or virtual device, it +will be enqueued to a qdisc unless the device is queueless. The +_enqueue()_ operation is followed by an immediate call to _dequeue()_ +for the same qdisc to eventually retrieve a packet which can be +scheduled for transmission by the driver. Additionally, the networking +stack runs a watchdog which polls the qdisc regularly to dequeue and +send packets even if no new packets are being enqueued. + +This additional watchdog is required due to the fact that qdiscs may +hold on to packets and not return any packets upon _dequeue()_ in +order to enforce bandwidth restrictions. + +image:classless_qdisc_nbands.png[alt="Multiband Qdisc", float="right"] + +The figure illustrates a trivial example of a classless qdisc +consisting of three bands (queues). Use of multiple bands is a common +technique in qdiscs to implement fair queueing between flows or +prioritize differentiated services. + +Classless qdiscs can be regarded as a blackbox, their inner workings +can only be steered using the configuration parameters provided by the +qdisc. There is no way of taking influence on the structure of its +internal queues itself. + +.Classful Qdisc + +Classful qdiscs allow for the queueing structure and classification +process to be created by the user. + +image:classful_qdisc.png["Classful Qdisc"] + +The figure above shows a classful qdisc with a classifier attached to +it which will make the decision whether to enqueue a packet to traffic +class +1:1+ or +1:2+. Unlike with classless qdiscs, classful qdiscs +allow the classification process and the structure of the queues to be +defined by the user. This allows for complex traffic class rules to +be applied. + +.List of Qdisc Implementations +[options="header", frame="topbot", cols="2,1^,8"] +|====================================================================== +| Qdisc | Classful | Description +| ATM | Yes | FIXME +| Blackhole | No | This qdisc will drop all packets passed to it. +| CBQ | Yes | +The CBQ (Class Based Queueing) is a classful qdisc which allows +creating traffic classes and enforce bandwidth limitations for each +class. +| DRR | Yes | +The DRR (Deficit Round Robin) scheduler is a classful qdisc +implementing fair queueing. Each class is assigned a quantum specifying +the maximum number of bytes that can be served per round. Unused +quantum at the end of the round is carried over to the next round. +| DSMARK | Yes | FIXME +| FIFO | No | FIXME +| GRED | No | FIXME +| HFSC | Yes | FIXME +| HTB | Yes | FIXME +| mq | Yes | FIXME +| multiq | Yes | FIXME +| netem | No | FIXME +| Prio | Yes | FIXME +| RED | Yes | FIXME +| SFQ | Yes | FIXME +| TBF | Yes | FIXME +| teql | No | FIXME +|====================================================================== + + +.QDisc API Overview +[cols="a,a", options="header", frame="topbot"] +|==================================================================== +| Attribute | C Interface +| +Allocation / Freeing:: +| +[source,c] +----- +struct rtnl_qdisc *rtnl_qdisc_alloc(void); +void rtnl_qdisc_put(struct rtnl_qdisc *qdisc); +----- +| +Addition:: +| +[source,c] +----- +int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags, + struct nl_msg **result); +int rtnl_qdisc_add(struct nl_sock *sock, struct rtnl_qdisc *qdisc, + int flags); +----- +| +Modification:: +| +[source,c] +----- +int rtnl_qdisc_build_change_request(struct rtnl_qdisc *old, + struct rtnl_qdisc *new, + struct nl_msg **result); +int rtnl_qdisc_change(struct nl_sock *sock, struct rtnl_qdisc *old, + struct rtnl_qdisc *new); +----- +| +Deletion:: +| +[source,c] +----- +int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc, + struct nl_msg **result); +int rtnl_qdisc_delete(struct nl_sock *sock, struct rtnl_qdisc *qdisc); +----- +| +Cache:: +| +[source,c] +----- +int rtnl_qdisc_alloc_cache(struct nl_sock *sock, + struct nl_cache **cache); +struct rtnl_qdisc *rtnl_qdisc_get(struct nl_cache *cache, int, uint32_t); + +struct rtnl_qdisc *rtnl_qdisc_get_by_parent(struct nl_cache *, int, uint32_t); +----- +|==================================================================== + +[[qdisc_get]] +==== Retrieving Qdisc Configuration + +The function rtnl_qdisc_alloc_cache() is used to retrieve the current +qdisc configuration in the kernel. It will construct a +RTM_GETQDISC+ +netlink message, requesting the complete list of qdiscs configured in +the kernel. + +[source,c] +------- +#include + +struct nl_cache *all_qdiscs; + +if (rtnl_link_alloc_cache(sock, &all_qdiscs) < 0) + /* error while retrieving qdisc cfg */ +------- + +The cache can be accessed using the following functions: + +- Search qdisc with matching ifindex and handle: ++ +[source,c] +-------- +struct rtnl_qdisc *rtnl_qdisc_get(struct nl_cache *cache, int ifindex, uint32_t handle); +-------- +- Search qdisc with matching ifindex and parent: ++ +[source,c] +-------- +struct rtnl_qdisc *rtnl_qdisc_get_by_parent(struct nl_cache *cache, int ifindex , uint32_t parent); +-------- +- Or any of the generic cache functions (e.g. nl_cache_search(), nl_cache_dump(), etc.) + +.Example: Search and print qdisc +[source,c] +------- +struct rtnl_qdisc *qdisc; +int ifindex; + +ifindex = rtnl_link_get_ifindex(eth0_obj); + +/* search for qdisc on eth0 with handle 1:0 */ +if (!(qdisc = rtnl_qdisc_get(all_qdiscs, ifindex, TC_HANDLE(1, 0)))) + /* no such qdisc found */ + +nl_object_dump(OBJ_CAST(qdisc), NULL); + +rtnl_qdisc_put(qdisc); +------- + +[[qdisc_add]] +==== Adding a Qdisc + +In order to add a new qdisc to the kernel, a qdisc object needs to be +allocated. It will hold all attributes of the new qdisc. + +[source,c] +----- +#include + +struct rtnl_qdisc *qdisc; + +if (!(qdisc = rtnl_qdisc_alloc())) + /* OOM error */ +----- + +The next step is to specify all generic qdisc attributes using the tc +object interface described in the section <>. + +The following attributes must be specified: +- IfIndex +- Parent +- Kind + +[source,c] +----- +/* Attach qdisc to device eth0 */ +rtnl_tc_set_link(TC_CAST(qdisc), eth0_obj); + +/* Make this the root qdisc */ +rtnl_tc_set_parent(TC_CAST(qdisc), TC_H_ROOT); + +/* Set qdisc identifier to 1:0, if left unspecified, a handle will be generated by the kernel. */ +rtnl_tc_set_handle(TC_CAST(qdisc), TC_HANDLE(1, 0)); + +/* Make this a HTB qdisc */ +rtnl_tc_set_kind(TC_CAST(qdisc), "htb"); +----- + +After specifying the qdisc kind (rtnl_tc_set_kind()) the qdisc type +specific interface can be used to set attributes which are specific +to the respective qdisc implementations: + +[source,c] +------ +/* HTB feature: Make unclassified packets go to traffic class 1:5 */ +rtnl_htb_set_defcls(qdisc, TC_HANDLE(1, 5)); +------ + +Finally, the qdisc is ready to be added and can be passed on to the +function rntl_qdisc_add() which takes care of constructing a netlink +message requesting the addition of the new qdisc, sends the message to +the kernel and waits for the response by the kernel. The function +returns 0 if the qdisc has been added or updated successfully or a +negative error code if an error occured. + +CAUTION: The kernel operation for updating and adding a qdisc is the + same. Therefore when calling rtnl_qdisc_add() any existing + qdisc with matching handle will be updated unless the flag + NLM_F_EXCL is specified. + +The following flags may be specified: +[horizontal] +NLM_F_CREATE:: Create qdisc if it does not exist, otherwise + -NLE_OBJ_NOTFOUND is returned. +NLM_F_REPLACE:: If another qdisc is already attached to the same + parent and their handles mismatch, replace the qdisc + instead of returning -EEXIST. +NLM_F_EXCL:: Return -NLE_EXISTS if a qdisc with matching handles + exists already. + +WARNING: The function rtnl_qdisc_add() requires administrator + privileges. + +[source,c] +------ +/* Submit request to kernel and wait for response */ +err = rtnl_qdisc_add(sock, qdisc, NLM_F_CREATE); + +/* Return the qdisc object to free memory resources */ +rtnl_qdisc_put(qdisc); + +if (err < 0) { + fprintf(stderr, "Unable to add qdisc: %s\n", nl_geterror(err)); + return err; +} +------ + +==== Deleting a qdisc + +[source,c] +------ +#include + +struct rtnl_qdisc *qdisc; + +qdisc = rtnl_qdisc_alloc(); + +rtnl_tc_set_link(TC_CAST(qdisc), eth0_obj); +rtnl_tc_set_parent(TC_CAST(qdisc), TC_H_ROOT); + +rtnl_qdisc_delete(sock, qdisc) + +rtnl_qdisc_put(qdisc); +------ + +WARNING: The function rtnl_qdisc_delete() requires administrator + privileges. + + +[[qdisc_htb]] +==== HTB - Hierarchical Token Bucket + +.HTB Qdisc Attributes + +Default Class:: +The default class is the fallback class to which all traffic which +remained unclassified is directed to. If no default class or an +invalid default class is specified, packets are transmitted directly +to the next layer (direct transmissions). ++ +[source,c] +----- +uint32_t rtnl_htb_get_defcls(struct rtnl_qdisc *qdisc); +int rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls); +----- + +Rate to Quantum (r2q):: +TODO ++ +[source,c] +----- +uint32_t rtnl_htb_get_rate2quantum(struct rtnl_qdisc *qdisc); +int rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum); +----- + + +.HTB Class Attributes + +Priority:: ++ +[source,c] +----- +uint32_t rtnl_htb_get_prio(struct rtnl_class *class); +int rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio); +----- + +Rate:: +The rate (bytes/s) specifies the maximum bandwidth an individual class +can use without borrowing. The rate of a class should always be greater +or erqual than the rate of its children. ++ +[source,c] +----- +uint32_t rtnl_htb_get_rate(struct rtnl_class *class); +int rtnl_htb_set_rate(struct rtnl_class *class, uint32_t ceil); +----- + +Ceil Rate:: +The ceil rate specifies the maximum bandwidth an individual class +can use. This includes bandwidth that is being borrowed from other +classes. Ceil defaults to the class rate implying that by default +the class will not borrow. The ceil rate of a class should always +be greater or erqual than the ceil rate of its children. ++ +[source,c] +----- +uint32_t rtnl_htb_get_ceil(struct rtnl_class *class); +int rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil); +----- + +Burst:: +TODO ++ +[source,c] +----- +uint32_t rtnl_htb_get_rbuffer(struct rtnl_class *class); +int rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t burst); +----- + +Ceil Burst:: +TODO ++ +[source,c] +----- +uint32_t rtnl_htb_get_cbuffer(struct rtnl_class *); +int rtnl_htb_set_cbuffer(struct rtnl_class *, uint32_t); +----- + +Quantum:: +TODO ++ +[source,c] +----- +uint32_t rtnl_htb_get_quantum(struct rtnl_class *class); +int rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum); +----- + + +[[tc_class]] +=== Class + +[options="header", cols="s,a,a,a,a"] +|======================================================================= +| | UNSPEC | TC_H_ROOT | 0:pY | pX:pY +| UNSPEC 3+^| +[horizontal] +qdisc =:: root-qdisc +class =:: root-qdisc:0 +| +[horizontal] +qdisc =:: pX:0 +class =:: pX:0 +| 0:hY 3+^| +[horizontal] +qdisc =:: root-qdisc +class =:: root-qdisc:hY +| +[horizontal] +qdisc =:: pX:0 +class =:: pX:hY +| hX:hY 3+^| +[horizontal] +qdisc =:: hX: +class =:: hX:hY +| +if pX != hX + return -EINVAL +[horizontal] +qdisc =:: hX: +class =:: hX:hY +|======================================================================= + +[[tc_cls]] +=== Classifier (cls) + +TODO + +[[tc_classid_mngt]] +=== ClassID Management + +TODO + +[[tc_pktloc]] +=== Packet Location Aliasing (pktloc) + +TODO + +[[tc_api]] +=== Traffic Control Module API + +TODO diff --git a/libnl/doc/src/hidden.c b/libnl/doc/src/hidden.c new file mode 100644 index 0000000..a63621d --- /dev/null +++ b/libnl/doc/src/hidden.c @@ -0,0 +1,42 @@ +/** + * \cond skip + * vim:syntax=doxygen + * \endcond + +\page auto_ack_warning Disabling Auto-ACK + +\attention Disabling Auto-ACK (nl_socket_disable_auto_ack()) will cause this + function to return immediately after sending the netlink message. + The function will not wait for an eventual error message. It is + the responsibility of the caller to handle any error messages or + ACKs returned. + +\page pointer_lifetime_warning Pointer Lifetime + +\attention The reference counter of the returned object is not incremented. + Therefore, the returned pointer is only valid during the lifetime + of the parent object. Increment the reference counter if the object + is supposed to stay around after the parent object was freed. + +\page private_struct Private Structure + +\note The definition of this structure is private to allow modification + without breaking API. Use the designated accessor functions to + access individual object attributes. + +\page read_only_attribute Read-Only Attribute + +\note The attribute this accessor is modifying is a read-only attribute + which can not be modified in the kernel. Any changes to the + attribute only have an effect on the local copy of the object. The + accessor function is provided solely for the purpose of creating + objects for comparison and filtering. + +\page low_level_api Low Level API + +\note This is a low level API function. A high level function implementing + the same functionality with a simplified usage pattern exists. This + function is available as an alternative if the default library + behaviour is not desirable. + +*/ diff --git a/libnl/doc/src/toc.c b/libnl/doc/src/toc.c new file mode 100644 index 0000000..921bcf6 --- /dev/null +++ b/libnl/doc/src/toc.c @@ -0,0 +1,54 @@ +/** + * \cond skip + * vim:syntax=doxygen + * \endcond + +\mainpage + +\section main_intro Introduction + +libnl is a set of libraries to deal with the netlink protocol and some +of the high level protocols implemented on top of it. The goal is to +provide APIs on different levels of abstraction. The core library libnl +provides a fundamental set of functions to deal with sockets, construct +messages, and send/receive those messages. Additional high level interfaces +for several individual netlink protocols are provided in separate +libraries (e.g. "nl-route", "nl-genl", ...). + +The library is designed to ensure that all components are optional, i.e. +even though the core library provides a caching system which allows to +easily manage objects of any kind, no application is required to use this +caching system if it has no need for it. + +The library was developed and tested on 2.6.x and 3.x kernel releases. It +may or may not work with older kernel series. Also, although all netlink +protocols are required to maintain backwards compatibility, this has not +always achieved and undesired side effects can occur if a recent libnl +version is used with a considerably older kernel. + +\section main_toc Table of Contents + +\section main_trees GIT Trees + +\subsection tree_dev Development Tree + +@code +git://git.infradead.org/users/tgr/libnl.git +@endcode +- Web: http://git.infradead.org/users/tgr/libnl.git + +\section main_website Website + +- https://www.infradead.org/~tgr/libnl/ + +\section main_mailinglist Mailinglist + +Please post questions and patches to the libnl mailinglist: + +@code +libnl@lists.infradead.org +@endcode + +- Archives: https://lists.infradead.org/pipermail/libnl/ + +*/ diff --git a/libnl/doc/stylesheets/asciidoc-manpage.css b/libnl/doc/stylesheets/asciidoc-manpage.css new file mode 100644 index 0000000..45eba23 --- /dev/null +++ b/libnl/doc/stylesheets/asciidoc-manpage.css @@ -0,0 +1,18 @@ +/* Overrides for manpage documents */ +h1 { + padding-top: 0.5em; + padding-bottom: 0.5em; + border-top: 2px solid silver; + border-bottom: 2px solid silver; +} +h2 { + border-style: none; +} +div.sectionbody { + margin-left: 3em; +} + +@media print { + div#toc { display: none; } +} + diff --git a/libnl/doc/stylesheets/asciidoc.css b/libnl/doc/stylesheets/asciidoc.css new file mode 100644 index 0000000..2852168 --- /dev/null +++ b/libnl/doc/stylesheets/asciidoc.css @@ -0,0 +1,526 @@ +/* Shared CSS for AsciiDoc xhtml11 and html5 backends */ + +/* Default font. */ +body { + font-family: Georgia,serif; +} + +/* Title font. */ +h1, h2, h3, h4, h5, h6, +div.title, caption.title, +thead, p.table.header, +#toctitle, +#author, #revnumber, #revdate, #revremark, +#footer { + /* OLD: font-family: Arial,Helvetica,sans-serif; */ + font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif; +} + +body { + margin: 1em 5% 1em 5%; +} + +a { + /* color: blue; */ + color: #990000; + text-decoration: none; +} +a:visited { + /* color: fuchsia; */ +} + +a:hover { + text-decoration: underline; +} + +em { + font-style: italic; + /* color: navy; */ +} + +strong { + font-weight: bold; + color: black; + /* color: #083194; */ +} + +h1, h2, h3, h4, h5, h6 { + /* color: #527bbd; */ + color: #990000; + margin-top: 1.2em; + margin-bottom: 0.5em; + line-height: 1.3; +} + +h1, h2, h3 { + border-bottom: 2px solid silver; +} +h2 { + padding-top: 0.5em; +} +h3 { + float: left; +} +h3 + * { + clear: left; +} +h5 { + font-size: 1.0em; +} + +div.sectionbody { + margin-left: 0; +} + +hr { + border: 1px solid silver; +} + +p { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +ul, ol, li > p { + margin-top: 0; +} +ul > li { color: #aaa; } +ul > li > * { color: black; } + +pre { + padding: 0; + margin: 0; +} + +#author { + /* color: #527bbd; */ + font-weight: bold; + font-size: 1.1em; +} +#email { +} +#revnumber, #revdate, #revremark { +} + +#footer { + font-size: small; + border-top: 2px solid silver; + padding-top: 0.5em; + margin-top: 4.0em; +} +#footer-text { + float: left; + padding-bottom: 0.5em; +} +#footer-badges { + float: right; + padding-bottom: 0.5em; +} + +#preamble { + margin-top: 1.5em; + margin-bottom: 1.5em; +} +div.imageblock, div.exampleblock, div.verseblock, +div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock, +div.admonitionblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +div.admonitionblock { + margin-top: 2.0em; + margin-bottom: 2.0em; + margin-right: 10%; + /* color: #606060; */ +} + +div.content { /* Block element content. */ + padding: 0; +} + +/* Block element titles. */ +div.title, caption.title { + /* OLD: color: #527bbd; */ + color: #990000; + font-weight: bold; + text-align: left; + margin-top: 1.0em; + margin-bottom: 0.5em; +} +div.title + * { + margin-top: 0; +} + +td div.title:first-child { + margin-top: 0.0em; +} +div.content div.title:first-child { + margin-top: 0.0em; +} +div.content + div.title { + margin-top: 0.0em; +} + +div.sidebarblock > div.content { + background: #ffffee; + border: 1px solid #dddddd; + border-left: 4px solid #f0f0f0; + padding: 0.5em; +} + +div.listingblock > div.content { + border: 1px solid #dddddd; + /* border-left: 5px solid #f0f0f0; */ + background: #f8f8f8; + padding: 0.5em; +} + +div.quoteblock, div.verseblock { + padding-left: 1.0em; + margin-left: 1.0em; + margin-right: 10%; + border-left: 5px solid #f0f0f0; + color: #777777; +} + +div.quoteblock > div.attribution { + padding-top: 0.5em; + text-align: right; +} + +div.verseblock > pre.content { + font-family: inherit; + font-size: inherit; +} +div.verseblock > div.attribution { + padding-top: 0.75em; + text-align: left; +} +/* DEPRECATED: Pre version 8.2.7 verse style literal block. */ +div.verseblock + div.attribution { + text-align: left; +} + +div.admonitionblock .icon { + vertical-align: top; + font-size: 1.1em; + font-weight: bold; + text-decoration: underline; + /* OLD: color: #527bbd; */ + color: #990000; + padding-right: 0.5em; +} +div.admonitionblock td.content { + padding-left: 0.5em; + border-left: 3px solid #dddddd; +} + +div.exampleblock > div.content { + border-left: 3px solid #dddddd; + padding-left: 0.5em; +} + +div.imageblock div.content { padding-left: 0; } +span.image img { border-style: none; } +a.image:visited { color: white; } + +dl { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +dt { + margin-top: 0.5em; + margin-bottom: 0; + font-style: normal; + color: navy; +} +dd > *:first-child { + margin-top: 0.1em; +} + +ul, ol { + list-style-position: outside; +} +ol.arabic { + list-style-type: decimal; +} +ol.loweralpha { + list-style-type: lower-alpha; +} +ol.upperalpha { + list-style-type: upper-alpha; +} +ol.lowerroman { + list-style-type: lower-roman; +} +ol.upperroman { + list-style-type: upper-roman; +} + +div.compact ul, div.compact ol, +div.compact p, div.compact p, +div.compact div, div.compact div { + margin-top: 0.1em; + margin-bottom: 0.1em; +} + +tfoot { + font-weight: bold; +} +td > div.verse { + white-space: pre; +} + +div.hdlist { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +div.hdlist tr { + padding-bottom: 15px; +} +dt.hdlist1.strong, td.hdlist1.strong { + font-weight: bold; +} +td.hdlist1 { + vertical-align: top; + font-style: normal; + padding-right: 0.8em; + /* color: navy; */ + color: #990000; +} +td.hdlist2 { + vertical-align: top; +} +div.hdlist.compact tr { + margin: 0; + padding-bottom: 0; +} + +.comment { + background: yellow; +} + +.footnote, .footnoteref { + font-size: 0.8em; +} + +span.footnote, span.footnoteref { + vertical-align: super; +} + +#footnotes { + margin: 20px 0 20px 0; + padding: 7px 0 0 0; +} + +#footnotes div.footnote { + margin: 0 0 5px 0; +} + +#footnotes hr { + border: none; + border-top: 1px solid silver; + height: 1px; + text-align: left; + margin-left: 0; + width: 20%; + min-width: 100px; +} + +div.colist td { + padding-right: 0.5em; + padding-bottom: 0.3em; + vertical-align: top; +} +div.colist td img { + margin-top: 0.3em; +} + +@media print { + #footer-badges { display: none; } +} + +#toc { + margin-bottom: 2.5em; +} + +#toctitle { + /* color: #527bbd; */ + color: #990000; + font-size: 1.1em; + font-weight: bold; + margin-top: 1.0em; + margin-bottom: 0.1em; +} + +div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 { + margin-top: 0; + margin-bottom: 0; +} +div.toclevel2 { + margin-left: 2em; + font-size: 0.9em; +} +div.toclevel3 { + margin-left: 4em; + font-size: 0.9em; +} +div.toclevel4 { + margin-left: 6em; + font-size: 0.9em; +} + +span.aqua { color: aqua; } +span.black { color: black; } +span.blue { color: blue; } +span.fuchsia { color: fuchsia; } +span.gray { color: gray; } +span.green { color: green; } +span.lime { color: lime; } +span.maroon { color: maroon; } +span.navy { color: navy; } +span.olive { color: olive; } +span.purple { color: purple; } +span.red { color: red; } +span.silver { color: silver; } +span.teal { color: teal; } +span.white { color: white; } +span.yellow { color: yellow; } + +span.aqua-background { background: aqua; } +span.black-background { background: black; } +span.blue-background { background: blue; } +span.fuchsia-background { background: fuchsia; } +span.gray-background { background: gray; } +span.green-background { background: green; } +span.lime-background { background: lime; } +span.maroon-background { background: maroon; } +span.navy-background { background: navy; } +span.olive-background { background: olive; } +span.purple-background { background: purple; } +span.red-background { background: red; } +span.silver-background { background: silver; } +span.teal-background { background: teal; } +span.white-background { background: white; } +span.yellow-background { background: yellow; } + +span.big { font-size: 2em; } +span.small { font-size: 0.6em; } + +span.underline { text-decoration: underline; } +span.overline { text-decoration: overline; } +span.line-through { text-decoration: line-through; } + + +/* + * xhtml11 specific + * + * */ + +tt { + font-family: monospace; + font-size: inherit; + /* color: navy; */ + color: black; +} + +div.tableblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +div.tableblock > table { + /* border: 3px solid #527bbd; */ + border: 2px solid #990000; +} +thead, p.table.header { + font-weight: bold; + /* color: #527bbd; */ + color: #990000; +} +p.table { + margin-top: 0; +} +/* Because the table frame attribute is overriden by CSS in most browsers. */ +div.tableblock > table[frame="void"] { + border-style: none; +} +div.tableblock > table[frame="hsides"] { + border-left-style: none; + border-right-style: none; +} +div.tableblock > table[frame="vsides"] { + border-top-style: none; + border-bottom-style: none; +} + + +/* + * html5 specific + * + * */ + +.monospaced { + font-family: monospace; + font-size: inherit; + color: navy; +} + +table.tableblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +thead, p.tableblock.header { + font-weight: bold; + /* color: #527bbd; */ + color: #990000; +} +p.tableblock { + margin-top: 0; +} +table.tableblock { + border-width: 3px; + border-spacing: 0px; + border-style: solid; + /* border-color: #527bbd; */ + border-color: #990000; + border-collapse: collapse; +} +th.tableblock, td.tableblock { + border-width: 1px; + padding: 4px; + border-style: solid; + /* border-color: #527bbd; */ + border-color: #990000; +} + +table.tableblock.frame-topbot { + border-left-style: hidden; + border-right-style: hidden; +} +table.tableblock.frame-sides { + border-top-style: hidden; + border-bottom-style: hidden; +} +table.tableblock.frame-none { + border-style: hidden; +} + +th.tableblock.halign-left, td.tableblock.halign-left { + text-align: left; +} +th.tableblock.halign-center, td.tableblock.halign-center { + text-align: center; +} +th.tableblock.halign-right, td.tableblock.halign-right { + text-align: right; +} + +th.tableblock.valign-top, td.tableblock.valign-top { + vertical-align: top; +} +th.tableblock.valign-middle, td.tableblock.valign-middle { + vertical-align: middle; +} +th.tableblock.valign-bottom, td.tableblock.valign-bottom { + vertical-align: bottom; +} diff --git a/libnl/doc/stylesheets/docbook-xsl.css b/libnl/doc/stylesheets/docbook-xsl.css new file mode 100644 index 0000000..6df2944 --- /dev/null +++ b/libnl/doc/stylesheets/docbook-xsl.css @@ -0,0 +1,322 @@ +/* + CSS stylesheet for XHTML produced by DocBook XSL stylesheets. + Tested with XSL stylesheets 1.61.2, 1.67.2 +*/ + +span.strong { + font-weight: bold; +} + +body blockquote { + margin-top: .75em; + line-height: 1.5; + margin-bottom: .75em; +} + +html body { + margin: 1em 5% 1em 5%; + line-height: 1.2; +} + +body div { + margin: 0; +} + +h1, h2, h3, h4, h5, h6 +{ + color: #527bbd; + font-family: tahoma, verdana, sans-serif; +} + +div.toc p:first-child, +div.list-of-figures p:first-child, +div.list-of-tables p:first-child, +div.list-of-examples p:first-child, +div.example p.title, +div.sidebar p.title +{ + font-weight: bold; + color: #527bbd; + font-family: tahoma, verdana, sans-serif; + margin-bottom: 0.2em; +} + +body h1 { + margin: .0em 0 0 -4%; + line-height: 1.3; + border-bottom: 2px solid silver; +} + +body h2 { + margin: 0.5em 0 0 -4%; + line-height: 1.3; + border-bottom: 2px solid silver; +} + +body h3 { + margin: .8em 0 0 -3%; + line-height: 1.3; +} + +body h4 { + margin: .8em 0 0 -3%; + line-height: 1.3; +} + +body h5 { + margin: .8em 0 0 -2%; + line-height: 1.3; +} + +body h6 { + margin: .8em 0 0 -1%; + line-height: 1.3; +} + +body hr { + border: none; /* Broken on IE6 */ +} +div.footnotes hr { + border: 1px solid silver; +} + +div.navheader th, div.navheader td, div.navfooter td { + font-family: sans-serif; + font-size: 0.9em; + font-weight: bold; + color: #527bbd; +} +div.navheader img, div.navfooter img { + border-style: none; +} +div.navheader a, div.navfooter a { + font-weight: normal; +} +div.navfooter hr { + border: 1px solid silver; +} + +body td { + line-height: 1.2 +} + +body th { + line-height: 1.2; +} + +ol { + line-height: 1.2; +} + +ul, body dir, body menu { + line-height: 1.2; +} + +html { + margin: 0; + padding: 0; +} + +body h1, body h2, body h3, body h4, body h5, body h6 { + margin-left: 0 +} + +body pre { + margin: 0.5em 10% 0.5em 1em; + line-height: 1.0; + color: navy; +} + +tt.literal, code.literal { + color: navy; +} + +.programlisting, .screen { + border: 1px solid silver; + background: #f4f4f4; + margin: 0.5em 10% 0.5em 0; + padding: 0.5em 1em; +} + +div.sidebar { + background: #ffffee; + margin: 1.0em 10% 0.5em 0; + padding: 0.5em 1em; + border: 1px solid silver; +} +div.sidebar * { padding: 0; } +div.sidebar div { margin: 0; } +div.sidebar p.title { + margin-top: 0.5em; + margin-bottom: 0.2em; +} + +div.bibliomixed { + margin: 0.5em 5% 0.5em 1em; +} + +div.glossary dt { + font-weight: bold; +} +div.glossary dd p { + margin-top: 0.2em; +} + +dl { + margin: .8em 0; + line-height: 1.2; +} + +dt { + margin-top: 0.5em; +} + +dt span.term { + font-style: normal; + color: navy; +} + +div.variablelist dd p { + margin-top: 0; +} + +div.itemizedlist li, div.orderedlist li { + margin-left: -0.8em; + margin-top: 0.5em; +} + +ul, ol { + list-style-position: outside; +} + +div.sidebar ul, div.sidebar ol { + margin-left: 2.8em; +} + +div.itemizedlist p.title, +div.orderedlist p.title, +div.variablelist p.title +{ + margin-bottom: -0.8em; +} + +div.revhistory table { + border-collapse: collapse; + border: none; +} +div.revhistory th { + border: none; + color: #527bbd; + font-family: tahoma, verdana, sans-serif; +} +div.revhistory td { + border: 1px solid silver; +} + +/* Keep TOC and index lines close together. */ +div.toc dl, div.toc dt, +div.list-of-figures dl, div.list-of-figures dt, +div.list-of-tables dl, div.list-of-tables dt, +div.indexdiv dl, div.indexdiv dt +{ + line-height: normal; + margin-top: 0; + margin-bottom: 0; +} + +/* + Table styling does not work because of overriding attributes in + generated HTML. +*/ +div.table table, +div.informaltable table +{ + margin-left: 0; + margin-right: 5%; + margin-bottom: 0.8em; +} +div.informaltable table +{ + margin-top: 0.4em +} +div.table thead, +div.table tfoot, +div.table tbody, +div.informaltable thead, +div.informaltable tfoot, +div.informaltable tbody +{ + /* No effect in IE6. */ + border-top: 3px solid #527bbd; + border-bottom: 3px solid #527bbd; +} +div.table thead, div.table tfoot, +div.informaltable thead, div.informaltable tfoot +{ + font-weight: bold; +} + +div.mediaobject img { + margin-bottom: 0.8em; +} +div.figure p.title, +div.table p.title +{ + margin-top: 1em; + margin-bottom: 0.4em; +} + +div.calloutlist p +{ + margin-top: 0em; + margin-bottom: 0.4em; +} + +a img { + border-style: none; +} + +@media print { + div.navheader, div.navfooter { display: none; } +} + +span.aqua { color: aqua; } +span.black { color: black; } +span.blue { color: blue; } +span.fuchsia { color: fuchsia; } +span.gray { color: gray; } +span.green { color: green; } +span.lime { color: lime; } +span.maroon { color: maroon; } +span.navy { color: navy; } +span.olive { color: olive; } +span.purple { color: purple; } +span.red { color: red; } +span.silver { color: silver; } +span.teal { color: teal; } +span.white { color: white; } +span.yellow { color: yellow; } + +span.aqua-background { background: aqua; } +span.black-background { background: black; } +span.blue-background { background: blue; } +span.fuchsia-background { background: fuchsia; } +span.gray-background { background: gray; } +span.green-background { background: green; } +span.lime-background { background: lime; } +span.maroon-background { background: maroon; } +span.navy-background { background: navy; } +span.olive-background { background: olive; } +span.purple-background { background: purple; } +span.red-background { background: red; } +span.silver-background { background: silver; } +span.teal-background { background: teal; } +span.white-background { background: white; } +span.yellow-background { background: yellow; } + +span.big { font-size: 2em; } +span.small { font-size: 0.6em; } + +span.underline { text-decoration: underline; } +span.overline { text-decoration: overline; } +span.line-through { text-decoration: line-through; } diff --git a/libnl/doc/stylesheets/flask-manpage.css b/libnl/doc/stylesheets/flask-manpage.css new file mode 100644 index 0000000..75a2dda --- /dev/null +++ b/libnl/doc/stylesheets/flask-manpage.css @@ -0,0 +1 @@ +/* Empty placeholder file */ diff --git a/libnl/doc/stylesheets/flask.css b/libnl/doc/stylesheets/flask.css new file mode 100644 index 0000000..8d33bc4 --- /dev/null +++ b/libnl/doc/stylesheets/flask.css @@ -0,0 +1,584 @@ +/* + * AsciiDoc 'flask' theme for xhtml11 and html5 backends. A shameless knock-off + * of the Flask website styling (http://flask.pocoo.org/docs/). + * + * The implementation is straight-forward, consisting of the asciidoc.css file + * followed by theme specific overrides. + * + * */ + + +/* Shared CSS for AsciiDoc xhtml11 and html5 backends */ + +/* Default font. */ +body { + font-family: Georgia,serif; +} + +/* Title font. */ +h1, h2, h3, h4, h5, h6, +div.title, caption.title, +thead, p.table.header, +#toctitle, +#author, #revnumber, #revdate, #revremark, +#footer { + font-family: Arial,Helvetica,sans-serif; +} + +body { + margin: 1em 5% 1em 5%; +} + +a { + color: blue; + text-decoration: underline; +} +a:visited { + color: fuchsia; +} + +em { + font-style: italic; + color: navy; +} + +strong { + font-weight: bold; + color: #083194; +} + +h1, h2, h3, h4, h5, h6 { + color: #527bbd; + margin-top: 1.2em; + margin-bottom: 0.5em; + line-height: 1.3; +} + +h1, h2, h3 { + border-bottom: 2px solid silver; +} +h2 { + padding-top: 0.5em; +} +h3 { + float: left; +} +h3 + * { + clear: left; +} +h5 { + font-size: 1.0em; +} + +div.sectionbody { + margin-left: 0; +} + +hr { + border: 1px solid silver; +} + +p { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +ul, ol, li > p { + margin-top: 0; +} +ul > li { color: #aaa; } +ul > li > * { color: black; } + +pre { + padding: 0; + margin: 0; +} + +#author { + color: #527bbd; + font-weight: bold; + font-size: 1.1em; +} +#email { +} +#revnumber, #revdate, #revremark { +} + +#footer { + font-size: small; + border-top: 2px solid silver; + padding-top: 0.5em; + margin-top: 4.0em; +} +#footer-text { + float: left; + padding-bottom: 0.5em; +} +#footer-badges { + float: right; + padding-bottom: 0.5em; +} + +#preamble { + margin-top: 1.5em; + margin-bottom: 1.5em; +} +div.imageblock, div.exampleblock, div.verseblock, +div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock, +div.admonitionblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +div.admonitionblock { + margin-top: 2.0em; + margin-bottom: 2.0em; + margin-right: 10%; + color: #606060; +} + +div.content { /* Block element content. */ + padding: 0; +} + +/* Block element titles. */ +div.title, caption.title { + color: #527bbd; + font-weight: bold; + text-align: left; + margin-top: 1.0em; + margin-bottom: 0.5em; +} +div.title + * { + margin-top: 0; +} + +td div.title:first-child { + margin-top: 0.0em; +} +div.content div.title:first-child { + margin-top: 0.0em; +} +div.content + div.title { + margin-top: 0.0em; +} + +div.sidebarblock > div.content { + background: #ffffee; + border: 1px solid #dddddd; + border-left: 4px solid #f0f0f0; + padding: 0.5em; +} + +div.listingblock > div.content { + border: 1px solid #dddddd; + border-left: 5px solid #f0f0f0; + background: #f8f8f8; + padding: 0.5em; +} + +div.quoteblock, div.verseblock { + padding-left: 1.0em; + margin-left: 1.0em; + margin-right: 10%; + border-left: 5px solid #f0f0f0; + color: #777777; +} + +div.quoteblock > div.attribution { + padding-top: 0.5em; + text-align: right; +} + +div.verseblock > pre.content { + font-family: inherit; + font-size: inherit; +} +div.verseblock > div.attribution { + padding-top: 0.75em; + text-align: left; +} +/* DEPRECATED: Pre version 8.2.7 verse style literal block. */ +div.verseblock + div.attribution { + text-align: left; +} + +div.admonitionblock .icon { + vertical-align: top; + font-size: 1.1em; + font-weight: bold; + text-decoration: underline; + color: #527bbd; + padding-right: 0.5em; +} +div.admonitionblock td.content { + padding-left: 0.5em; + border-left: 3px solid #dddddd; +} + +div.exampleblock > div.content { + border-left: 3px solid #dddddd; + padding-left: 0.5em; +} + +div.imageblock div.content { padding-left: 0; } +span.image img { border-style: none; } +a.image:visited { color: white; } + +dl { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +dt { + margin-top: 0.5em; + margin-bottom: 0; + font-style: normal; + color: navy; +} +dd > *:first-child { + margin-top: 0.1em; +} + +ul, ol { + list-style-position: outside; +} +ol.arabic { + list-style-type: decimal; +} +ol.loweralpha { + list-style-type: lower-alpha; +} +ol.upperalpha { + list-style-type: upper-alpha; +} +ol.lowerroman { + list-style-type: lower-roman; +} +ol.upperroman { + list-style-type: upper-roman; +} + +div.compact ul, div.compact ol, +div.compact p, div.compact p, +div.compact div, div.compact div { + margin-top: 0.1em; + margin-bottom: 0.1em; +} + +tfoot { + font-weight: bold; +} +td > div.verse { + white-space: pre; +} + +div.hdlist { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +div.hdlist tr { + padding-bottom: 15px; +} +dt.hdlist1.strong, td.hdlist1.strong { + font-weight: bold; +} +td.hdlist1 { + vertical-align: top; + font-style: normal; + padding-right: 0.8em; + color: navy; +} +td.hdlist2 { + vertical-align: top; +} +div.hdlist.compact tr { + margin: 0; + padding-bottom: 0; +} + +.comment { + background: yellow; +} + +.footnote, .footnoteref { + font-size: 0.8em; +} + +span.footnote, span.footnoteref { + vertical-align: super; +} + +#footnotes { + margin: 20px 0 20px 0; + padding: 7px 0 0 0; +} + +#footnotes div.footnote { + margin: 0 0 5px 0; +} + +#footnotes hr { + border: none; + border-top: 1px solid silver; + height: 1px; + text-align: left; + margin-left: 0; + width: 20%; + min-width: 100px; +} + +div.colist td { + padding-right: 0.5em; + padding-bottom: 0.3em; + vertical-align: top; +} +div.colist td img { + margin-top: 0.3em; +} + +@media print { + #footer-badges { display: none; } +} + +#toc { + margin-bottom: 2.5em; +} + +#toctitle { + color: #527bbd; + font-size: 1.1em; + font-weight: bold; + margin-top: 1.0em; + margin-bottom: 0.1em; +} + +div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 { + margin-top: 0; + margin-bottom: 0; +} +div.toclevel2 { + margin-left: 2em; + font-size: 0.9em; +} +div.toclevel3 { + margin-left: 4em; + font-size: 0.9em; +} +div.toclevel4 { + margin-left: 6em; + font-size: 0.9em; +} + +span.aqua { color: aqua; } +span.black { color: black; } +span.blue { color: blue; } +span.fuchsia { color: fuchsia; } +span.gray { color: gray; } +span.green { color: green; } +span.lime { color: lime; } +span.maroon { color: maroon; } +span.navy { color: navy; } +span.olive { color: olive; } +span.purple { color: purple; } +span.red { color: red; } +span.silver { color: silver; } +span.teal { color: teal; } +span.white { color: white; } +span.yellow { color: yellow; } + +span.aqua-background { background: aqua; } +span.black-background { background: black; } +span.blue-background { background: blue; } +span.fuchsia-background { background: fuchsia; } +span.gray-background { background: gray; } +span.green-background { background: green; } +span.lime-background { background: lime; } +span.maroon-background { background: maroon; } +span.navy-background { background: navy; } +span.olive-background { background: olive; } +span.purple-background { background: purple; } +span.red-background { background: red; } +span.silver-background { background: silver; } +span.teal-background { background: teal; } +span.white-background { background: white; } +span.yellow-background { background: yellow; } + +span.big { font-size: 2em; } +span.small { font-size: 0.6em; } + +span.underline { text-decoration: underline; } +span.overline { text-decoration: overline; } +span.line-through { text-decoration: line-through; } + + +/* + * xhtml11 specific + * + * */ + +tt { + font-family: monospace; + font-size: inherit; + color: navy; +} + +div.tableblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +div.tableblock > table { + border: 3px solid #527bbd; +} +thead, p.table.header { + font-weight: bold; + color: #527bbd; +} +p.table { + margin-top: 0; +} +/* Because the table frame attribute is overriden by CSS in most browsers. */ +div.tableblock > table[frame="void"] { + border-style: none; +} +div.tableblock > table[frame="hsides"] { + border-left-style: none; + border-right-style: none; +} +div.tableblock > table[frame="vsides"] { + border-top-style: none; + border-bottom-style: none; +} + + +/* + * html5 specific + * + * */ + +.monospaced { + font-family: monospace; + font-size: inherit; + color: navy; +} + +table.tableblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +thead, p.tableblock.header { + font-weight: bold; + color: #527bbd; +} +p.tableblock { + margin-top: 0; +} +table.tableblock { + border-width: 3px; + border-spacing: 0px; + border-style: solid; + border-color: #527bbd; + border-collapse: collapse; +} +th.tableblock, td.tableblock { + border-width: 1px; + padding: 4px; + border-style: solid; + border-color: #527bbd; +} + +table.tableblock.frame-topbot { + border-left-style: hidden; + border-right-style: hidden; +} +table.tableblock.frame-sides { + border-top-style: hidden; + border-bottom-style: hidden; +} +table.tableblock.frame-none { + border-style: hidden; +} + +th.tableblock.halign-left, td.tableblock.halign-left { + text-align: left; +} +th.tableblock.halign-center, td.tableblock.halign-center { + text-align: center; +} +th.tableblock.halign-right, td.tableblock.halign-right { + text-align: right; +} + +th.tableblock.valign-top, td.tableblock.valign-top { + vertical-align: top; +} +th.tableblock.valign-middle, td.tableblock.valign-middle { + vertical-align: middle; +} +th.tableblock.valign-bottom, td.tableblock.valign-bottom { + vertical-align: bottom; +} + + +/* + * Theme specific overrides of the preceding (asciidoc.css) CSS. + * + */ +body { + font-family: Garamond, Georgia, serif; + font-size: 17px; + color: #3E4349; + line-height: 1.3em; +} +h1, h2, h3, h4, h5, h6, +div.title, caption.title, +thead, p.table.header, +#toctitle, +#author, #revnumber, #revdate, #revremark, +#footer { + font-family: Garmond, Georgia, serif; + font-weight: normal; + border-bottom-width: 0; + color: #3E4349; +} +div.title, caption.title { color: #596673; font-weight: bold; } +h1 { font-size: 240%; } +h2 { font-size: 180%; } +h3 { font-size: 150%; } +h4 { font-size: 130%; } +h5 { font-size: 100%; } +h6 { font-size: 100%; } +#header h1 { margin-top: 0; } +#toc { + color: #444444; + line-height: 1.5; + padding-top: 1.5em; +} +#toctitle { + font-size: 20px; +} +#toc a { + border-bottom: 1px dotted #999999; + color: #444444 !important; + text-decoration: none !important; +} +#toc a:hover { + border-bottom: 1px solid #6D4100; + color: #6D4100 !important; + text-decoration: none !important; +} +div.toclevel1 { margin-top: 0.2em; font-size: 16px; } +div.toclevel2 { margin-top: 0.15em; font-size: 14px; } +em, dt, td.hdlist1 { color: black; } +strong { color: #3E4349; } +a { color: #004B6B; text-decoration: none; border-bottom: 1px dotted #004B6B; } +a:visited { color: #615FA0; border-bottom: 1px dotted #615FA0; } +a:hover { color: #6D4100; border-bottom: 1px solid #6D4100; } +div.tableblock > table, table.tableblock { border: 3px solid #E8E8E8; } +th.tableblock, td.tableblock { border: 1px solid #E8E8E8; } +ul > li > * { color: #3E4349; } +pre, tt, .monospaced { font-family: Consolas,Menlo,'Deja Vu Sans Mono','Bitstream Vera Sans Mono',monospace; } +tt, .monospaced { font-size: 0.9em; color: black; +} +div.exampleblock > div.content, div.sidebarblock > div.content, div.listingblock > div.content { border-width: 0 0 0 3px; border-color: #E8E8E8; } +div.verseblock { border-left-width: 0; margin-left: 3em; } +div.quoteblock { border-left-width: 3px; margin-left: 0; margin-right: 0;} +div.admonitionblock td.content { border-left: 3px solid #E8E8E8; } diff --git a/libnl/doc/stylesheets/pygments.css b/libnl/doc/stylesheets/pygments.css new file mode 100644 index 0000000..9ca3659 --- /dev/null +++ b/libnl/doc/stylesheets/pygments.css @@ -0,0 +1,66 @@ +/* + pygmentize filter +*/ +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f4f4f4; } +.highlight .c { color: #008800; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #AA22FF; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .cm { color: #008800; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #008800 } /* Comment.Preproc */ +.highlight .c1 { color: #008800; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #008800; font-weight: bold } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #FF0000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #808080 } /* Generic.Output */ +.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0040D0 } /* Generic.Traceback */ +.highlight .kc { color: #AA22FF; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #AA22FF; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #AA22FF; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #AA22FF } /* Keyword.Pseudo */ +.highlight .kr { color: #AA22FF; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #00BB00; font-weight: bold } /* Keyword.Type */ +.highlight .m { color: #666666 } /* Literal.Number */ +.highlight .s { color: #BB4444 } /* Literal.String */ +.highlight .na { color: #BB4444 } /* Name.Attribute */ +.highlight .nb { color: #AA22FF } /* Name.Builtin */ +.highlight .nc { color: #0000FF } /* Name.Class */ +.highlight .no { color: #880000 } /* Name.Constant */ +.highlight .nd { color: #AA22FF } /* Name.Decorator */ +.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #00A000 } /* Name.Function */ +.highlight .nl { color: #A0A000 } /* Name.Label */ +.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #B8860B } /* Name.Variable */ +.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #666666 } /* Literal.Number.Float */ +.highlight .mh { color: #666666 } /* Literal.Number.Hex */ +.highlight .mi { color: #666666 } /* Literal.Number.Integer */ +.highlight .mo { color: #666666 } /* Literal.Number.Oct */ +.highlight .sb { color: #BB4444 } /* Literal.String.Backtick */ +.highlight .sc { color: #BB4444 } /* Literal.String.Char */ +.highlight .sd { color: #BB4444; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #BB4444 } /* Literal.String.Double */ +.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #BB4444 } /* Literal.String.Heredoc */ +.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ +.highlight .sx { color: #008000 } /* Literal.String.Other */ +.highlight .sr { color: #BB6688 } /* Literal.String.Regex */ +.highlight .s1 { color: #BB4444 } /* Literal.String.Single */ +.highlight .ss { color: #B8860B } /* Literal.String.Symbol */ +.highlight .bp { color: #AA22FF } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #B8860B } /* Name.Variable.Class */ +.highlight .vg { color: #B8860B } /* Name.Variable.Global */ +.highlight .vi { color: #B8860B } /* Name.Variable.Instance */ +.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ + diff --git a/libnl/doc/stylesheets/slidy.css b/libnl/doc/stylesheets/slidy.css new file mode 100644 index 0000000..bbb790e --- /dev/null +++ b/libnl/doc/stylesheets/slidy.css @@ -0,0 +1,445 @@ +/* slidy.css + + Copyright (c) 2005-2010 W3C (MIT, ERCIM, Keio), All Rights Reserved. + W3C liability, trademark, document use and software licensing + rules apply, see: + + http://www.w3.org/Consortium/Legal/copyright-documents + http://www.w3.org/Consortium/Legal/copyright-software +*/ + +/* + SJR: 2010-09-29: Modified for AsciiDoc slidy backend. + Mostly just commented out stuff that is handled by AsciiDoc's CSS files. +*/ + +body +{ + margin: 0 0 0 0; + padding: 0 0 0 0; + width: 100%; + height: 100%; + color: black; + background-color: white; +/* + font-family: "Gill Sans MT", "Gill Sans", GillSans, sans-serif; +*/ + font-size: 14pt; +} + +div.toolbar { + position: fixed; z-index: 200; + top: auto; bottom: 0; left: 0; right: 0; + height: 1.2em; text-align: right; + padding-left: 1em; + padding-right: 1em; + font-size: 60%; + color: red; + background-color: rgb(240,240,240); + border-top: solid 1px rgb(180,180,180); +} + +div.toolbar span.copyright { + color: black; + margin-left: 0.5em; +} + +div.initial_prompt { + position: absolute; + z-index: 1000; + bottom: 1.2em; + width: 90%; + background-color: rgb(200,200,200); + opacity: 0.35; + background-color: rgb(200,200,200, 0.35); + cursor: pointer; +} + +div.initial_prompt p.help { + text-align: center; +} + +div.initial_prompt p.close { + text-align: right; + font-style: italic; +} + +div.slidy_toc { + position: absolute; + z-index: 300; + width: 60%; + max-width: 30em; + height: 30em; + overflow: auto; + top: auto; + right: auto; + left: 4em; + bottom: 4em; + padding: 1em; + background: rgb(240,240,240); + border-style: solid; + border-width: 2px; + font-size: 60%; +} + +div.slidy_toc .toc_heading { + text-align: center; + width: 100%; + margin: 0; + margin-bottom: 1em; + border-bottom-style: solid; + border-bottom-color: rgb(180,180,180); + border-bottom-width: 1px; +} + +div.slide { + z-index: 20; + margin: 0 0 0 0; + padding-top: 0; + padding-bottom: 0; + padding-left: 20px; + padding-right: 20px; + border-width: 0; + clear: both; + top: 0; + bottom: 0; + left: 0; + right: 0; + line-height: 120%; + background-color: transparent; +} + +div.background { + display: none; +} + +div.handout { + margin-left: 20px; + margin-right: 20px; +} + +div.slide.titlepage { + text-align: center; +} + +div.slide.titlepage.h1 { + padding-top: 10%; +} + +div.slide h1 { + padding-left: 0; + padding-right: 20pt; + padding-top: 4pt; + padding-bottom: 4pt; + margin-top: 0; + margin-left: 0; + margin-right: 60pt; + margin-bottom: 0.5em; + display: block; + font-size: 160%; + line-height: 1.2em; + background: transparent; +} + +div.toc { + position: absolute; + top: auto; + bottom: 4em; + left: 4em; + right: auto; + width: 60%; + max-width: 30em; + height: 30em; + border: solid thin black; + padding: 1em; + background: rgb(240,240,240); + color: black; + z-index: 300; + overflow: auto; + display: block; + visibility: visible; +} + +div.toc-heading { + width: 100%; + border-bottom: solid 1px rgb(180,180,180); + margin-bottom: 1em; + text-align: center; +} + +/* +pre { + font-size: 80%; + font-weight: bold; + line-height: 120%; + padding-top: 0.2em; + padding-bottom: 0.2em; + padding-left: 1em; + padding-right: 1em; + border-style: solid; + border-left-width: 1em; + border-top-width: thin; + border-right-width: thin; + border-bottom-width: thin; + border-color: #95ABD0; + color: #00428C; + background-color: #E4E5E7; +} +*/ + +/* +li pre { margin-left: 0; } + +blockquote { font-style: italic } + +img { background-color: transparent } + +p.copyright { font-size: smaller } +*/ + +.center { text-align: center } +.footnote { font-size: smaller; margin-left: 2em; } + +/* +a img { border-width: 0; border-style: none } +*/ + +a:visited { color: navy } +a:link { color: navy } +a:hover { color: red; text-decoration: underline } +a:active { color: red; text-decoration: underline } + +a {text-decoration: none} +.navbar a:link {color: white} +.navbar a:visited {color: yellow} +.navbar a:active {color: red} +.navbar a:hover {color: red} + +/* +ul { list-style-type: square; } +ul ul { list-style-type: disc; } +ul ul ul { list-style-type: circle; } +ul ul ul ul { list-style-type: disc; } +li { margin-left: 0.5em; margin-top: 0.5em; } +li li { font-size: 85%; font-style: italic } +li li li { font-size: 85%; font-style: normal } +*/ + +div dt +{ + margin-left: 0; + margin-top: 1em; + margin-bottom: 0.5em; + font-weight: bold; +} +div dd +{ + margin-left: 2em; + margin-bottom: 0.5em; +} + + +/* +p,pre,ul,ol,blockquote,h2,h3,h4,h5,h6,dl,table { + margin-left: 1em; + margin-right: 1em; +} +*/ + +p.subhead { font-weight: bold; margin-top: 2em; } + +.smaller { font-size: smaller } +.bigger { font-size: 130% } + +/* +td,th { padding: 0.2em } +*/ + +ul { + margin: 0.5em 1.5em 0.5em 1.5em; + padding: 0; +} + +ol { + margin: 0.5em 1.5em 0.5em 1.5em; + padding: 0; +} + +ul { list-style-type: square; } +ul ul { list-style-type: disc; } +ul ul ul { list-style-type: circle; } +ul ul ul ul { list-style-type: disc; } + +/* +ul li { + list-style: square; + margin: 0.1em 0em 0.6em 0; + padding: 0 0 0 0; + line-height: 140%; +} + +ol li { + margin: 0.1em 0em 0.6em 1.5em; + padding: 0 0 0 0px; + line-height: 140%; + list-style-type: decimal; +} + +li ul li { + font-size: 85%; + font-style: italic; + list-style-type: disc; + background: transparent; + padding: 0 0 0 0; +} +li li ul li { + font-size: 85%; + font-style: normal; + list-style-type: circle; + background: transparent; + padding: 0 0 0 0; +} +li li li ul li { + list-style-type: disc; + background: transparent; + padding: 0 0 0 0; +} + +li ol li { + list-style-type: decimal; +} + + +li li ol li { + list-style-type: decimal; +} +*/ + +/* + setting class="outline" on ol or ul makes it behave as an + ouline list where blocklevel content in li elements is + hidden by default and can be expanded or collapsed with + mouse click. Set class="expand" on li to override default +*/ + +ol.outline li:hover { cursor: pointer } +ol.outline li.nofold:hover { cursor: default } + +ul.outline li:hover { cursor: pointer } +ul.outline li.nofold:hover { cursor: default } + +ol.outline { list-style:decimal; } +ol.outline ol { list-style-type:lower-alpha } + +ol.outline li.nofold { + padding: 0 0 0 20px; + background: transparent url(../graphics/nofold-dim.gif) no-repeat 0px 0.5em; +} +ol.outline li.unfolded { + padding: 0 0 0 20px; + background: transparent url(../graphics/fold-dim.gif) no-repeat 0px 0.5em; +} +ol.outline li.folded { + padding: 0 0 0 20px; + background: transparent url(../graphics/unfold-dim.gif) no-repeat 0px 0.5em; +} +ol.outline li.unfolded:hover { + padding: 0 0 0 20px; + background: transparent url(../graphics/fold.gif) no-repeat 0px 0.5em; +} +ol.outline li.folded:hover { + padding: 0 0 0 20px; + background: transparent url(../graphics/unfold.gif) no-repeat 0px 0.5em; +} + +ul.outline li.nofold { + padding: 0 0 0 20px; + background: transparent url(../graphics/nofold-dim.gif) no-repeat 0px 0.5em; +} +ul.outline li.unfolded { + padding: 0 0 0 20px; + background: transparent url(../graphics/fold-dim.gif) no-repeat 0px 0.5em; +} +ul.outline li.folded { + padding: 0 0 0 20px; + background: transparent url(../graphics/unfold-dim.gif) no-repeat 0px 0.5em; +} +ul.outline li.unfolded:hover { + padding: 0 0 0 20px; + background: transparent url(../graphics/fold.gif) no-repeat 0px 0.5em; +} +ul.outline li.folded:hover { + padding: 0 0 0 20px; + background: transparent url(../graphics/unfold.gif) no-repeat 0px 0.5em; +} + +/* for slides with class "title" in table of contents */ +a.titleslide { font-weight: bold; font-style: italic } + +/* + hide images for work around for save as bug + where browsers fail to save images used by CSS +*/ +img.hidden { display: none; visibility: hidden } +div.initial_prompt { display: none; visibility: hidden } + + div.slide { + visibility: visible; + position: inherit; + } + div.handout { + border-top-style: solid; + border-top-width: thin; + border-top-color: black; + } + +@media screen { + .hidden { display: none; visibility: visible } + + div.slide.hidden { display: block; visibility: visible } + div.handout.hidden { display: block; visibility: visible } + div.background { display: none; visibility: hidden } + body.single_slide div.initial_prompt { display: block; visibility: visible } + body.single_slide div.background { display: block; visibility: visible } + body.single_slide div.background.hidden { display: none; visibility: hidden } + body.single_slide .invisible { visibility: hidden } + body.single_slide .hidden { display: none; visibility: hidden } + body.single_slide div.slide { position: absolute } + body.single_slide div.handout { display: none; visibility: hidden } +} + +@media print { + .hidden { display: block; visibility: visible } + +/* + div.slide pre { font-size: 60%; padding-left: 0.5em; } +*/ + div.toolbar { display: none; visibility: hidden; } + div.slidy_toc { display: none; visibility: hidden; } + div.background { display: none; visibility: hidden; } + div.slide { page-break-before: always } + /* :first-child isn't reliable for print media */ + div.slide.first-slide { page-break-before: avoid } +} + + +/* SJR: AsciiDoc slidy backend tweaks */ + +ol, ul { + margin: 0.8em 1.5em 0.8em 1.8em; +} +li > ul, li > ol { + margin-top: 0.5em; +} + +.outline > li.folded, +.outline > li.unfolded { + color: #527bbd; +} +ul > li{ color: #aaa; } +ul > li > *, ol > li > * { color: black; } + +li { + margin-top: 0.5em; + margin-bottom: 0.5em; +} diff --git a/libnl/doc/stylesheets/toc2.css b/libnl/doc/stylesheets/toc2.css new file mode 100644 index 0000000..a1e368b --- /dev/null +++ b/libnl/doc/stylesheets/toc2.css @@ -0,0 +1,34 @@ +@media screen { + body { + max-width: 50em; /* approximately 80 characters wide */ + margin-left: 16em; + } + + #toc { + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: 13em; + padding: 0.5em; + padding-bottom: 1.5em; + margin: 0; + overflow: auto; + border-right: 3px solid #f8f8f8; + } + + #toc .toclevel1 { + margin-top: 0.5em; + } + + #toc .toclevel2 { + margin-top: 0.25em; + display: list-item; + /* OLD color: #aaaaaa; */ + color: #990000; + } + + #toctitle { + margin-top: 0.5em; + } +} diff --git a/libnl/doc/stylesheets/volnitsky-manpage.css b/libnl/doc/stylesheets/volnitsky-manpage.css new file mode 100644 index 0000000..75a2dda --- /dev/null +++ b/libnl/doc/stylesheets/volnitsky-manpage.css @@ -0,0 +1 @@ +/* Empty placeholder file */ diff --git a/libnl/doc/stylesheets/volnitsky.css b/libnl/doc/stylesheets/volnitsky.css new file mode 100644 index 0000000..b6c4a15 --- /dev/null +++ b/libnl/doc/stylesheets/volnitsky.css @@ -0,0 +1,435 @@ +/* + * AsciiDoc 'volnitsky' theme for xhtml11 and html5 backends. + * Based on css from http://volnitsky.com, which was in turn based on default + * theme from AsciiDoc + * + * FIXME: The stlying is still a bit rough in places. + * + */ + +/* Default font. */ +body { + font-family: Georgia,"Times New Roman",Times,serif; +} + +/* Title font. */ +h1, h2, h3, h4, h5, h6, +div.title, caption.title, +thead, p.table.header, +#toctitle, +#author, #revnumber, #revdate, #revremark, +#footer { + font-family: Candara,Arial,sans-serif; +} + + +#toc a { + border-bottom: 1px dotted #999999; + color: #3A3A4D !important; + text-decoration: none !important; +} +#toc a:hover { + border-bottom: 1px solid #6D4100; + color: #6D4100 !important; + text-decoration: none !important; +} +a { color: #666688; text-decoration: none; border-bottom: 1px dotted #666688; } +a:visited { color: #615FA0; border-bottom: 1px dotted #615FA0; } +a:hover { color: #6D4100; border-bottom: 1px solid #6D4100; } + +em { + font-style: italic; + color: #444466; +} + +strong { + font-weight: bold; + color: #444466; +} + +h1, h2, h3, h4, h5, h6 { + color: #666688; + margin-bottom: 0.5em; + line-height: 1.3; + letter-spacing:+0.15em; +} + +h1, h2, h3 { border-bottom: 2px solid #ccd; } +h2 { padding-top: 0.5em; } +h3 { float: left; } +h3 + * { clear: left; } + +div.sectionbody { + margin-left: 0; +} + +hr { + border: 1px solid #444466; +} + +p { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +ul, ol, li > p { + margin-top: 0; +} + +pre { + padding: 0; + margin: 0; +} + +#author { + color: #444466; + font-weight: bold; + font-size: 1.1em; +} + +#footer { + font-size: small; + border-top: 2px solid silver; + padding-top: 0.5em; + margin-top: 4.0em; +} + +#footer-text { + float: left; + padding-bottom: 0.5em; +} + +#footer-badges { + float: right; + padding-bottom: 0.5em; +} + +#preamble { + margin-top: 1.5em; + margin-bottom: 1.5em; +} + +div.tableblock, div.imageblock, div.exampleblock, div.verseblock, +div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock, +div.admonitionblock { + margin-top: 1.5em; + margin-bottom: 1.5em; +} + +div.admonitionblock { + margin-top: 2.5em; + margin-bottom: 2.5em; +} + +div.content { /* Block element content. */ + padding: 0; +} + +/* Block element titles. */ +div.title, caption.title { + color: #444466; + font-weight: bold; + text-align: left; + margin-top: 1.0em; + margin-bottom: 0.5em; +} +div.title + * { + margin-top: 0; +} + +td div.title:first-child { + margin-top: 0.0em; +} +div.content div.title:first-child { + margin-top: 0.0em; +} +div.content + div.title { + margin-top: 0.0em; +} + +div.sidebarblock > div.content { + background: #ffffee; + border: 1px solid silver; + padding: 0.5em; +} + +div.listingblock > div.content { + border: 1px solid silver; + background: #f4f4f4; + padding: 0.5em; +} + +div.quoteblock { + padding-left: 2.0em; + margin-right: 10%; +} +div.quoteblock > div.attribution { + padding-top: 0.5em; + text-align: right; +} + +div.verseblock { + padding-left: 2.0em; + margin-right: 10%; +} +div.verseblock > pre.content { + font-family: inherit; +} +div.verseblock > div.attribution { + padding-top: 0.75em; + text-align: left; +} +/* DEPRECATED: Pre version 8.2.7 verse style literal block. */ +div.verseblock + div.attribution { + text-align: left; +} + +div.admonitionblock .icon { + vertical-align: top; + font-size: 1.1em; + font-weight: bold; + text-decoration: underline; + color: #444466; + padding-right: 0.5em; +} +div.admonitionblock td.content { + padding-left: 0.5em; + border-left: 2px solid silver; +} + +div.exampleblock > div.content { + border-left: 2px solid silver; + padding: 0.5em; +} + +div.imageblock div.content { padding-left: 0; } +span.image img { border-style: none; } +a.image:visited { color: white; } + +dl { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +dt { + margin-top: 0.5em; + margin-bottom: 0; + font-style: normal; + color: #444466; +} +dd > *:first-child { + margin-top: 0.1em; +} + +ul, ol { + list-style-position: outside; +} +ol.arabic { + list-style-type: decimal; +} +ol.loweralpha { + list-style-type: lower-alpha; +} +ol.upperalpha { + list-style-type: upper-alpha; +} +ol.lowerroman { + list-style-type: lower-roman; +} +ol.upperroman { + list-style-type: upper-roman; +} + +div.compact ul, div.compact ol, +div.compact p, div.compact p, +div.compact div, div.compact div { + margin-top: 0.1em; + margin-bottom: 0.1em; +} + +div.tableblock > table { + border: 3px solid #444466; +} +thead { + font-weight: bold; + color: #444466; +} +tfoot { + font-weight: bold; +} +td > div.verse { + white-space: pre; +} +p.table { + margin-top: 0; +} +/* Because the table frame attribute is overriden by CSS in most browsers. */ +div.tableblock > table[frame="void"] { + border-style: none; +} +div.tableblock > table[frame="hsides"] { + border-left-style: none; + border-right-style: none; +} +div.tableblock > table[frame="vsides"] { + border-top-style: none; + border-bottom-style: none; +} + + +div.hdlist { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +div.hdlist tr { + padding-bottom: 15px; +} +dt.hdlist1.strong, td.hdlist1.strong { + font-weight: bold; +} +td.hdlist1 { + vertical-align: top; + font-style: normal; + padding-right: 0.8em; + color: #444466; +} +td.hdlist2 { + vertical-align: top; +} +div.hdlist.compact tr { + margin: 0; + padding-bottom: 0; +} + +.comment { + background: yellow; +} + +@media print { + #footer-badges { display: none; } +} + +#toctitle { + color: #666688; + font-size: 1.2em; + font-weight: bold; + margin-top: 1.0em; + margin-bottom: 0.1em; +} + +div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 { margin-top: 0; margin-bottom: 0; } +div.toclevel1 { margin-top: 0.3em; margin-left: 0; font-size: 1.0em; } +div.toclevel2 { margin-top: 0.25em; margin-left: 2em; font-size: 0.9em; } +div.toclevel3 { margin-left: 4em; font-size: 0.8em; } +div.toclevel4 { margin-left: 6em; font-size: 0.8em; } + +body { + margin: 1em 5%; + max-width: 55em; + padding-left: 0; + +} + +.monospaced, tt, div.listingblock > div.content { + font-family: Consolas, "Andale Mono", "Courier New", monospace; + color: #004400; + background: #f4f4f4; + max-width: 80em; + line-height: 1.2em; +} + +.paragraph p { + line-height: 1.5em; + margin-top: 1em; +} + +.paragraph p, li, dd, .content { max-width: 45em; } +.admonitionblock { max-width: 35em; } + +div.sectionbody div.ulist > ul > li { + list-style-type: square; + color: #aaa; +} + div.sectionbody div.ulist > ul > li > * { + color: black; + /*font-size: 50%;*/ + } + + +div.sectionbody div.ulist > ul > li div.ulist > ul > li { + color: #ccd ; +} + div.sectionbody div.ulist > ul > li div.ulist > ul > li > * { + color: black ; + } + +em { + font-style: normal ! important; + font-weight: bold ! important; + color: #662222 ! important; + letter-spacing:+0.08em ! important; +} + + +/* + * html5 specific + * + * */ + +table.tableblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +thead, p.tableblock.header { + font-weight: bold; + color: #666688; +} +p.tableblock { + margin-top: 0; +} +table.tableblock { + border-width: 3px; + border-spacing: 0px; + border-style: solid; + border-color: #444466; + border-collapse: collapse; +} +th.tableblock, td.tableblock { + border-width: 1px; + padding: 4px; + border-style: solid; + border-color: #444466; +} + +table.tableblock.frame-topbot { + border-left-style: hidden; + border-right-style: hidden; +} +table.tableblock.frame-sides { + border-top-style: hidden; + border-bottom-style: hidden; +} +table.tableblock.frame-none { + border-style: hidden; +} + +th.tableblock.halign-left, td.tableblock.halign-left { + text-align: left; +} +th.tableblock.halign-center, td.tableblock.halign-center { + text-align: center; +} +th.tableblock.halign-right, td.tableblock.halign-right { + text-align: right; +} + +th.tableblock.valign-top, td.tableblock.valign-top { + vertical-align: top; +} +th.tableblock.valign-middle, td.tableblock.valign-middle { + vertical-align: middle; +} +th.tableblock.valign-bottom, td.tableblock.valign-bottom { + vertical-align: bottom; +} + + diff --git a/libnl/doc/stylesheets/xhtml11-quirks.css b/libnl/doc/stylesheets/xhtml11-quirks.css new file mode 100644 index 0000000..b3b46d2 --- /dev/null +++ b/libnl/doc/stylesheets/xhtml11-quirks.css @@ -0,0 +1,43 @@ +/* Workarounds for IE6's broken and incomplete CSS2. */ + +div.sidebar-content { + background: #ffffee; + border: 1px solid silver; + padding: 0.5em; +} +div.sidebar-title, div.image-title { + color: #527bbd; + font-family: sans-serif; + font-weight: bold; + margin-top: 0.0em; + margin-bottom: 0.5em; +} + +div.listingblock div.content { + border: 1px solid silver; + background: #f4f4f4; + padding: 0.5em; +} + +div.quoteblock-attribution { + padding-top: 0.5em; + text-align: right; +} + +pre.verseblock-content { + font-family: inherit; +} +div.verseblock-attribution { + padding-top: 0.75em; + text-align: left; +} + +div.exampleblock-content { + border-left: 3px solid #dddddd; + padding-left: 0.5em; +} + +div.imageblock.latex div.image-title { margin-top: 0.5em; } + +/* IE6 sets dynamically generated links as visited. */ +div#toc a:visited { color: blue; } diff --git a/libnl/doc/stylesheets/xhtml11.css b/libnl/doc/stylesheets/xhtml11.css new file mode 100644 index 0000000..1e6bf5a --- /dev/null +++ b/libnl/doc/stylesheets/xhtml11.css @@ -0,0 +1,333 @@ +/* Debug borders */ +p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 { +/* + border: 1px solid red; +*/ +} + +body { + margin: 1em 5% 1em 5%; +} + +a { + color: blue; + text-decoration: underline; +} +a:visited { + color: fuchsia; +} + +em { + font-style: italic; + color: navy; +} + +strong { + font-weight: bold; + color: #083194; +} + +tt { + color: navy; +} + +h1, h2, h3, h4, h5, h6 { + color: #527bbd; + font-family: sans-serif; + margin-top: 1.2em; + margin-bottom: 0.5em; + line-height: 1.3; +} + +h1, h2, h3 { + border-bottom: 2px solid silver; +} +h2 { + padding-top: 0.5em; +} +h3 { + float: left; +} +h3 + * { + clear: left; +} + +div.sectionbody { + font-family: serif; + margin-left: 0; +} + +hr { + border: 1px solid silver; +} + +p { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +ul, ol, li > p { + margin-top: 0; +} + +pre { + padding: 0; + margin: 0; +} + +span#author { + color: #527bbd; + font-family: sans-serif; + font-weight: bold; + font-size: 1.1em; +} +span#email { +} +span#revnumber, span#revdate, span#revremark { + font-family: sans-serif; +} + +div#footer { + font-family: sans-serif; + font-size: small; + border-top: 2px solid silver; + padding-top: 0.5em; + margin-top: 4.0em; +} +div#footer-text { + float: left; + padding-bottom: 0.5em; +} +div#footer-badges { + float: right; + padding-bottom: 0.5em; +} + +div#preamble { + margin-top: 1.5em; + margin-bottom: 1.5em; +} +div.tableblock, div.imageblock, div.exampleblock, div.verseblock, +div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock, +div.admonitionblock { + margin-top: 1.5em; + margin-bottom: 1.5em; +} +div.admonitionblock { + margin-top: 2.5em; + margin-bottom: 2.5em; +} + +div.content { /* Block element content. */ + padding: 0; +} + +/* Block element titles. */ +div.title, caption.title { + color: #527bbd; + font-family: sans-serif; + font-weight: bold; + text-align: left; + margin-top: 1.0em; + margin-bottom: 0.5em; +} +div.title + * { + margin-top: 0; +} + +td div.title:first-child { + margin-top: 0.0em; +} +div.content div.title:first-child { + margin-top: 0.0em; +} +div.content + div.title { + margin-top: 0.0em; +} + +div.sidebarblock > div.content { + background: #ffffee; + border: 1px solid silver; + padding: 0.5em; +} + +div.listingblock > div.content { + border: 1px solid silver; + background: #f4f4f4; + padding: 0.5em; +} + +div.quoteblock { + padding-left: 2.0em; + margin-right: 10%; +} +div.quoteblock > div.attribution { + padding-top: 0.5em; + text-align: right; +} + +div.verseblock { + padding-left: 2.0em; + margin-right: 10%; +} +div.verseblock > div.content { + white-space: pre; +} +div.verseblock > div.attribution { + padding-top: 0.75em; + text-align: left; +} +/* DEPRECATED: Pre version 8.2.7 verse style literal block. */ +div.verseblock + div.attribution { + text-align: left; +} + +div.admonitionblock .icon { + vertical-align: top; + font-size: 1.1em; + font-weight: bold; + text-decoration: underline; + color: #527bbd; + padding-right: 0.5em; +} +div.admonitionblock td.content { + padding-left: 0.5em; + border-left: 2px solid silver; +} + +div.exampleblock > div.content { + border-left: 2px solid silver; + padding: 0.5em; +} + +div.imageblock div.content { padding-left: 0; } +span.image img { border-style: none; } +a.image:visited { color: white; } + +dl { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +dt { + margin-top: 0.5em; + margin-bottom: 0; + font-style: normal; + color: navy; +} +dd > *:first-child { + margin-top: 0.1em; +} + +ul, ol { + list-style-position: outside; +} +ol.arabic { + list-style-type: decimal; +} +ol.loweralpha { + list-style-type: lower-alpha; +} +ol.upperalpha { + list-style-type: upper-alpha; +} +ol.lowerroman { + list-style-type: lower-roman; +} +ol.upperroman { + list-style-type: upper-roman; +} + +div.compact ul, div.compact ol, +div.compact p, div.compact p, +div.compact div, div.compact div { + margin-top: 0.1em; + margin-bottom: 0.1em; +} + +div.tableblock > table { + border: 3px solid #527bbd; +} +thead { + font-family: sans-serif; + font-weight: bold; +} +tfoot { + font-weight: bold; +} +td > div.verse { + white-space: pre; +} +p.table { + margin-top: 0; +} +/* Because the table frame attribute is overriden by CSS in most browsers. */ +div.tableblock > table[frame="void"] { + border-style: none; +} +div.tableblock > table[frame="hsides"] { + border-left-style: none; + border-right-style: none; +} +div.tableblock > table[frame="vsides"] { + border-top-style: none; + border-bottom-style: none; +} + + +div.hdlist { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +div.hdlist tr { + padding-bottom: 15px; +} +dt.hdlist1.strong, td.hdlist1.strong { + font-weight: bold; +} +td.hdlist1 { + vertical-align: top; + font-style: normal; + padding-right: 0.8em; + color: navy; +} +td.hdlist2 { + vertical-align: top; +} +div.hdlist.compact tr { + margin: 0; + padding-bottom: 0; +} + +.comment { + background: yellow; +} + +@media print { + div#footer-badges { display: none; } +} + +div#toctitle { + color: #527bbd; + font-family: sans-serif; + font-size: 1.1em; + font-weight: bold; + margin-top: 1.0em; + margin-bottom: 0.1em; +} + +div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 { + margin-top: 0; + margin-bottom: 0; +} +div.toclevel2 { + margin-left: 2em; + font-size: 0.9em; +} +div.toclevel3 { + margin-left: 4em; + font-size: 0.9em; +} +div.toclevel4 { + margin-left: 6em; + font-size: 0.9em; +} diff --git a/libnl/etc/classid b/libnl/etc/classid new file mode 100644 index 0000000..2203243 --- /dev/null +++ b/libnl/etc/classid @@ -0,0 +1,45 @@ +############################################################################### +# +# ClassID <-> Name Translation Table +# +# This file can be used to assign names to classids for easier reference +# in all libnl tools. +# +# Format: +# # qdisc definition +# # class deifnition +# # class definition referencing an +# existing qdisc definition. +# +# Example: +# 1: top # top -> 1:0 +# top:1 interactive # interactive -> 1:1 +# top:2 www # www -> 1:2 +# top:3 bulk # bulk -> 1:3 +# 2:1 test_class # test_class -> 2:1 +# +# Illegal Example: +# 30:1 classD +# classD:2 invalidClass # classD refers to a class, not a qdisc +# +############################################################################### + +# + +# Reserved default classids +0:0 none +ffff:ffff root +ffff:fff1 ingress + +# +# List your classid definitions here: +# + + + +############################################################################### +# List of auto-generated classids +# +# DO NOT ADD CLASSID DEFINITIONS BELOW THIS LINE +# +# diff --git a/libnl/etc/pktloc b/libnl/etc/pktloc new file mode 100644 index 0000000..8559161 --- /dev/null +++ b/libnl/etc/pktloc @@ -0,0 +1,76 @@ +# +# Location definitions for packet matching +# + +# name alignment offset mask shift +ip.version u8 net+0 0xF0 4 +ip.hdrlen u8 net+0 0x0F +ip.diffserv u8 net+1 +ip.length u16 net+2 +ip.id u16 net+4 +ip.flag.res u8 net+6 0xff 7 +ip.df u8 net+6 0x40 6 +ip.mf u8 net+6 0x20 5 +ip.offset u16 net+6 0x1FFF +ip.ttl u8 net+8 +ip.proto u8 net+9 +ip.chksum u16 net+10 +ip.src u32 net+12 +ip.dst u32 net+16 + +# if ip.ihl > 5 +ip.opts u32 net+20 + + +# +# IP version 6 +# +# name alignment offset mask shift +ip6.version u8 net+0 0xF0 4 +ip6.tc u16 net+0 0xFF0 4 +ip6.flowlabel u32 net+0 0xFFFFF +ip6.length u16 net+4 +ip6.nexthdr u8 net+6 +ip6.hoplimit u8 net+7 +ip6.src 16 net+8 +ip6.dst 16 net+24 + +# +# Transmission Control Protocol (TCP) +# +# name alignment offset mask shift +tcp.sport u16 tcp+0 +tcp.dport u16 tcp+2 +tcp.seq u32 tcp+4 +tcp.ack u32 tcp+8 + +# Data offset (4 bits) +tcp.off u8 tcp+12 0xF0 4 + +# Reserved [0 0 0] (3 bits) +tcp.reserved u8 tcp+12 0x04 1 + +# ECN [N C E] (3 bits) +tcp.ecn u16 tcp+12 0x01C00 6 + +# Individual TCP flags (0|1) (6 bits in total) +tcp.flag.urg u8 tcp+13 0x20 5 +tcp.flag.ack u8 tcp+13 0x10 4 +tcp.flag.psh u8 tcp+13 0x08 3 +tcp.flag.rst u8 tcp+13 0x04 2 +tcp.flag.syn u8 tcp+13 0x02 1 +tcp.flag.fin u8 tcp+13 0x01 + +tcp.win u16 tcp+14 +tcp.csum u16 tcp+16 +tcp.urg u16 tcp+18 +tcp.opts u32 tcp+20 + +# +# User Datagram Protocol (UDP) +# +# name alignment offset mask shift +udp.sport u16 tcp+0 +udp.dport u16 tcp+2 +udp.length u16 tcp+4 +udp.csum u16 tcp+6 diff --git a/libnl/include/base/README.md b/libnl/include/base/README.md new file mode 100644 index 0000000..3da1621 --- /dev/null +++ b/libnl/include/base/README.md @@ -0,0 +1,13 @@ +include/base +============ + +This directory contains libnl-independent helper. + +It's an internal "library" of helpers, that purely depend +on C and external dependencies. + +Currently, it's also header-only, so there is no need to link +with anything special. + +This can be used freely by all our internal code, but it's private API, +so it cannot be used in public headers. diff --git a/libnl/include/base/nl-base-utils.h b/libnl/include/base/nl-base-utils.h new file mode 100644 index 0000000..a735134 --- /dev/null +++ b/libnl/include/base/nl-base-utils.h @@ -0,0 +1,874 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef __NETLINK_BASE_NL_BASE_UTILS_H__ +#define __NETLINK_BASE_NL_BASE_UTILS_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef DISABLE_PTHREADS +#include +#endif + +/*****************************************************************************/ + +#if __BYTE_ORDER == __BIG_ENDIAN +#define ntohll(x) (x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define ntohll(x) bswap_64((x)) +#endif +#define htonll(x) ntohll(x) + +/*****************************************************************************/ + +#define _NL_STRINGIFY_ARG(contents) #contents +#define _NL_STRINGIFY(macro_or_string) _NL_STRINGIFY_ARG(macro_or_string) + +/*****************************************************************************/ + +#if defined(__GNUC__) +#define _NL_PRAGMA_WARNING_DO(warning) \ + _NL_STRINGIFY(GCC diagnostic ignored warning) +#elif defined(__clang__) +#define _NL_PRAGMA_WARNING_DO(warning) \ + _NL_STRINGIFY(clang diagnostic ignored warning) +#endif + +/* you can only suppress a specific warning that the compiler + * understands. Otherwise you will get another compiler warning + * about invalid pragma option. + * It's not that bad however, because gcc and clang often have the + * same name for the same warning. */ + +#if defined(__GNUC__) && \ + (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +#define _NL_PRAGMA_WARNING_DISABLE(warning) \ + _Pragma("GCC diagnostic push") \ + _Pragma(_NL_PRAGMA_WARNING_DO("-Wpragmas")) \ + _Pragma(_NL_PRAGMA_WARNING_DO(warning)) +#elif defined(__clang__) +#define _NL_PRAGMA_WARNING_DISABLE(warning) \ + _Pragma("clang diagnostic push") \ + _Pragma(_NL_PRAGMA_WARNING_DO("-Wunknown-warning-option")) \ + _Pragma(_NL_PRAGMA_WARNING_DO(warning)) +#else +#define _NL_PRAGMA_WARNING_DISABLE(warning) +#endif + +#if defined(__GNUC__) && \ + (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +#define _NL_PRAGMA_WARNING_REENABLE _Pragma("GCC diagnostic pop") +#elif defined(__clang__) +#define _NL_PRAGMA_WARNING_REENABLE _Pragma("clang diagnostic pop") +#else +#define _NL_PRAGMA_WARNING_REENABLE +#endif + +/*****************************************************************************/ + +#define _nl_packed __attribute__((__packed__)) +#define _nl_unused __attribute__((__unused__)) +#define _nl_always_inline __attribute__((__always_inline__)) +#define _nl_used __attribute__((__used__)) +#define _nl_pure __attribute__((__pure__)) +#define _nl_const __attribute__((__const__)) +#define _nl_noreturn __attribute__((__noreturn__)) +#define _nl_warn_unused_result __attribute__((__warn_unused_result__)) +#define _nl_printf(a, b) __attribute__((__format__(__printf__, a, b))) +#define _nl_align(s) __attribute__((__aligned__(s))) +#define _nl_section(s) __attribute__((__section__(s))) +#define _nl_alignof(type) __alignof(type) +#define _nl_alignas(type) _nl_align(_nl_alignof(type)) +#define _nl_deprecated(msg) __attribute__((__deprecated__(msg))) +#define _nl_init __attribute__((constructor)) +#define _nl_exit __attribute__((destructor)) +#define _nl_auto(fcn) __attribute__((__cleanup__(fcn))) + +/*****************************************************************************/ + +#ifdef thread_local +#define _nl_thread_local thread_local +/* + * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__ + * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769 + */ +#elif __STDC_VERSION__ >= 201112L && \ + !(defined(__STDC_NO_THREADS__) || \ + (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && \ + __GLIBC_MINOR__ < 16)) +#define _nl_thread_local _Thread_local +#else +#define _nl_thread_local __thread +#endif + +/*****************************************************************************/ + +#define _NL_STATIC_ASSERT(cond) ((void)sizeof(char[(cond) ? 1 : -1])) + +/*****************************************************************************/ + +#if defined(NL_MORE_ASSERTS) && NL_MORE_ASSERTS > 0 +#define _nl_assert(cond) assert(cond) +#else +#define _nl_assert(cond) \ + do { \ + if (0) { \ + assert(cond); \ + } \ + } while (0) +#endif + +#define _nl_assert_not_reached() assert(0) + +/*****************************************************************************/ + +#define _NL_BIT(n) (1ull << (n)) + +/*****************************************************************************/ + +#define _NL_PASTE_ARGS(identifier1, identifier2) identifier1##identifier2 +#define _NL_PASTE(identifier1, identifier2) \ + _NL_PASTE_ARGS(identifier1, identifier2) + +/* Taken from systemd's UNIQ_T and UNIQ macros. */ + +#define _NL_UNIQ_T(x, uniq) _NL_PASTE(__unique_prefix_, _NL_PASTE(x, uniq)) +#define _NL_UNIQ __COUNTER__ + +/*****************************************************************************/ + +#define _nl_assert_addr_family_or_unspec(addr_family) \ + do { \ + typeof(addr_family) _addr_family = (addr_family); \ + \ + _nl_assert(_addr_family == AF_UNSPEC || \ + _addr_family == AF_INET || \ + _addr_family == AF_INET6); \ + } while (0) + +#define _nl_assert_addr_family(addr_family) \ + do { \ + typeof(addr_family) _addr_family = (addr_family); \ + \ + _nl_assert(_addr_family == AF_INET || \ + _addr_family == AF_INET6); \ + } while (0) + +/*****************************************************************************/ + +#define _NL_SWAP(pa, pb) \ + do { \ + typeof(*(pa)) *_pa = (pa); \ + typeof(*(pb)) *_pb = (pb); \ + typeof(*_pa) _tmp; \ + \ + _nl_assert(_pa); \ + _nl_assert(_pb); \ + _tmp = *_pa; \ + *_pa = *_pb; \ + *_pb = _tmp; \ + } while (0) + +/*****************************************************************************/ + +#define _NL_N_ELEMENTS(arr) (sizeof(arr) / sizeof((arr)[0])) + +#define ARRAY_SIZE(arr) _NL_N_ELEMENTS(arr) + +/*****************************************************************************/ + +/* This is also defined in stddef.h */ +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER) +#endif + +/*****************************************************************************/ + +static inline uintptr_t _nl_ptr_to_uintptr(const void *p) +{ + /* in C, pointers can only be compared (with less-than or greater-than) under certain + * circumstances. Since uintptr_t is supposed to be able to represent the pointer + * as a plain integer and also support to convert the integer back to the pointer, + * it should be safer to compare the pointers directly. + * + * Of course, this function isn't very useful beyond that its use makes it clear + * that we want to compare pointers by value, which otherwise may not be valid. */ + return (uintptr_t)p; +} + +/*****************************************************************************/ + +static inline int _nl_strcmp0(const char *s1, const char *s2) +{ + int c; + + /* like g_strcmp0(), but this is inlinable. + * + * Also, it is guaranteed to return either -1, 0, or 1. */ + if (s1 == s2) + return 0; + if (!s1) + return -1; + if (!s2) + return 1; + c = strcmp(s1, s2); + if (c < 0) + return -1; + if (c > 0) + return 1; + return 0; +} + +static inline bool _nl_streq(const char *a, const char *b) +{ + return !strcmp(a, b); +} + +static inline bool _nl_streq0(const char *a, const char *b) +{ + return a == b || (a && b && _nl_streq(a, b)); +} + +static inline int _nl_memcmp(const void *s1, const void *s2, size_t n) +{ + /* Workaround undefined behavior in memcmp() with NULL pointers. */ + if (n == 0) + return 0; + _nl_assert(s1); + _nl_assert(s2); + return memcmp(s1, s2, n); +} + +static inline bool _nl_memeq(const void *s1, const void *s2, size_t len) +{ + return _nl_memcmp(s1, s2, len) == 0; +} + +static inline void *_nl_memcpy(void *restrict dest, const void *restrict src, + size_t n) +{ + /* Workaround undefined behavior in memcpy() with NULL pointers. */ + if (n == 0) + return dest; + + _nl_assert(src); + return memcpy(dest, src, n); +} + +/*****************************************************************************/ + +#define _NL_INT_IS_SIGNED(arg) (!(((typeof(arg))-1) > 0)) + +#define _NL_INT_SAME_SIGNEDNESS(arg1, arg2) \ + (_NL_INT_IS_SIGNED(arg1) == _NL_INT_IS_SIGNED(arg2)) + +/*****************************************************************************/ + +/* glib's MIN()/MAX() macros don't have function-like behavior, in that they evaluate + * the argument possibly twice. + * + * Taken from systemd's MIN()/MAX() macros. */ + +#define _NL_MIN(a, b) __NL_MIN(_NL_UNIQ, a, _NL_UNIQ, b) +#define __NL_MIN(aq, a, bq, b) \ + ({ \ + typeof(a) _NL_UNIQ_T(A, aq) = (a); \ + typeof(b) _NL_UNIQ_T(B, bq) = (b); \ + \ + _NL_STATIC_ASSERT(_NL_INT_SAME_SIGNEDNESS(_NL_UNIQ_T(A, aq), \ + _NL_UNIQ_T(B, bq))); \ + \ + ((_NL_UNIQ_T(A, aq) < _NL_UNIQ_T(B, bq)) ? _NL_UNIQ_T(A, aq) : \ + _NL_UNIQ_T(B, bq)); \ + }) + +#define _NL_MAX(a, b) __NL_MAX(_NL_UNIQ, a, _NL_UNIQ, b) +#define __NL_MAX(aq, a, bq, b) \ + ({ \ + typeof(a) _NL_UNIQ_T(A, aq) = (a); \ + typeof(b) _NL_UNIQ_T(B, bq) = (b); \ + \ + _NL_STATIC_ASSERT(_NL_INT_SAME_SIGNEDNESS(_NL_UNIQ_T(A, aq), \ + _NL_UNIQ_T(B, bq))); \ + \ + ((_NL_UNIQ_T(A, aq) > _NL_UNIQ_T(B, bq)) ? _NL_UNIQ_T(A, aq) : \ + _NL_UNIQ_T(B, bq)); \ + }) + +#define _NL_CLAMP(x, low, high) \ + __NL_CLAMP(_NL_UNIQ, x, _NL_UNIQ, low, _NL_UNIQ, high) +#define __NL_CLAMP(xq, x, lowq, low, highq, high) \ + ({ \ + typeof(x) _NL_UNIQ_T(X, xq) = (x); \ + typeof(low) _NL_UNIQ_T(LOW, lowq) = (low); \ + typeof(high) _NL_UNIQ_T(HIGH, highq) = (high); \ + \ + _NL_STATIC_ASSERT(_NL_INT_SAME_SIGNEDNESS( \ + _NL_UNIQ_T(X, xq), _NL_UNIQ_T(LOW, lowq))); \ + _NL_STATIC_ASSERT(_NL_INT_SAME_SIGNEDNESS( \ + _NL_UNIQ_T(X, xq), _NL_UNIQ_T(HIGH, highq))); \ + \ + ((_NL_UNIQ_T(X, xq) > _NL_UNIQ_T(HIGH, highq)) ? \ + _NL_UNIQ_T(HIGH, highq) : \ + (_NL_UNIQ_T(X, xq) < _NL_UNIQ_T(LOW, lowq)) ? \ + _NL_UNIQ_T(LOW, lowq) : \ + _NL_UNIQ_T(X, xq)); \ + }) + +#define _NL_MAX_WITH_CMP(cmp, a, b) \ + ({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + \ + (((cmp(_a, _b)) >= 0) ? _a : _b); \ + }) + +/* evaluates to (void) if _A or _B are not constant or of different types */ +#define _NL_CONST_MAX(_A, _B) \ + (__builtin_choose_expr( \ + (__builtin_constant_p(_A) && __builtin_constant_p(_B) && \ + __builtin_types_compatible_p(typeof(_A), typeof(_B))), \ + ((_A) > (_B)) ? (_A) : (_B), ((void)0))) + +/*****************************************************************************/ + +#define _NL_CMP_RETURN(c) \ + do { \ + const int _cc = (c); \ + \ + if (_cc) \ + return _cc < 0 ? -1 : 1; \ + } while (0) + +#define _NL_CMP_RETURN_DIRECT(c) \ + /* Usually we want that our CMP functions return strictly + * -1, 0, or 1. _NL_CMP_RETURN_DIRECT() is like _NL_CMP_RETURN(), + * except, it does not clamp the integer value. */ \ + do { \ + const int _cc = (c); \ + \ + if (_cc) \ + return _cc; \ + } while (0) + +#define _NL_CMP_SELF(a, b) \ + do { \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + \ + if (_a == _b) \ + return 0; \ + if (!_a) \ + return -1; \ + if (!_b) \ + return 1; \ + } while (0) + +/*****************************************************************************/ + +#define _NL_CMP_DIRECT(a, b) \ + do { \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + \ + _NL_STATIC_ASSERT(_NL_INT_SAME_SIGNEDNESS(_a, _b)); \ + \ + if (_a != _b) \ + return (_a < _b) ? -1 : 1; \ + } while (0) + +#define _NL_CMP_DIRECT_UNSAFE(a, b) \ + /* This variant is "unsafe", because it evaluates the arguments more then once. + * This is only useful for bitfields, for which typeof() doesn't work. + * Don't use otherwise. */ \ + do { \ + if ((a) != (b)) \ + return ((a) < (b)) ? -1 : 1; \ + } while (0) + +/* In the general case, direct pointer comparison is undefined behavior in C. + * Avoid that by casting pointers to void* and then to uintptr_t. This comparison + * is not really meaningful, except that it provides some kind of stable sort order + * between pointers (that can otherwise not be compared). */ +#define _NL_CMP_DIRECT_PTR(a, b) \ + _NL_CMP_DIRECT(_nl_ptr_to_uintptr(a), _nl_ptr_to_uintptr(b)) + +#define _NL_CMP_DIRECT_BOOL(a, b) _NL_CMP_DIRECT(!!(a), !!(b)) + +#define _NL_CMP_DIRECT_MEMCMP(a, b, size) \ + _NL_CMP_RETURN(_nl_memcmp((a), (b), (size))) + +#define _NL_CMP_DIRECT_STRCMP(a, b) _NL_CMP_RETURN_DIRECT(strcmp((a), (b))) + +#define _NL_CMP_DIRECT_STRCMP0(a, b) \ + _NL_CMP_RETURN_DIRECT(_nl_strcmp0((a), (b))) + +#define _NL_CMP_DIRECT_STR_INTERNED(a, b) \ + /* This is interned strings, which are first checked for equality only using pointer + * comparison. Only in case of differences, the sort order is still determined by strcmp(). */ \ + do { \ + const char *const _a = (a); \ + const char *const _b = (b); \ + \ + if (_a != _b) \ + _NL_CMP_RETURN_DIRECT(_nl_strcmp0(_a, _b)); \ + } while (0) + +#define _NL_CMP_DIRECT_IN6ADDR(a, b) \ + do { \ + const struct in6_addr *const _a = (a); \ + const struct in6_addr *const _b = (b); \ + \ + _NL_CMP_RETURN(memcmp(_a, _b, sizeof(struct in6_addr))); \ + } while (0) + +/*****************************************************************************/ + +#define _NL_CMP_FIELD(a, b, field) _NL_CMP_DIRECT(((a)->field), ((b)->field)) + +#define _NL_CMP_FIELD_UNSAFE(a, b, field) \ + /* This variant is "unsafe", because it evaluates the arguments more then once. + * This is only useful for bitfields, for which typeof() doesn't work. + * Don't use otherwise. */ \ + _NL_CMP_DIRECT_UNSAFE(((a)->field), ((b)->field)) + +#define _NL_CMP_FIELD_BOOL(a, b, field) \ + _NL_CMP_DIRECT_BOOL(((a)->field), ((b)->field)) + +#define _NL_CMP_FIELD_STR(a, b, field) \ + _NL_CMP_DIRECT_STRCMP(((a)->field), ((b)->field)) + +#define _NL_CMP_FIELD_STR0(a, b, field) \ + _NL_CMP_DIRECT_STRCMP0(((a)->field), ((b)->field)) + +#define _NL_CMP_FIELD_STR_INTERNED(a, b, field) \ + _NL_CMP_DIRECT_STR_INTERNED(((a)->field), ((b)->field)) + +#define _NL_CMP_FIELD_MEMCMP_LEN(a, b, field, len) \ + _NL_CMP_DIRECT_MEMCMP(&((a)->field), &((b)->field), \ + _NL_MIN(len, sizeof((a)->field))) + +#define _NL_CMP_FIELD_MEMCMP(a, b, field) \ + _NL_CMP_DIRECT_MEMCMP(&((a)->field), &((b)->field), sizeof((a)->field)) + +#define _NL_CMP_FIELD_IN6ADDR(a, b, field) \ + _NL_CMP_DIRECT_IN6ADDR(&((a)->field), &((b)->field)) + +/*****************************************************************************/ + +/* internal macro to calculate the size of a struct @type up to (and including) @field. + * this will be used for .minlen policy fields, so that we require only a field of up + * to the given size. */ +#define _nl_offsetofend(type, field) \ + (offsetof(type, field) + sizeof(((type *)NULL)->field)) + +/*****************************************************************************/ + +#define _nl_clear_pointer(pp, destroy) \ + ({ \ + __typeof__(*(pp)) *_pp = (pp); \ + __typeof__(*_pp) _p; \ + int _changed = 0; \ + \ + if (_pp && (_p = *_pp)) { \ + _nl_unused const void *const _p_check_is_pointer = _p; \ + \ + *_pp = NULL; \ + \ + (destroy)(_p); \ + \ + _changed = 1; \ + } \ + _changed; \ + }) + +#define _nl_clear_free(pp) _nl_clear_pointer(pp, free) + +#define _nl_steal_pointer(pp) \ + ({ \ + __typeof__(*(pp)) *const _pp = (pp); \ + __typeof__(*_pp) _p = NULL; \ + \ + if (_pp && (_p = *_pp)) { \ + *_pp = NULL; \ + } \ + \ + _p; \ + }) + +/*****************************************************************************/ + +#define _nl_malloc_maybe_a(alloca_maxlen, bytes, to_free) \ + ({ \ + const size_t _bytes = (bytes); \ + __typeof__(to_free) _to_free = (to_free); \ + __typeof__(*_to_free) _ptr; \ + \ + _NL_STATIC_ASSERT((alloca_maxlen) <= 500); \ + _nl_assert(_to_free && !*_to_free); \ + \ + if (_bytes <= (alloca_maxlen)) { \ + _ptr = alloca(_bytes); \ + } else { \ + _ptr = malloc(_bytes); \ + *_to_free = _ptr; \ + }; \ + \ + _ptr; \ + }) + +/*****************************************************************************/ + +static inline char *_nl_strncpy_trunc(char *dst, const char *src, size_t len) +{ + /* we don't use/reimplement strlcpy(), because we want the fill-all-with-NUL + * behavior of strncpy(). This is just strncpy() with gracefully handling truncation + * (and disabling the "-Wstringop-truncation" warning). + * + * Note that truncation is silently accepted. + */ + + _NL_PRAGMA_WARNING_DISABLE("-Wstringop-truncation"); + _NL_PRAGMA_WARNING_DISABLE("-Wstringop-overflow"); + + if (len > 0) { + _nl_assert(dst); + _nl_assert(src); + + strncpy(dst, src, len); + + dst[len - 1] = '\0'; + } + + _NL_PRAGMA_WARNING_REENABLE; + _NL_PRAGMA_WARNING_REENABLE; + + return dst; +} + +static inline char *_nl_strncpy_assert(char *dst, const char *src, size_t len) +{ + /* we don't use/reimplement strlcpy(), because we want the fill-all-with-NUL + * behavior of strncpy(). This is just strncpy() with assertion against truncation + * (and disabling the "-Wstringop-truncation" warning). + * + * Note that truncation is still a bug and there is an _nl_assert() + * against that. + */ + + _NL_PRAGMA_WARNING_DISABLE("-Wstringop-truncation"); + _NL_PRAGMA_WARNING_DISABLE("-Wstringop-overflow"); + + if (len > 0) { + _nl_assert(dst); + _nl_assert(src); + + strncpy(dst, src, len); + + _nl_assert(dst[len - 1] == '\0'); + + dst[len - 1] = '\0'; + } + + _NL_PRAGMA_WARNING_REENABLE; + _NL_PRAGMA_WARNING_REENABLE; + + return dst; +} + +#define _NL_RETURN_ON_ERR(cmd) \ + do { \ + int _err; \ + \ + _err = (cmd); \ + if (_err < 0) \ + return _err; \ + } while (0) + +#define _NL_RETURN_E_ON_ERR(e, cmd) \ + do { \ + int _err; \ + \ + _err = (cmd); \ + if (_err < 0) { \ + _NL_STATIC_ASSERT((e) > 0); \ + return -(e); \ + } \ + } while (0) + +/* _NL_RETURN_ON_PUT_ERR() shall only be used with a put command (nla_put or nlmsg_append). + * These commands can either fail with a regular error code (which gets propagated) + * or with -NLE_NOMEM. However, they don't really try to allocate memory, so we don't + * want to propagate -NLE_NOMEM. Instead, we coerce such failure to -NLE_MSGSIZE. */ +#define _NL_RETURN_ON_PUT_ERR(put_cmd) \ + do { \ + int _err; \ + \ + _err = (put_cmd); \ + if (_err < 0) { \ + if (_err == -NLE_NOMEM) { \ + /* nla_put() returns -NLE_NOMEM in case of out of buffer size. We don't + * want to propagate that error and map it to -NLE_MSGSIZE. */ \ + return -NLE_MSGSIZE; \ + } \ + /* any other error can only be due to invalid parameters. Propagate the + * error, however also assert that it cannot be reached. */ \ + _nl_assert_not_reached(); \ + return _err; \ + } else \ + _nl_assert(_err == 0); \ + } while (0) + +static inline int _nl_close(int fd) +{ + int r; + + r = close(fd); + _nl_assert(r == 0 || fd < 0 || errno != EBADF); + return r; +} + +static inline void *_nl_memdup(const void *ptr, size_t len) +{ + void *p; + + if (len == 0) { + /* malloc() leaves it implementation defined whether to return NULL. + * Callers rely on returning NULL if len is zero. */ + return NULL; + } + + p = malloc(len); + if (!p) + return NULL; + memcpy(p, ptr, len); + return p; +} + +#define _nl_memdup_ptr(ptr) ((__typeof__(ptr))_nl_memdup((ptr), sizeof(*(ptr)))) + +/*****************************************************************************/ + +static inline size_t _nl_addr_family_to_size(int addr_family) +{ + if (addr_family == AF_INET) + return sizeof(in_addr_t); + if (addr_family == AF_INET6) + return sizeof(struct in6_addr); + return 0; +} + +/*****************************************************************************/ + +typedef union { + in_addr_t addr4; + struct in_addr a4; + struct in6_addr a6; +} _NLIPAddr; + +static inline char *_nl_inet_ntop(int addr_family, const void *addr, char *buf) +{ + char *r; + + _nl_assert_addr_family(addr_family); + _nl_assert(addr); + + /* inet_ntop() is documented to fail, but if we pass a known address family + * and a suitably large buffer, it cannot. Assert for that. */ + + r = (char *)inet_ntop(addr_family, addr, buf, + (addr_family == AF_INET) ? INET_ADDRSTRLEN : + INET6_ADDRSTRLEN); + _nl_assert(r == buf); + _nl_assert(strlen(r) < ((addr_family == AF_INET) ? INET_ADDRSTRLEN : + INET6_ADDRSTRLEN)); + + return r; +} + +static inline char *_nl_inet_ntop_dup(int addr_family, const void *addr) +{ + return (char *)_nl_inet_ntop(addr_family, addr, + malloc((addr_family == AF_INET) ? + INET_ADDRSTRLEN : + INET6_ADDRSTRLEN)); +} + +/*****************************************************************************/ + +static inline size_t _nl_ptrarray_len_(const void *const *ptr, ssize_t len) +{ + size_t l = 0; + + if (len >= 0) + return len; + if (ptr) { + while (ptr[l]) + l++; + } + return l; +} + +#define _nl_ptrarray_len(ptr, len) \ + ({ \ + typeof(*(ptr)) *const _ptr = (ptr); \ + \ + _nl_ptrarray_len_((const void *const *)_ptr, (len)); \ + }) + +/*****************************************************************************/ + +#define _NL_AUTO_DEFINE_FCN_VOID0(CastType, name, func) \ + static inline void name(void *v) \ + { \ + if (*((CastType *)v)) \ + func(*((CastType *)v)); \ + } \ + struct _nl_dummy_for_tailing_semicolon + +#define _NL_AUTO_DEFINE_FCN_STRUCT(CastType, name, func) \ + static inline void name(CastType *v) \ + { \ + if (v) \ + func(v); \ + } \ + struct _nl_dummy_for_tailing_semicolon + +#define _NL_AUTO_DEFINE_FCN_TYPED0(CastType, name, func) \ + static inline void name(CastType *v) \ + { \ + if (*v) \ + func(*v); \ + } \ + struct _nl_dummy_for_tailing_semicolon + +#define _NL_AUTO_DEFINE_FCN_INDIRECT0(CastType, name, func) \ + static inline void name(CastType *v) \ + { \ + if (*v) \ + func(v); \ + } \ + struct _nl_dummy_for_tailing_semicolon + +#define _nl_auto_free _nl_auto(_nl_auto_free_fcn) +_NL_AUTO_DEFINE_FCN_VOID0(void *, _nl_auto_free_fcn, free); + +/*****************************************************************************/ + +#define _nl_swap(p_a, p_b) \ + do { \ + typeof(*(p_a)) *const _p_a = (p_a); \ + typeof(*(p_a)) *const _p_b = (p_b); \ + typeof(*(p_a)) _tmp; \ + \ + _tmp = *_p_a; \ + *_p_a = *_p_b; \ + *_p_b = _tmp; \ + } while (0) + +/*****************************************************************************/ + +#define NSEC_PER_SEC 1000000000L + +struct trans_tbl { + uint64_t i; + const char *a; +}; + +#define __ADD(id, name) { .i = id, .a = #name } + +#define BUG() \ + do { \ + fprintf(stderr, "BUG at file position %s:%d:%s\n", __FILE__, \ + __LINE__, __func__); \ + assert(0); \ + } while (0) + +#define BUG_ON(condition) \ + do { \ + if (condition) \ + BUG(); \ + } while (0) + +#define APPBUG(msg) \ + do { \ + fprintf(stderr, "APPLICATION BUG: %s:%d:%s: %s\n", __FILE__, \ + __LINE__, __func__, msg); \ + assert(0); \ + } while (0) + +/*****************************************************************************/ + +#ifndef DISABLE_PTHREADS +#define NL_LOCK(NAME) pthread_mutex_t(NAME) = PTHREAD_MUTEX_INITIALIZER +#define NL_RW_LOCK(NAME) pthread_rwlock_t(NAME) = PTHREAD_RWLOCK_INITIALIZER + +static inline void nl_lock(pthread_mutex_t *lock) +{ + pthread_mutex_lock(lock); +} + +static inline void nl_unlock(pthread_mutex_t *lock) +{ + pthread_mutex_unlock(lock); +} + +static inline void nl_read_lock(pthread_rwlock_t *lock) +{ + pthread_rwlock_rdlock(lock); +} + +static inline void nl_read_unlock(pthread_rwlock_t *lock) +{ + pthread_rwlock_unlock(lock); +} + +static inline void nl_write_lock(pthread_rwlock_t *lock) +{ + pthread_rwlock_wrlock(lock); +} + +static inline void nl_write_unlock(pthread_rwlock_t *lock) +{ + pthread_rwlock_unlock(lock); +} + +#else +#define NL_LOCK(NAME) int __unused_lock_##NAME _nl_unused +#define NL_RW_LOCK(NAME) int __unused_lock_##NAME _nl_unused + +#define nl_lock(LOCK) \ + do { \ + } while (0) +#define nl_unlock(LOCK) \ + do { \ + } while (0) +#define nl_read_lock(LOCK) \ + do { \ + } while (0) +#define nl_read_unlock(LOCK) \ + do { \ + } while (0) +#define nl_write_lock(LOCK) \ + do { \ + } while (0) +#define nl_write_unlock(LOCK) \ + do { \ + } while (0) +#endif + +#endif /* __NETLINK_BASE_NL_BASE_UTILS_H__ */ diff --git a/libnl/include/linux-private/linux/atm.h b/libnl/include/linux-private/linux/atm.h new file mode 100644 index 0000000..e33ff6b --- /dev/null +++ b/libnl/include/linux-private/linux/atm.h @@ -0,0 +1,242 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* atm.h - general ATM declarations */ + +/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ + + +/* + * WARNING: User-space programs should not #include directly. + * Instead, #include + */ + +#ifndef _LINUX_ATM_H +#define _LINUX_ATM_H + +/* + * BEGIN_xx and END_xx markers are used for automatic generation of + * documentation. Do not change them. + */ + + +#include +#include +#include +#include + + +/* general ATM constants */ +#define ATM_CELL_SIZE 53 /* ATM cell size incl. header */ +#define ATM_CELL_PAYLOAD 48 /* ATM payload size */ +#define ATM_AAL0_SDU 52 /* AAL0 SDU size */ +#define ATM_MAX_AAL34_PDU 65535 /* maximum AAL3/4 PDU payload */ +#define ATM_AAL5_TRAILER 8 /* AAL5 trailer size */ +#define ATM_MAX_AAL5_PDU 65535 /* maximum AAL5 PDU payload */ +#define ATM_MAX_CDV 9999 /* maximum (default) CDV */ +#define ATM_NOT_RSV_VCI 32 /* first non-reserved VCI value */ + +#define ATM_MAX_VPI 255 /* maximum VPI at the UNI */ +#define ATM_MAX_VPI_NNI 4096 /* maximum VPI at the NNI */ +#define ATM_MAX_VCI 65535 /* maximum VCI */ + + +/* "protcol" values for the socket system call */ +#define ATM_NO_AAL 0 /* AAL not specified */ +#define ATM_AAL0 13 /* "raw" ATM cells */ +#define ATM_AAL1 1 /* AAL1 (CBR) */ +#define ATM_AAL2 2 /* AAL2 (VBR) */ +#define ATM_AAL34 3 /* AAL3/4 (data) */ +#define ATM_AAL5 5 /* AAL5 (data) */ + +/* + * socket option name coding functions + * + * Note that __SO_ENCODE and __SO_LEVEL are somewhat a hack since the + * << 22 only reserves 9 bits for the level. On some architectures + * SOL_SOCKET is 0xFFFF, so that's a bit of a problem + */ + +#define __SO_ENCODE(l,n,t) ((((l) & 0x1FF) << 22) | ((n) << 16) | \ + sizeof(t)) +#define __SO_LEVEL_MATCH(c,m) (((c) >> 22) == ((m) & 0x1FF)) +#define __SO_NUMBER(c) (((c) >> 16) & 0x3f) +#define __SO_SIZE(c) ((c) & 0x3fff) + +/* + * ATM layer + */ + +#define SO_SETCLP __SO_ENCODE(SOL_ATM,0,int) + /* set CLP bit value - TODO */ +#define SO_CIRANGE __SO_ENCODE(SOL_ATM,1,struct atm_cirange) + /* connection identifier range; socket must be + bound or connected */ +#define SO_ATMQOS __SO_ENCODE(SOL_ATM,2,struct atm_qos) + /* Quality of Service setting */ +#define SO_ATMSAP __SO_ENCODE(SOL_ATM,3,struct atm_sap) + /* Service Access Point */ +#define SO_ATMPVC __SO_ENCODE(SOL_ATM,4,struct sockaddr_atmpvc) + /* "PVC" address (also for SVCs); get only */ +#define SO_MULTIPOINT __SO_ENCODE(SOL_ATM, 5, int) + /* make this vc a p2mp */ + + +/* + * Note @@@: since the socket layers don't really distinguish the control and + * the data plane but generally seems to be data plane-centric, any layer is + * about equally wrong for the SAP. If you have a better idea about this, + * please speak up ... + */ + + +/* ATM cell header (for AAL0) */ + +/* BEGIN_CH */ +#define ATM_HDR_GFC_MASK 0xf0000000 +#define ATM_HDR_GFC_SHIFT 28 +#define ATM_HDR_VPI_MASK 0x0ff00000 +#define ATM_HDR_VPI_SHIFT 20 +#define ATM_HDR_VCI_MASK 0x000ffff0 +#define ATM_HDR_VCI_SHIFT 4 +#define ATM_HDR_PTI_MASK 0x0000000e +#define ATM_HDR_PTI_SHIFT 1 +#define ATM_HDR_CLP 0x00000001 +/* END_CH */ + + +/* PTI codings */ + +/* BEGIN_PTI */ +#define ATM_PTI_US0 0 /* user data cell, congestion not exp, SDU-type 0 */ +#define ATM_PTI_US1 1 /* user data cell, congestion not exp, SDU-type 1 */ +#define ATM_PTI_UCES0 2 /* user data cell, cong. experienced, SDU-type 0 */ +#define ATM_PTI_UCES1 3 /* user data cell, cong. experienced, SDU-type 1 */ +#define ATM_PTI_SEGF5 4 /* segment OAM F5 flow related cell */ +#define ATM_PTI_E2EF5 5 /* end-to-end OAM F5 flow related cell */ +#define ATM_PTI_RSV_RM 6 /* reserved for traffic control/resource mgmt */ +#define ATM_PTI_RSV 7 /* reserved */ +/* END_PTI */ + + +/* + * The following items should stay in linux/atm.h, which should be linked to + * netatm/atm.h + */ + +/* Traffic description */ + +#define ATM_NONE 0 /* no traffic */ +#define ATM_UBR 1 +#define ATM_CBR 2 +#define ATM_VBR 3 +#define ATM_ABR 4 +#define ATM_ANYCLASS 5 /* compatible with everything */ + +#define ATM_MAX_PCR -1 /* maximum available PCR */ + +struct atm_trafprm { + unsigned char traffic_class; /* traffic class (ATM_UBR, ...) */ + int max_pcr; /* maximum PCR in cells per second */ + int pcr; /* desired PCR in cells per second */ + int min_pcr; /* minimum PCR in cells per second */ + int max_cdv; /* maximum CDV in microseconds */ + int max_sdu; /* maximum SDU in bytes */ + /* extra params for ABR */ + unsigned int icr; /* Initial Cell Rate (24-bit) */ + unsigned int tbe; /* Transient Buffer Exposure (24-bit) */ + unsigned int frtt : 24; /* Fixed Round Trip Time (24-bit) */ + unsigned int rif : 4; /* Rate Increment Factor (4-bit) */ + unsigned int rdf : 4; /* Rate Decrease Factor (4-bit) */ + unsigned int nrm_pres :1; /* nrm present bit */ + unsigned int trm_pres :1; /* rm present bit */ + unsigned int adtf_pres :1; /* adtf present bit */ + unsigned int cdf_pres :1; /* cdf present bit*/ + unsigned int nrm :3; /* Max # of Cells for each forward RM cell (3-bit) */ + unsigned int trm :3; /* Time between forward RM cells (3-bit) */ + unsigned int adtf :10; /* ACR Decrease Time Factor (10-bit) */ + unsigned int cdf :3; /* Cutoff Decrease Factor (3-bit) */ + unsigned int spare :9; /* spare bits */ +}; + +struct atm_qos { + struct atm_trafprm txtp; /* parameters in TX direction */ + struct atm_trafprm rxtp __ATM_API_ALIGN; + /* parameters in RX direction */ + unsigned char aal __ATM_API_ALIGN; +}; + +/* PVC addressing */ + +#define ATM_ITF_ANY -1 /* "magic" PVC address values */ +#define ATM_VPI_ANY -1 +#define ATM_VCI_ANY -1 +#define ATM_VPI_UNSPEC -2 +#define ATM_VCI_UNSPEC -2 + + +struct sockaddr_atmpvc { + unsigned short sap_family; /* address family, AF_ATMPVC */ + struct { /* PVC address */ + short itf; /* ATM interface */ + short vpi; /* VPI (only 8 bits at UNI) */ + int vci; /* VCI (only 16 bits at UNI) */ + } sap_addr __ATM_API_ALIGN; /* PVC address */ +}; + +/* SVC addressing */ + +#define ATM_ESA_LEN 20 /* ATM End System Address length */ +#define ATM_E164_LEN 12 /* maximum E.164 number length */ + +#define ATM_AFI_DCC 0x39 /* DCC ATM Format */ +#define ATM_AFI_ICD 0x47 /* ICD ATM Format */ +#define ATM_AFI_E164 0x45 /* E.164 ATM Format */ +#define ATM_AFI_LOCAL 0x49 /* Local ATM Format */ + +#define ATM_AFI_DCC_GROUP 0xBD /* DCC ATM Group Format */ +#define ATM_AFI_ICD_GROUP 0xC5 /* ICD ATM Group Format */ +#define ATM_AFI_E164_GROUP 0xC3 /* E.164 ATM Group Format */ +#define ATM_AFI_LOCAL_GROUP 0xC7 /* Local ATM Group Format */ + +#define ATM_LIJ_NONE 0 /* no leaf-initiated join */ +#define ATM_LIJ 1 /* request joining */ +#define ATM_LIJ_RPJ 2 /* set to root-prompted join */ +#define ATM_LIJ_NJ 3 /* set to network join */ + + +struct sockaddr_atmsvc { + unsigned short sas_family; /* address family, AF_ATMSVC */ + struct { /* SVC address */ + unsigned char prv[ATM_ESA_LEN];/* private ATM address */ + char pub[ATM_E164_LEN+1]; /* public address (E.164) */ + /* unused addresses must be bzero'ed */ + char lij_type; /* role in LIJ call; one of ATM_LIJ* */ + __u32 lij_id; /* LIJ call identifier */ + } sas_addr __ATM_API_ALIGN; /* SVC address */ +}; + + +static __inline__ int atmsvc_addr_in_use(struct sockaddr_atmsvc addr) +{ + return *addr.sas_addr.prv || *addr.sas_addr.pub; +} + + +static __inline__ int atmpvc_addr_in_use(struct sockaddr_atmpvc addr) +{ + return addr.sap_addr.itf || addr.sap_addr.vpi || addr.sap_addr.vci; +} + + +/* + * Some stuff for linux/sockios.h + */ + +struct atmif_sioc { + int number; + int length; + void *arg; +}; + + +typedef unsigned short atm_backend_t; +#endif /* _LINUX_ATM_H */ diff --git a/libnl/include/linux-private/linux/atmapi.h b/libnl/include/linux-private/linux/atmapi.h new file mode 100644 index 0000000..c9bf5c2 --- /dev/null +++ b/libnl/include/linux-private/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/libnl/include/linux-private/linux/atmioc.h b/libnl/include/linux-private/linux/atmioc.h new file mode 100644 index 0000000..a9030bc --- /dev/null +++ b/libnl/include/linux-private/linux/atmioc.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* atmioc.h - ranges for ATM-related ioctl numbers */ + +/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */ + + +/* + * See https://icawww1.epfl.ch/linux-atm/magic.html for the complete list of + * "magic" ioctl numbers. + */ + + +#ifndef _LINUX_ATMIOC_H +#define _LINUX_ATMIOC_H + +#include + /* everybody including atmioc.h will also need _IO{,R,W,WR} */ + +#define ATMIOC_PHYCOM 0x00 /* PHY device common ioctls, globally unique */ +#define ATMIOC_PHYCOM_END 0x0f +#define ATMIOC_PHYTYP 0x10 /* PHY dev type ioctls, unique per PHY type */ +#define ATMIOC_PHYTYP_END 0x2f +#define ATMIOC_PHYPRV 0x30 /* PHY dev private ioctls, unique per driver */ +#define ATMIOC_PHYPRV_END 0x4f +#define ATMIOC_SARCOM 0x50 /* SAR device common ioctls, globally unique */ +#define ATMIOC_SARCOM_END 0x50 +#define ATMIOC_SARPRV 0x60 /* SAR dev private ioctls, unique per driver */ +#define ATMIOC_SARPRV_END 0x7f +#define ATMIOC_ITF 0x80 /* Interface ioctls, globally unique */ +#define ATMIOC_ITF_END 0x8f +#define ATMIOC_BACKEND 0x90 /* ATM generic backend ioctls, u. per backend */ +#define ATMIOC_BACKEND_END 0xaf +/* 0xb0-0xbf: Reserved for future use */ +#define ATMIOC_AREQUIPA 0xc0 /* Application requested IP over ATM, glob. u. */ +#define ATMIOC_LANE 0xd0 /* LAN Emulation, globally unique */ +#define ATMIOC_MPOA 0xd8 /* MPOA, globally unique */ +#define ATMIOC_CLIP 0xe0 /* Classical IP over ATM control, globally u. */ +#define ATMIOC_CLIP_END 0xef +#define ATMIOC_SPECIAL 0xf0 /* Special-purpose controls, globally unique */ +#define ATMIOC_SPECIAL_END 0xff + +#endif diff --git a/libnl/include/linux-private/linux/atmsap.h b/libnl/include/linux-private/linux/atmsap.h new file mode 100644 index 0000000..fc05248 --- /dev/null +++ b/libnl/include/linux-private/linux/atmsap.h @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* atmsap.h - ATM Service Access Point addressing definitions */ + +/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */ + + +#ifndef _LINUX_ATMSAP_H +#define _LINUX_ATMSAP_H + +#include + +/* + * BEGIN_xx and END_xx markers are used for automatic generation of + * documentation. Do not change them. + */ + + +/* + * Layer 2 protocol identifiers + */ + +/* BEGIN_L2 */ +#define ATM_L2_NONE 0 /* L2 not specified */ +#define ATM_L2_ISO1745 0x01 /* Basic mode ISO 1745 */ +#define ATM_L2_Q291 0x02 /* ITU-T Q.291 (Rec. I.441) */ +#define ATM_L2_X25_LL 0x06 /* ITU-T X.25, link layer */ +#define ATM_L2_X25_ML 0x07 /* ITU-T X.25, multilink */ +#define ATM_L2_LAPB 0x08 /* Extended LAPB, half-duplex (Rec. T.71) */ +#define ATM_L2_HDLC_ARM 0x09 /* HDLC ARM (ISO/IEC 4335) */ +#define ATM_L2_HDLC_NRM 0x0a /* HDLC NRM (ISO/IEC 4335) */ +#define ATM_L2_HDLC_ABM 0x0b /* HDLC ABM (ISO/IEC 4335) */ +#define ATM_L2_ISO8802 0x0c /* LAN LLC (ISO/IEC 8802/2) */ +#define ATM_L2_X75 0x0d /* ITU-T X.75, SLP */ +#define ATM_L2_Q922 0x0e /* ITU-T Q.922 */ +#define ATM_L2_USER 0x10 /* user-specified */ +#define ATM_L2_ISO7776 0x11 /* ISO 7776 DTE-DTE */ +/* END_L2 */ + + +/* + * Layer 3 protocol identifiers + */ + +/* BEGIN_L3 */ +#define ATM_L3_NONE 0 /* L3 not specified */ +#define ATM_L3_X25 0x06 /* ITU-T X.25, packet layer */ +#define ATM_L3_ISO8208 0x07 /* ISO/IEC 8208 */ +#define ATM_L3_X223 0x08 /* ITU-T X.223 | ISO/IEC 8878 */ +#define ATM_L3_ISO8473 0x09 /* ITU-T X.233 | ISO/IEC 8473 */ +#define ATM_L3_T70 0x0a /* ITU-T T.70 minimum network layer */ +#define ATM_L3_TR9577 0x0b /* ISO/IEC TR 9577 */ +#define ATM_L3_H310 0x0c /* ITU-T Recommendation H.310 */ +#define ATM_L3_H321 0x0d /* ITU-T Recommendation H.321 */ +#define ATM_L3_USER 0x10 /* user-specified */ +/* END_L3 */ + + +/* + * High layer identifiers + */ + +/* BEGIN_HL */ +#define ATM_HL_NONE 0 /* HL not specified */ +#define ATM_HL_ISO 0x01 /* ISO */ +#define ATM_HL_USER 0x02 /* user-specific */ +#define ATM_HL_HLP 0x03 /* high layer profile - UNI 3.0 only */ +#define ATM_HL_VENDOR 0x04 /* vendor-specific application identifier */ +/* END_HL */ + + +/* + * ITU-T coded mode of operation + */ + +/* BEGIN_IMD */ +#define ATM_IMD_NONE 0 /* mode not specified */ +#define ATM_IMD_NORMAL 1 /* normal mode of operation */ +#define ATM_IMD_EXTENDED 2 /* extended mode of operation */ +/* END_IMD */ + +/* + * H.310 code points + */ + +#define ATM_TT_NONE 0 /* terminal type not specified */ +#define ATM_TT_RX 1 /* receive only */ +#define ATM_TT_TX 2 /* send only */ +#define ATM_TT_RXTX 3 /* receive and send */ + +#define ATM_MC_NONE 0 /* no multiplexing */ +#define ATM_MC_TS 1 /* transport stream (TS) */ +#define ATM_MC_TS_FEC 2 /* transport stream with forward error corr. */ +#define ATM_MC_PS 3 /* program stream (PS) */ +#define ATM_MC_PS_FEC 4 /* program stream with forward error corr. */ +#define ATM_MC_H221 5 /* ITU-T Rec. H.221 */ + +/* + * SAP structures + */ + +#define ATM_MAX_HLI 8 /* maximum high-layer information length */ + + +struct atm_blli { + unsigned char l2_proto; /* layer 2 protocol */ + union { + struct { + unsigned char mode; /* mode of operation (ATM_IMD_xxx), 0 if */ + /* absent */ + unsigned char window; /* window size (k), 1-127 (0 to omit) */ + } itu; /* ITU-T encoding */ + unsigned char user; /* user-specified l2 information */ + } l2; + unsigned char l3_proto; /* layer 3 protocol */ + union { + struct { + unsigned char mode; /* mode of operation (ATM_IMD_xxx), 0 if */ + /* absent */ + unsigned char def_size; /* default packet size (log2), 4-12 (0 to */ + /* omit) */ + unsigned char window;/* packet window size, 1-127 (0 to omit) */ + } itu; /* ITU-T encoding */ + unsigned char user; /* user specified l3 information */ + struct { /* if l3_proto = ATM_L3_H310 */ + unsigned char term_type; /* terminal type */ + unsigned char fw_mpx_cap; /* forward multiplexing capability */ + /* only if term_type != ATM_TT_NONE */ + unsigned char bw_mpx_cap; /* backward multiplexing capability */ + /* only if term_type != ATM_TT_NONE */ + } h310; + struct { /* if l3_proto = ATM_L3_TR9577 */ + unsigned char ipi; /* initial protocol id */ + unsigned char snap[5];/* IEEE 802.1 SNAP identifier */ + /* (only if ipi == NLPID_IEEE802_1_SNAP) */ + } tr9577; + } l3; +} __ATM_API_ALIGN; + + +struct atm_bhli { + unsigned char hl_type; /* high layer information type */ + unsigned char hl_length; /* length (only if hl_type == ATM_HL_USER || */ + /* hl_type == ATM_HL_ISO) */ + unsigned char hl_info[ATM_MAX_HLI];/* high layer information */ +}; + + +#define ATM_MAX_BLLI 3 /* maximum number of BLLI elements */ + + +struct atm_sap { + struct atm_bhli bhli; /* local SAP, high-layer information */ + struct atm_blli blli[ATM_MAX_BLLI] __ATM_API_ALIGN; + /* local SAP, low-layer info */ +}; + + +static __inline__ int blli_in_use(struct atm_blli blli) +{ + return blli.l2_proto || blli.l3_proto; +} + +#endif diff --git a/libnl/include/linux-private/linux/byteorder/little_endian.h b/libnl/include/linux-private/linux/byteorder/little_endian.h new file mode 100644 index 0000000..ba6c199 --- /dev/null +++ b/libnl/include/linux-private/linux/byteorder/little_endian.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_BYTEORDER_LITTLE_ENDIAN_H +#define _LINUX_BYTEORDER_LITTLE_ENDIAN_H + +#ifndef __LITTLE_ENDIAN +#define __LITTLE_ENDIAN 1234 +#endif +#ifndef __LITTLE_ENDIAN_BITFIELD +#define __LITTLE_ENDIAN_BITFIELD +#endif + +#include +#include +#include + +#define __constant_htonl(x) ((__be32)___constant_swab32((x))) +#define __constant_ntohl(x) ___constant_swab32((__be32)(x)) +#define __constant_htons(x) ((__be16)___constant_swab16((x))) +#define __constant_ntohs(x) ___constant_swab16((__be16)(x)) +#define __constant_cpu_to_le64(x) ((__le64)(__u64)(x)) +#define __constant_le64_to_cpu(x) ((__u64)(__le64)(x)) +#define __constant_cpu_to_le32(x) ((__le32)(__u32)(x)) +#define __constant_le32_to_cpu(x) ((__u32)(__le32)(x)) +#define __constant_cpu_to_le16(x) ((__le16)(__u16)(x)) +#define __constant_le16_to_cpu(x) ((__u16)(__le16)(x)) +#define __constant_cpu_to_be64(x) ((__be64)___constant_swab64((x))) +#define __constant_be64_to_cpu(x) ___constant_swab64((__u64)(__be64)(x)) +#define __constant_cpu_to_be32(x) ((__be32)___constant_swab32((x))) +#define __constant_be32_to_cpu(x) ___constant_swab32((__u32)(__be32)(x)) +#define __constant_cpu_to_be16(x) ((__be16)___constant_swab16((x))) +#define __constant_be16_to_cpu(x) ___constant_swab16((__u16)(__be16)(x)) +#define __cpu_to_le64(x) ((__le64)(__u64)(x)) +#define __le64_to_cpu(x) ((__u64)(__le64)(x)) +#define __cpu_to_le32(x) ((__le32)(__u32)(x)) +#define __le32_to_cpu(x) ((__u32)(__le32)(x)) +#define __cpu_to_le16(x) ((__le16)(__u16)(x)) +#define __le16_to_cpu(x) ((__u16)(__le16)(x)) +#define __cpu_to_be64(x) ((__be64)__swab64((x))) +#define __be64_to_cpu(x) __swab64((__u64)(__be64)(x)) +#define __cpu_to_be32(x) ((__be32)__swab32((x))) +#define __be32_to_cpu(x) __swab32((__u32)(__be32)(x)) +#define __cpu_to_be16(x) ((__be16)__swab16((x))) +#define __be16_to_cpu(x) __swab16((__u16)(__be16)(x)) + +static __always_inline __le64 __cpu_to_le64p(const __u64 *p) +{ + return (__le64)*p; +} +static __always_inline __u64 __le64_to_cpup(const __le64 *p) +{ + return (__u64)*p; +} +static __always_inline __le32 __cpu_to_le32p(const __u32 *p) +{ + return (__le32)*p; +} +static __always_inline __u32 __le32_to_cpup(const __le32 *p) +{ + return (__u32)*p; +} +static __always_inline __le16 __cpu_to_le16p(const __u16 *p) +{ + return (__le16)*p; +} +static __always_inline __u16 __le16_to_cpup(const __le16 *p) +{ + return (__u16)*p; +} +static __always_inline __be64 __cpu_to_be64p(const __u64 *p) +{ + return (__be64)__swab64p(p); +} +static __always_inline __u64 __be64_to_cpup(const __be64 *p) +{ + return __swab64p((__u64 *)p); +} +static __always_inline __be32 __cpu_to_be32p(const __u32 *p) +{ + return (__be32)__swab32p(p); +} +static __always_inline __u32 __be32_to_cpup(const __be32 *p) +{ + return __swab32p((__u32 *)p); +} +static __always_inline __be16 __cpu_to_be16p(const __u16 *p) +{ + return (__be16)__swab16p(p); +} +static __always_inline __u16 __be16_to_cpup(const __be16 *p) +{ + return __swab16p((__u16 *)p); +} +#define __cpu_to_le64s(x) do { (void)(x); } while (0) +#define __le64_to_cpus(x) do { (void)(x); } while (0) +#define __cpu_to_le32s(x) do { (void)(x); } while (0) +#define __le32_to_cpus(x) do { (void)(x); } while (0) +#define __cpu_to_le16s(x) do { (void)(x); } while (0) +#define __le16_to_cpus(x) do { (void)(x); } while (0) +#define __cpu_to_be64s(x) __swab64s((x)) +#define __be64_to_cpus(x) __swab64s((x)) +#define __cpu_to_be32s(x) __swab32s((x)) +#define __be32_to_cpus(x) __swab32s((x)) +#define __cpu_to_be16s(x) __swab16s((x)) +#define __be16_to_cpus(x) __swab16s((x)) + + +#endif /* _LINUX_BYTEORDER_LITTLE_ENDIAN_H */ diff --git a/libnl/include/linux-private/linux/can/netlink.h b/libnl/include/linux-private/linux/can/netlink.h new file mode 100644 index 0000000..8ec98c2 --- /dev/null +++ b/libnl/include/linux-private/linux/can/netlink.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * linux/can/netlink.h + * + * Definitions for the CAN netlink interface + * + * Copyright (c) 2009 Wolfgang Grandegger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CAN_NETLINK_H +#define _CAN_NETLINK_H + +#include + +/* + * CAN bit-timing parameters + * + * For further information, please read chapter "8 BIT TIMING + * REQUIREMENTS" of the "Bosch CAN Specification version 2.0" + * at http://www.semiconductors.bosch.de/pdf/can2spec.pdf. + */ +struct can_bittiming { + __u32 bitrate; /* Bit-rate in bits/second */ + __u32 sample_point; /* Sample point in one-tenth of a percent */ + __u32 tq; /* Time quanta (TQ) in nanoseconds */ + __u32 prop_seg; /* Propagation segment in TQs */ + __u32 phase_seg1; /* Phase buffer segment 1 in TQs */ + __u32 phase_seg2; /* Phase buffer segment 2 in TQs */ + __u32 sjw; /* Synchronisation jump width in TQs */ + __u32 brp; /* Bit-rate prescaler */ +}; + +/* + * CAN hardware-dependent bit-timing constant + * + * Used for calculating and checking bit-timing parameters + */ +struct can_bittiming_const { + char name[16]; /* Name of the CAN controller hardware */ + __u32 tseg1_min; /* Time segment 1 = prop_seg + phase_seg1 */ + __u32 tseg1_max; + __u32 tseg2_min; /* Time segment 2 = phase_seg2 */ + __u32 tseg2_max; + __u32 sjw_max; /* Synchronisation jump width */ + __u32 brp_min; /* Bit-rate prescaler */ + __u32 brp_max; + __u32 brp_inc; +}; + +/* + * CAN clock parameters + */ +struct can_clock { + __u32 freq; /* CAN system clock frequency in Hz */ +}; + +/* + * CAN operational and error states + */ +enum can_state { + CAN_STATE_ERROR_ACTIVE = 0, /* RX/TX error count < 96 */ + CAN_STATE_ERROR_WARNING, /* RX/TX error count < 128 */ + CAN_STATE_ERROR_PASSIVE, /* RX/TX error count < 256 */ + CAN_STATE_BUS_OFF, /* RX/TX error count >= 256 */ + CAN_STATE_STOPPED, /* Device is stopped */ + CAN_STATE_SLEEPING, /* Device is sleeping */ + CAN_STATE_MAX +}; + +/* + * CAN bus error counters + */ +struct can_berr_counter { + __u16 txerr; + __u16 rxerr; +}; + +/* + * CAN controller mode + */ +struct can_ctrlmode { + __u32 mask; + __u32 flags; +}; + +#define CAN_CTRLMODE_LOOPBACK 0x01 /* Loopback mode */ +#define CAN_CTRLMODE_LISTENONLY 0x02 /* Listen-only mode */ +#define CAN_CTRLMODE_3_SAMPLES 0x04 /* Triple sampling mode */ +#define CAN_CTRLMODE_ONE_SHOT 0x08 /* One-Shot mode */ +#define CAN_CTRLMODE_BERR_REPORTING 0x10 /* Bus-error reporting */ +#define CAN_CTRLMODE_FD 0x20 /* CAN FD mode */ +#define CAN_CTRLMODE_PRESUME_ACK 0x40 /* Ignore missing CAN ACKs */ +#define CAN_CTRLMODE_FD_NON_ISO 0x80 /* CAN FD in non-ISO mode */ +#define CAN_CTRLMODE_CC_LEN8_DLC 0x100 /* Classic CAN DLC option */ +#define CAN_CTRLMODE_TDC_AUTO 0x200 /* CAN transiver automatically calculates TDCV */ +#define CAN_CTRLMODE_TDC_MANUAL 0x400 /* TDCV is manually set up by user */ + +/* + * 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_TDC, + IFLA_CAN_CTRLMODE_EXT, + + /* add new constants above here */ + __IFLA_CAN_MAX, + IFLA_CAN_MAX = __IFLA_CAN_MAX - 1 +}; + +/* + * CAN FD Transmitter Delay Compensation (TDC) + * + * Please refer to struct can_tdc_const and can_tdc in + * include/linux/can/bittiming.h for further details. + */ +enum { + IFLA_CAN_TDC_UNSPEC, + IFLA_CAN_TDC_TDCV_MIN, /* u32 */ + IFLA_CAN_TDC_TDCV_MAX, /* u32 */ + IFLA_CAN_TDC_TDCO_MIN, /* u32 */ + IFLA_CAN_TDC_TDCO_MAX, /* u32 */ + IFLA_CAN_TDC_TDCF_MIN, /* u32 */ + IFLA_CAN_TDC_TDCF_MAX, /* u32 */ + IFLA_CAN_TDC_TDCV, /* u32 */ + IFLA_CAN_TDC_TDCO, /* u32 */ + IFLA_CAN_TDC_TDCF, /* u32 */ + + /* add new constants above here */ + __IFLA_CAN_TDC, + IFLA_CAN_TDC_MAX = __IFLA_CAN_TDC - 1 +}; + +/* + * IFLA_CAN_CTRLMODE_EXT nest: controller mode extended parameters + */ +enum { + IFLA_CAN_CTRLMODE_UNSPEC, + IFLA_CAN_CTRLMODE_SUPPORTED, /* u32 */ + + /* add new constants above here */ + __IFLA_CAN_CTRLMODE, + IFLA_CAN_CTRLMODE_MAX = __IFLA_CAN_CTRLMODE - 1 +}; + +/* u16 termination range: 1..65535 Ohms */ +#define CAN_TERMINATION_DISABLED 0 + +#endif /* !_UAPI_CAN_NETLINK_H */ diff --git a/libnl/include/linux-private/linux/const.h b/libnl/include/linux-private/linux/const.h new file mode 100644 index 0000000..1eb84b5 --- /dev/null +++ b/libnl/include/linux-private/linux/const.h @@ -0,0 +1,36 @@ +/* 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)) + +#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_CONST_H */ diff --git a/libnl/include/linux-private/linux/errno.h b/libnl/include/linux-private/linux/errno.h new file mode 100644 index 0000000..70f2bd3 --- /dev/null +++ b/libnl/include/linux-private/linux/errno.h @@ -0,0 +1 @@ +#include diff --git a/libnl/include/linux-private/linux/ethtool.h b/libnl/include/linux-private/linux/ethtool.h new file mode 100644 index 0000000..1d0731b --- /dev/null +++ b/libnl/include/linux-private/linux/ethtool.h @@ -0,0 +1,2206 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * ethtool.h: Defines for Linux ethtool. + * + * Copyright (C) 1998 David S. Miller (davem@redhat.com) + * Copyright 2001 Jeff Garzik + * Portions Copyright 2001 Sun Microsystems (thockin@sun.com) + * Portions Copyright 2002 Intel (eli.kupermann@intel.com, + * christopher.leech@intel.com, + * scott.feldman@intel.com) + * Portions Copyright (C) Sun Microsystems 2008 + */ + +#ifndef _LINUX_ETHTOOL_H +#define _LINUX_ETHTOOL_H + +#include +#include +#include + +#include /* for INT_MAX */ + +/* All structures exposed to userland should be defined such that they + * have the same layout for 32-bit and 64-bit userland. + */ + +/* Note on reserved space. + * Reserved fields must not be accessed directly by user space because + * they may be replaced by a different field in the future. They must + * be initialized to zero before making the request, e.g. via memset + * of the entire structure or implicitly by not being set in a structure + * initializer. + */ + +/** + * struct ethtool_cmd - DEPRECATED, link control and status + * This structure is DEPRECATED, please use struct ethtool_link_settings. + * @cmd: Command number = %ETHTOOL_GSET or %ETHTOOL_SSET + * @supported: Bitmask of %SUPPORTED_* flags for the link modes, + * physical connectors and other link features for which the + * interface supports autonegotiation or auto-detection. + * Read-only. + * @advertising: Bitmask of %ADVERTISED_* flags for the link modes, + * physical connectors and other link features that are + * advertised through autonegotiation or enabled for + * auto-detection. + * @speed: Low bits of the speed, 1Mb units, 0 to INT_MAX or SPEED_UNKNOWN + * @duplex: Duplex mode; one of %DUPLEX_* + * @port: Physical connector type; one of %PORT_* + * @phy_address: MDIO address of PHY (transceiver); 0 or 255 if not + * applicable. For clause 45 PHYs this is the PRTAD. + * @transceiver: Historically used to distinguish different possible + * PHY types, but not in a consistent way. Deprecated. + * @autoneg: Enable/disable autonegotiation and auto-detection; + * either %AUTONEG_DISABLE or %AUTONEG_ENABLE + * @mdio_support: Bitmask of %ETH_MDIO_SUPPORTS_* flags for the MDIO + * protocols supported by the interface; 0 if unknown. + * Read-only. + * @maxtxpkt: Historically used to report TX IRQ coalescing; now + * obsoleted by &struct ethtool_coalesce. Read-only; deprecated. + * @maxrxpkt: Historically used to report RX IRQ coalescing; now + * obsoleted by &struct ethtool_coalesce. Read-only; deprecated. + * @speed_hi: High bits of the speed, 1Mb units, 0 to INT_MAX or SPEED_UNKNOWN + * @eth_tp_mdix: Ethernet twisted-pair MDI(-X) status; one of + * %ETH_TP_MDI_*. If the status is unknown or not applicable, the + * value will be %ETH_TP_MDI_INVALID. Read-only. + * @eth_tp_mdix_ctrl: Ethernet twisted pair MDI(-X) control; one of + * %ETH_TP_MDI_*. If MDI(-X) control is not implemented, reads + * yield %ETH_TP_MDI_INVALID and writes may be ignored or rejected. + * When written successfully, the link should be renegotiated if + * necessary. + * @lp_advertising: Bitmask of %ADVERTISED_* flags for the link modes + * and other link features that the link partner advertised + * through autonegotiation; 0 if unknown or not applicable. + * Read-only. + * @reserved: Reserved for future use; see the note on reserved space. + * + * The link speed in Mbps is split between @speed and @speed_hi. Use + * the ethtool_cmd_speed() and ethtool_cmd_speed_set() functions to + * access it. + * + * If autonegotiation is disabled, the speed and @duplex represent the + * fixed link mode and are writable if the driver supports multiple + * link modes. If it is enabled then they are read-only; if the link + * is up they represent the negotiated link mode; if the link is down, + * the speed is 0, %SPEED_UNKNOWN or the highest enabled speed and + * @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode. + * + * Some hardware interfaces may have multiple PHYs and/or physical + * connectors fitted or do not allow the driver to detect which are + * fitted. For these interfaces @port and/or @phy_address may be + * writable, possibly dependent on @autoneg being %AUTONEG_DISABLE. + * Otherwise, attempts to write different values may be ignored or + * rejected. + * + * Users should assume that all fields not marked read-only are + * writable and subject to validation by the driver. They should use + * %ETHTOOL_GSET to get the current values before making specific + * changes and then applying them with %ETHTOOL_SSET. + * + * Deprecated fields should be ignored by both users and drivers. + */ +struct ethtool_cmd { + __u32 cmd; + __u32 supported; + __u32 advertising; + __u16 speed; + __u8 duplex; + __u8 port; + __u8 phy_address; + __u8 transceiver; + __u8 autoneg; + __u8 mdio_support; + __u32 maxtxpkt; + __u32 maxrxpkt; + __u16 speed_hi; + __u8 eth_tp_mdix; + __u8 eth_tp_mdix_ctrl; + __u32 lp_advertising; + __u32 reserved[2]; +}; + +static __inline__ void ethtool_cmd_speed_set(struct ethtool_cmd *ep, + __u32 speed) +{ + ep->speed = (__u16)(speed & 0xFFFF); + ep->speed_hi = (__u16)(speed >> 16); +} + +static __inline__ __u32 ethtool_cmd_speed(const struct ethtool_cmd *ep) +{ + return (ep->speed_hi << 16) | ep->speed; +} + +/* Device supports clause 22 register access to PHY or peripherals + * using the interface defined in . This should not be + * set if there are known to be no such peripherals present or if + * the driver only emulates clause 22 registers for compatibility. + */ +#define ETH_MDIO_SUPPORTS_C22 1 + +/* Device supports clause 45 register access to PHY or peripherals + * using the interface defined in and . + * This should not be set if there are known to be no such peripherals + * present. + */ +#define ETH_MDIO_SUPPORTS_C45 2 + +#define ETHTOOL_FWVERS_LEN 32 +#define ETHTOOL_BUSINFO_LEN 32 +#define ETHTOOL_EROMVERS_LEN 32 + +/** + * struct ethtool_drvinfo - general driver and device information + * @cmd: Command number = %ETHTOOL_GDRVINFO + * @driver: Driver short name. This should normally match the name + * in its bus driver structure (e.g. pci_driver::name). Must + * not be an empty string. + * @version: Driver version string; may be an empty string + * @fw_version: Firmware version string; driver defined; may be an + * empty string + * @erom_version: Expansion ROM version string; driver defined; may be + * an empty string + * @bus_info: Device bus address. This should match the dev_name() + * string for the underlying bus device, if there is one. May be + * an empty string. + * @reserved2: Reserved for future use; see the note on reserved space. + * @n_priv_flags: Number of flags valid for %ETHTOOL_GPFLAGS and + * %ETHTOOL_SPFLAGS commands; also the number of strings in the + * %ETH_SS_PRIV_FLAGS set + * @n_stats: Number of u64 statistics returned by the %ETHTOOL_GSTATS + * command; also the number of strings in the %ETH_SS_STATS set + * @testinfo_len: Number of results returned by the %ETHTOOL_TEST + * command; also the number of strings in the %ETH_SS_TEST set + * @eedump_len: Size of EEPROM accessible through the %ETHTOOL_GEEPROM + * and %ETHTOOL_SEEPROM commands, in bytes + * @regdump_len: Size of register dump returned by the %ETHTOOL_GREGS + * command, in bytes + * + * Users can use the %ETHTOOL_GSSET_INFO command to get the number of + * strings in any string set (from Linux 2.6.34). + */ +struct ethtool_drvinfo { + __u32 cmd; + char driver[32]; + char version[32]; + char fw_version[ETHTOOL_FWVERS_LEN]; + char bus_info[ETHTOOL_BUSINFO_LEN]; + char erom_version[ETHTOOL_EROMVERS_LEN]; + char reserved2[12]; + __u32 n_priv_flags; + __u32 n_stats; + __u32 testinfo_len; + __u32 eedump_len; + __u32 regdump_len; +}; + +#define SOPASS_MAX 6 + +/** + * struct ethtool_wolinfo - Wake-On-Lan configuration + * @cmd: Command number = %ETHTOOL_GWOL or %ETHTOOL_SWOL + * @supported: Bitmask of %WAKE_* flags for supported Wake-On-Lan modes. + * Read-only. + * @wolopts: Bitmask of %WAKE_* flags for enabled Wake-On-Lan modes. + * @sopass: SecureOn(tm) password; meaningful only if %WAKE_MAGICSECURE + * is set in @wolopts. + */ +struct ethtool_wolinfo { + __u32 cmd; + __u32 supported; + __u32 wolopts; + __u8 sopass[SOPASS_MAX]; +}; + +/* for passing single values */ +struct ethtool_value { + __u32 cmd; + __u32 data; +}; + +#define PFC_STORM_PREVENTION_AUTO 0xffff +#define PFC_STORM_PREVENTION_DISABLE 0 + +enum tunable_id { + ETHTOOL_ID_UNSPEC, + ETHTOOL_RX_COPYBREAK, + ETHTOOL_TX_COPYBREAK, + ETHTOOL_PFC_PREVENTION_TOUT, /* timeout in msecs */ + ETHTOOL_TX_COPYBREAK_BUF_SIZE, + /* + * Add your fresh new tunable attribute above and remember to update + * tunable_strings[] in net/ethtool/common.c + */ + __ETHTOOL_TUNABLE_COUNT, +}; + +enum tunable_type_id { + ETHTOOL_TUNABLE_UNSPEC, + ETHTOOL_TUNABLE_U8, + ETHTOOL_TUNABLE_U16, + ETHTOOL_TUNABLE_U32, + ETHTOOL_TUNABLE_U64, + ETHTOOL_TUNABLE_STRING, + ETHTOOL_TUNABLE_S8, + ETHTOOL_TUNABLE_S16, + ETHTOOL_TUNABLE_S32, + ETHTOOL_TUNABLE_S64, +}; + +struct ethtool_tunable { + __u32 cmd; + __u32 id; + __u32 type_id; + __u32 len; + void *data[]; +}; + +#define DOWNSHIFT_DEV_DEFAULT_COUNT 0xff +#define DOWNSHIFT_DEV_DISABLE 0 + +/* Time in msecs after which link is reported as down + * 0 = lowest time supported by the PHY + * 0xff = off, link down detection according to standard + */ +#define ETHTOOL_PHY_FAST_LINK_DOWN_ON 0 +#define ETHTOOL_PHY_FAST_LINK_DOWN_OFF 0xff + +/* Energy Detect Power Down (EDPD) is a feature supported by some PHYs, where + * the PHY's RX & TX blocks are put into a low-power mode when there is no + * link detected (typically cable is un-plugged). For RX, only a minimal + * link-detection is available, and for TX the PHY wakes up to send link pulses + * to avoid any lock-ups in case the peer PHY may also be running in EDPD mode. + * + * Some PHYs may support configuration of the wake-up interval for TX pulses, + * and some PHYs may support only disabling TX pulses entirely. For the latter + * a special value is required (ETHTOOL_PHY_EDPD_NO_TX) so that this can be + * configured from userspace (should the user want it). + * + * The interval units for TX wake-up are in milliseconds, since this should + * cover a reasonable range of intervals: + * - from 1 millisecond, which does not sound like much of a power-saver + * - to ~65 seconds which is quite a lot to wait for a link to come up when + * plugging a cable + */ +#define ETHTOOL_PHY_EDPD_DFLT_TX_MSECS 0xffff +#define ETHTOOL_PHY_EDPD_NO_TX 0xfffe +#define ETHTOOL_PHY_EDPD_DISABLE 0 + +enum phy_tunable_id { + ETHTOOL_PHY_ID_UNSPEC, + ETHTOOL_PHY_DOWNSHIFT, + ETHTOOL_PHY_FAST_LINK_DOWN, + ETHTOOL_PHY_EDPD, + /* + * Add your fresh new phy tunable attribute above and remember to update + * phy_tunable_strings[] in net/ethtool/common.c + */ + __ETHTOOL_PHY_TUNABLE_COUNT, +}; + +/** + * struct ethtool_regs - hardware register dump + * @cmd: Command number = %ETHTOOL_GREGS + * @version: Dump format version. This is driver-specific and may + * distinguish different chips/revisions. Drivers must use new + * version numbers whenever the dump format changes in an + * incompatible way. + * @len: On entry, the real length of @data. On return, the number of + * bytes used. + * @data: Buffer for the register dump + * + * Users should use %ETHTOOL_GDRVINFO to find the maximum length of + * a register dump for the interface. They must allocate the buffer + * immediately following this structure. + */ +struct ethtool_regs { + __u32 cmd; + __u32 version; + __u32 len; + __u8 data[]; +}; + +/** + * struct ethtool_eeprom - EEPROM dump + * @cmd: Command number = %ETHTOOL_GEEPROM, %ETHTOOL_GMODULEEEPROM or + * %ETHTOOL_SEEPROM + * @magic: A 'magic cookie' value to guard against accidental changes. + * The value passed in to %ETHTOOL_SEEPROM must match the value + * returned by %ETHTOOL_GEEPROM for the same device. This is + * unused when @cmd is %ETHTOOL_GMODULEEEPROM. + * @offset: Offset within the EEPROM to begin reading/writing, in bytes + * @len: On entry, number of bytes to read/write. On successful + * return, number of bytes actually read/written. In case of + * error, this may indicate at what point the error occurred. + * @data: Buffer to read/write from + * + * Users may use %ETHTOOL_GDRVINFO or %ETHTOOL_GMODULEINFO to find + * the length of an on-board or module EEPROM, respectively. They + * must allocate the buffer immediately following this structure. + */ +struct ethtool_eeprom { + __u32 cmd; + __u32 magic; + __u32 offset; + __u32 len; + __u8 data[]; +}; + +/** + * struct ethtool_eee - Energy Efficient Ethernet information + * @cmd: ETHTOOL_{G,S}EEE + * @supported: Mask of %SUPPORTED_* flags for the speed/duplex combinations + * for which there is EEE support. + * @advertised: Mask of %ADVERTISED_* flags for the speed/duplex combinations + * advertised as eee capable. + * @lp_advertised: Mask of %ADVERTISED_* flags for the speed/duplex + * combinations advertised by the link partner as eee capable. + * @eee_active: Result of the eee auto negotiation. + * @eee_enabled: EEE configured mode (enabled/disabled). + * @tx_lpi_enabled: Whether the interface should assert its tx lpi, given + * that eee was negotiated. + * @tx_lpi_timer: Time in microseconds the interface delays prior to asserting + * its tx lpi (after reaching 'idle' state). Effective only when eee + * was negotiated and tx_lpi_enabled was set. + * @reserved: Reserved for future use; see the note on reserved space. + */ +struct ethtool_eee { + __u32 cmd; + __u32 supported; + __u32 advertised; + __u32 lp_advertised; + __u32 eee_active; + __u32 eee_enabled; + __u32 tx_lpi_enabled; + __u32 tx_lpi_timer; + __u32 reserved[2]; +}; + +/** + * struct ethtool_modinfo - plugin module eeprom information + * @cmd: %ETHTOOL_GMODULEINFO + * @type: Standard the module information conforms to %ETH_MODULE_SFF_xxxx + * @eeprom_len: Length of the eeprom + * @reserved: Reserved for future use; see the note on reserved space. + * + * This structure is used to return the information to + * properly size memory for a subsequent call to %ETHTOOL_GMODULEEEPROM. + * The type code indicates the eeprom data format + */ +struct ethtool_modinfo { + __u32 cmd; + __u32 type; + __u32 eeprom_len; + __u32 reserved[8]; +}; + +/** + * struct ethtool_coalesce - coalescing parameters for IRQs and stats updates + * @cmd: ETHTOOL_{G,S}COALESCE + * @rx_coalesce_usecs: How many usecs to delay an RX interrupt after + * a packet arrives. + * @rx_max_coalesced_frames: Maximum number of packets to receive + * before an RX interrupt. + * @rx_coalesce_usecs_irq: Same as @rx_coalesce_usecs, except that + * this value applies while an IRQ is being serviced by the host. + * @rx_max_coalesced_frames_irq: Same as @rx_max_coalesced_frames, + * except that this value applies while an IRQ is being serviced + * by the host. + * @tx_coalesce_usecs: How many usecs to delay a TX interrupt after + * a packet is sent. + * @tx_max_coalesced_frames: Maximum number of packets to be sent + * before a TX interrupt. + * @tx_coalesce_usecs_irq: Same as @tx_coalesce_usecs, except that + * this value applies while an IRQ is being serviced by the host. + * @tx_max_coalesced_frames_irq: Same as @tx_max_coalesced_frames, + * except that this value applies while an IRQ is being serviced + * by the host. + * @stats_block_coalesce_usecs: How many usecs to delay in-memory + * statistics block updates. Some drivers do not have an + * in-memory statistic block, and in such cases this value is + * ignored. This value must not be zero. + * @use_adaptive_rx_coalesce: Enable adaptive RX coalescing. + * @use_adaptive_tx_coalesce: Enable adaptive TX coalescing. + * @pkt_rate_low: Threshold for low packet rate (packets per second). + * @rx_coalesce_usecs_low: How many usecs to delay an RX interrupt after + * a packet arrives, when the packet rate is below @pkt_rate_low. + * @rx_max_coalesced_frames_low: Maximum number of packets to be received + * before an RX interrupt, when the packet rate is below @pkt_rate_low. + * @tx_coalesce_usecs_low: How many usecs to delay a TX interrupt after + * a packet is sent, when the packet rate is below @pkt_rate_low. + * @tx_max_coalesced_frames_low: Maximum nuumber of packets to be sent before + * a TX interrupt, when the packet rate is below @pkt_rate_low. + * @pkt_rate_high: Threshold for high packet rate (packets per second). + * @rx_coalesce_usecs_high: How many usecs to delay an RX interrupt after + * a packet arrives, when the packet rate is above @pkt_rate_high. + * @rx_max_coalesced_frames_high: Maximum number of packets to be received + * before an RX interrupt, when the packet rate is above @pkt_rate_high. + * @tx_coalesce_usecs_high: How many usecs to delay a TX interrupt after + * a packet is sent, when the packet rate is above @pkt_rate_high. + * @tx_max_coalesced_frames_high: Maximum number of packets to be sent before + * a TX interrupt, when the packet rate is above @pkt_rate_high. + * @rate_sample_interval: How often to do adaptive coalescing packet rate + * sampling, measured in seconds. Must not be zero. + * + * Each pair of (usecs, max_frames) fields specifies that interrupts + * should be coalesced until + * (usecs > 0 && time_since_first_completion >= usecs) || + * (max_frames > 0 && completed_frames >= max_frames) + * + * It is illegal to set both usecs and max_frames to zero as this + * would cause interrupts to never be generated. To disable + * coalescing, set usecs = 0 and max_frames = 1. + * + * Some implementations ignore the value of max_frames and use the + * condition time_since_first_completion >= usecs + * + * This is deprecated. Drivers for hardware that does not support + * counting completions should validate that max_frames == !rx_usecs. + * + * Adaptive RX/TX coalescing is an algorithm implemented by some + * drivers to improve latency under low packet rates and improve + * throughput under high packet rates. Some drivers only implement + * one of RX or TX adaptive coalescing. Anything not implemented by + * the driver causes these values to be silently ignored. + * + * When the packet rate is below @pkt_rate_high but above + * @pkt_rate_low (both measured in packets per second) the + * normal {rx,tx}_* coalescing parameters are used. + */ +struct ethtool_coalesce { + __u32 cmd; + __u32 rx_coalesce_usecs; + __u32 rx_max_coalesced_frames; + __u32 rx_coalesce_usecs_irq; + __u32 rx_max_coalesced_frames_irq; + __u32 tx_coalesce_usecs; + __u32 tx_max_coalesced_frames; + __u32 tx_coalesce_usecs_irq; + __u32 tx_max_coalesced_frames_irq; + __u32 stats_block_coalesce_usecs; + __u32 use_adaptive_rx_coalesce; + __u32 use_adaptive_tx_coalesce; + __u32 pkt_rate_low; + __u32 rx_coalesce_usecs_low; + __u32 rx_max_coalesced_frames_low; + __u32 tx_coalesce_usecs_low; + __u32 tx_max_coalesced_frames_low; + __u32 pkt_rate_high; + __u32 rx_coalesce_usecs_high; + __u32 rx_max_coalesced_frames_high; + __u32 tx_coalesce_usecs_high; + __u32 tx_max_coalesced_frames_high; + __u32 rate_sample_interval; +}; + +/** + * struct ethtool_ringparam - RX/TX ring parameters + * @cmd: Command number = %ETHTOOL_GRINGPARAM or %ETHTOOL_SRINGPARAM + * @rx_max_pending: Maximum supported number of pending entries per + * RX ring. Read-only. + * @rx_mini_max_pending: Maximum supported number of pending entries + * per RX mini ring. Read-only. + * @rx_jumbo_max_pending: Maximum supported number of pending entries + * per RX jumbo ring. Read-only. + * @tx_max_pending: Maximum supported number of pending entries per + * TX ring. Read-only. + * @rx_pending: Current maximum number of pending entries per RX ring + * @rx_mini_pending: Current maximum number of pending entries per RX + * mini ring + * @rx_jumbo_pending: Current maximum number of pending entries per RX + * jumbo ring + * @tx_pending: Current maximum supported number of pending entries + * per TX ring + * + * If the interface does not have separate RX mini and/or jumbo rings, + * @rx_mini_max_pending and/or @rx_jumbo_max_pending will be 0. + * + * There may also be driver-dependent minimum values for the number + * of entries per ring. + */ +struct ethtool_ringparam { + __u32 cmd; + __u32 rx_max_pending; + __u32 rx_mini_max_pending; + __u32 rx_jumbo_max_pending; + __u32 tx_max_pending; + __u32 rx_pending; + __u32 rx_mini_pending; + __u32 rx_jumbo_pending; + __u32 tx_pending; +}; + +/** + * struct ethtool_channels - configuring number of network channel + * @cmd: ETHTOOL_{G,S}CHANNELS + * @max_rx: Read only. Maximum number of receive channel the driver support. + * @max_tx: Read only. Maximum number of transmit channel the driver support. + * @max_other: Read only. Maximum number of other channel the driver support. + * @max_combined: Read only. Maximum number of combined channel the driver + * support. Set of queues RX, TX or other. + * @rx_count: Valid values are in the range 1 to the max_rx. + * @tx_count: Valid values are in the range 1 to the max_tx. + * @other_count: Valid values are in the range 1 to the max_other. + * @combined_count: Valid values are in the range 1 to the max_combined. + * + * This can be used to configure RX, TX and other channels. + */ + +struct ethtool_channels { + __u32 cmd; + __u32 max_rx; + __u32 max_tx; + __u32 max_other; + __u32 max_combined; + __u32 rx_count; + __u32 tx_count; + __u32 other_count; + __u32 combined_count; +}; + +/** + * struct ethtool_pauseparam - Ethernet pause (flow control) parameters + * @cmd: Command number = %ETHTOOL_GPAUSEPARAM or %ETHTOOL_SPAUSEPARAM + * @autoneg: Flag to enable autonegotiation of pause frame use + * @rx_pause: Flag to enable reception of pause frames + * @tx_pause: Flag to enable transmission of pause frames + * + * Drivers should reject a non-zero setting of @autoneg when + * autoneogotiation is disabled (or not supported) for the link. + * + * If the link is autonegotiated, drivers should use + * mii_advertise_flowctrl() or similar code to set the advertised + * pause frame capabilities based on the @rx_pause and @tx_pause flags, + * even if @autoneg is zero. They should also allow the advertised + * pause frame capabilities to be controlled directly through the + * advertising field of &struct ethtool_cmd. + * + * If @autoneg is non-zero, the MAC is configured to send and/or + * receive pause frames according to the result of autonegotiation. + * Otherwise, it is configured directly based on the @rx_pause and + * @tx_pause flags. + */ +struct ethtool_pauseparam { + __u32 cmd; + __u32 autoneg; + __u32 rx_pause; + __u32 tx_pause; +}; + +/* Link extended state */ +enum ethtool_link_ext_state { + ETHTOOL_LINK_EXT_STATE_AUTONEG, + ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE, + ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH, + ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY, + ETHTOOL_LINK_EXT_STATE_NO_CABLE, + ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE, + ETHTOOL_LINK_EXT_STATE_EEPROM_ISSUE, + ETHTOOL_LINK_EXT_STATE_CALIBRATION_FAILURE, + ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED, + ETHTOOL_LINK_EXT_STATE_OVERHEAT, + ETHTOOL_LINK_EXT_STATE_MODULE, +}; + +/* More information in addition to ETHTOOL_LINK_EXT_STATE_AUTONEG. */ +enum ethtool_link_ext_substate_autoneg { + ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED = 1, + ETHTOOL_LINK_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED, + ETHTOOL_LINK_EXT_SUBSTATE_AN_NEXT_PAGE_EXCHANGE_FAILED, + ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED_FORCE_MODE, + ETHTOOL_LINK_EXT_SUBSTATE_AN_FEC_MISMATCH_DURING_OVERRIDE, + ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_HCD, +}; + +/* More information in addition to ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE. + */ +enum ethtool_link_ext_substate_link_training { + ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED = 1, + ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_INHIBIT_TIMEOUT, + ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_PARTNER_DID_NOT_SET_RECEIVER_READY, + ETHTOOL_LINK_EXT_SUBSTATE_LT_REMOTE_FAULT, +}; + +/* More information in addition to ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH. + */ +enum ethtool_link_ext_substate_link_logical_mismatch { + ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK = 1, + ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_AM_LOCK, + ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_GET_ALIGN_STATUS, + ETHTOOL_LINK_EXT_SUBSTATE_LLM_FC_FEC_IS_NOT_LOCKED, + ETHTOOL_LINK_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED, +}; + +/* More information in addition to ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY. + */ +enum ethtool_link_ext_substate_bad_signal_integrity { + ETHTOOL_LINK_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS = 1, + ETHTOOL_LINK_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE, + ETHTOOL_LINK_EXT_SUBSTATE_BSI_SERDES_REFERENCE_CLOCK_LOST, + ETHTOOL_LINK_EXT_SUBSTATE_BSI_SERDES_ALOS, +}; + +/* More information in addition to ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE. */ +enum ethtool_link_ext_substate_cable_issue { + ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE = 1, + ETHTOOL_LINK_EXT_SUBSTATE_CI_CABLE_TEST_FAILURE, +}; + +/* More information in addition to ETHTOOL_LINK_EXT_STATE_MODULE. */ +enum ethtool_link_ext_substate_module { + ETHTOOL_LINK_EXT_SUBSTATE_MODULE_CMIS_NOT_READY = 1, +}; + +#define ETH_GSTRING_LEN 32 + +/** + * enum ethtool_stringset - string set ID + * @ETH_SS_TEST: Self-test result names, for use with %ETHTOOL_TEST + * @ETH_SS_STATS: Statistic names, for use with %ETHTOOL_GSTATS + * @ETH_SS_PRIV_FLAGS: Driver private flag names, for use with + * %ETHTOOL_GPFLAGS and %ETHTOOL_SPFLAGS + * @ETH_SS_NTUPLE_FILTERS: Previously used with %ETHTOOL_GRXNTUPLE; + * now deprecated + * @ETH_SS_FEATURES: Device feature names + * @ETH_SS_RSS_HASH_FUNCS: RSS hush function names + * @ETH_SS_TUNABLES: tunable names + * @ETH_SS_PHY_STATS: Statistic names, for use with %ETHTOOL_GPHYSTATS + * @ETH_SS_PHY_TUNABLES: PHY tunable names + * @ETH_SS_LINK_MODES: link mode names + * @ETH_SS_MSG_CLASSES: debug message class names + * @ETH_SS_WOL_MODES: wake-on-lan modes + * @ETH_SS_SOF_TIMESTAMPING: SOF_TIMESTAMPING_* flags + * @ETH_SS_TS_TX_TYPES: timestamping Tx types + * @ETH_SS_TS_RX_FILTERS: timestamping Rx filters + * @ETH_SS_UDP_TUNNEL_TYPES: UDP tunnel types + * @ETH_SS_STATS_STD: standardized stats + * @ETH_SS_STATS_ETH_PHY: names of IEEE 802.3 PHY statistics + * @ETH_SS_STATS_ETH_MAC: names of IEEE 802.3 MAC statistics + * @ETH_SS_STATS_ETH_CTRL: names of IEEE 802.3 MAC Control statistics + * @ETH_SS_STATS_RMON: names of RMON statistics + * + * @ETH_SS_COUNT: number of defined string sets + */ +enum ethtool_stringset { + ETH_SS_TEST = 0, + ETH_SS_STATS, + ETH_SS_PRIV_FLAGS, + ETH_SS_NTUPLE_FILTERS, + ETH_SS_FEATURES, + ETH_SS_RSS_HASH_FUNCS, + ETH_SS_TUNABLES, + ETH_SS_PHY_STATS, + ETH_SS_PHY_TUNABLES, + ETH_SS_LINK_MODES, + ETH_SS_MSG_CLASSES, + ETH_SS_WOL_MODES, + ETH_SS_SOF_TIMESTAMPING, + ETH_SS_TS_TX_TYPES, + ETH_SS_TS_RX_FILTERS, + ETH_SS_UDP_TUNNEL_TYPES, + ETH_SS_STATS_STD, + ETH_SS_STATS_ETH_PHY, + ETH_SS_STATS_ETH_MAC, + ETH_SS_STATS_ETH_CTRL, + ETH_SS_STATS_RMON, + + /* add new constants above here */ + ETH_SS_COUNT +}; + +/** + * enum ethtool_mac_stats_src - source of ethtool MAC statistics + * @ETHTOOL_MAC_STATS_SRC_AGGREGATE: + * if device supports a MAC merge layer, this retrieves the aggregate + * statistics of the eMAC and pMAC. Otherwise, it retrieves just the + * statistics of the single (express) MAC. + * @ETHTOOL_MAC_STATS_SRC_EMAC: + * if device supports a MM layer, this retrieves the eMAC statistics. + * Otherwise, it retrieves the statistics of the single (express) MAC. + * @ETHTOOL_MAC_STATS_SRC_PMAC: + * if device supports a MM layer, this retrieves the pMAC statistics. + */ +enum ethtool_mac_stats_src { + ETHTOOL_MAC_STATS_SRC_AGGREGATE, + ETHTOOL_MAC_STATS_SRC_EMAC, + ETHTOOL_MAC_STATS_SRC_PMAC, +}; + +/** + * enum ethtool_module_power_mode_policy - plug-in module power mode policy + * @ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH: Module is always in high power mode. + * @ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO: Module is transitioned by the host + * to high power mode when the first port using it is put administratively + * up and to low power mode when the last port using it is put + * administratively down. + */ +enum ethtool_module_power_mode_policy { + ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH = 1, + ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO, +}; + +/** + * enum ethtool_module_power_mode - plug-in module power mode + * @ETHTOOL_MODULE_POWER_MODE_LOW: Module is in low power mode. + * @ETHTOOL_MODULE_POWER_MODE_HIGH: Module is in high power mode. + */ +enum ethtool_module_power_mode { + ETHTOOL_MODULE_POWER_MODE_LOW = 1, + ETHTOOL_MODULE_POWER_MODE_HIGH, +}; + +/** + * enum ethtool_podl_pse_admin_state - operational state of the PoDL PSE + * functions. IEEE 802.3-2018 30.15.1.1.2 aPoDLPSEAdminState + * @ETHTOOL_PODL_PSE_ADMIN_STATE_UNKNOWN: state of PoDL PSE functions are + * unknown + * @ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED: PoDL PSE functions are disabled + * @ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED: PoDL PSE functions are enabled + */ +enum ethtool_podl_pse_admin_state { + ETHTOOL_PODL_PSE_ADMIN_STATE_UNKNOWN = 1, + ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED, + ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED, +}; + +/** + * enum ethtool_podl_pse_pw_d_status - power detection status of the PoDL PSE. + * IEEE 802.3-2018 30.15.1.1.3 aPoDLPSEPowerDetectionStatus: + * @ETHTOOL_PODL_PSE_PW_D_STATUS_UNKNOWN: PoDL PSE + * @ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED: "The enumeration “disabled” is + * asserted true when the PoDL PSE state diagram variable mr_pse_enable is + * false" + * @ETHTOOL_PODL_PSE_PW_D_STATUS_SEARCHING: "The enumeration “searching” is + * asserted true when either of the PSE state diagram variables + * pi_detecting or pi_classifying is true." + * @ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING: "The enumeration “deliveringPower” + * is asserted true when the PoDL PSE state diagram variable pi_powered is + * true." + * @ETHTOOL_PODL_PSE_PW_D_STATUS_SLEEP: "The enumeration “sleep” is asserted + * true when the PoDL PSE state diagram variable pi_sleeping is true." + * @ETHTOOL_PODL_PSE_PW_D_STATUS_IDLE: "The enumeration “idle” is asserted true + * when the logical combination of the PoDL PSE state diagram variables + * pi_prebiased*!pi_sleeping is true." + * @ETHTOOL_PODL_PSE_PW_D_STATUS_ERROR: "The enumeration “error” is asserted + * true when the PoDL PSE state diagram variable overload_held is true." + */ +enum ethtool_podl_pse_pw_d_status { + ETHTOOL_PODL_PSE_PW_D_STATUS_UNKNOWN = 1, + ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED, + ETHTOOL_PODL_PSE_PW_D_STATUS_SEARCHING, + ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING, + ETHTOOL_PODL_PSE_PW_D_STATUS_SLEEP, + ETHTOOL_PODL_PSE_PW_D_STATUS_IDLE, + ETHTOOL_PODL_PSE_PW_D_STATUS_ERROR, +}; + +/** + * enum ethtool_mm_verify_status - status of MAC Merge Verify function + * @ETHTOOL_MM_VERIFY_STATUS_UNKNOWN: + * verification status is unknown + * @ETHTOOL_MM_VERIFY_STATUS_INITIAL: + * the 802.3 Verify State diagram is in the state INIT_VERIFICATION + * @ETHTOOL_MM_VERIFY_STATUS_VERIFYING: + * the Verify State diagram is in the state VERIFICATION_IDLE, + * SEND_VERIFY or WAIT_FOR_RESPONSE + * @ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED: + * indicates that the Verify State diagram is in the state VERIFIED + * @ETHTOOL_MM_VERIFY_STATUS_FAILED: + * the Verify State diagram is in the state VERIFY_FAIL + * @ETHTOOL_MM_VERIFY_STATUS_DISABLED: + * verification of preemption operation is disabled + */ +enum ethtool_mm_verify_status { + ETHTOOL_MM_VERIFY_STATUS_UNKNOWN, + ETHTOOL_MM_VERIFY_STATUS_INITIAL, + ETHTOOL_MM_VERIFY_STATUS_VERIFYING, + ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED, + ETHTOOL_MM_VERIFY_STATUS_FAILED, + ETHTOOL_MM_VERIFY_STATUS_DISABLED, +}; + +/** + * struct ethtool_gstrings - string set for data tagging + * @cmd: Command number = %ETHTOOL_GSTRINGS + * @string_set: String set ID; one of &enum ethtool_stringset + * @len: On return, the number of strings in the string set + * @data: Buffer for strings. Each string is null-padded to a size of + * %ETH_GSTRING_LEN. + * + * Users must use %ETHTOOL_GSSET_INFO to find the number of strings in + * the string set. They must allocate a buffer of the appropriate + * size immediately following this structure. + */ +struct ethtool_gstrings { + __u32 cmd; + __u32 string_set; + __u32 len; + __u8 data[]; +}; + +/** + * struct ethtool_sset_info - string set information + * @cmd: Command number = %ETHTOOL_GSSET_INFO + * @reserved: Reserved for future use; see the note on reserved space. + * @sset_mask: On entry, a bitmask of string sets to query, with bits + * numbered according to &enum ethtool_stringset. On return, a + * bitmask of those string sets queried that are supported. + * @data: Buffer for string set sizes. On return, this contains the + * size of each string set that was queried and supported, in + * order of ID. + * + * Example: The user passes in @sset_mask = 0x7 (sets 0, 1, 2) and on + * return @sset_mask == 0x6 (sets 1, 2). Then @data[0] contains the + * size of set 1 and @data[1] contains the size of set 2. + * + * Users must allocate a buffer of the appropriate size (4 * number of + * sets queried) immediately following this structure. + */ +struct ethtool_sset_info { + __u32 cmd; + __u32 reserved; + __u64 sset_mask; + __u32 data[]; +}; + +/** + * enum ethtool_test_flags - flags definition of ethtool_test + * @ETH_TEST_FL_OFFLINE: if set perform online and offline tests, otherwise + * only online tests. + * @ETH_TEST_FL_FAILED: Driver set this flag if test fails. + * @ETH_TEST_FL_EXTERNAL_LB: Application request to perform external loopback + * test. + * @ETH_TEST_FL_EXTERNAL_LB_DONE: Driver performed the external loopback test + */ + +enum ethtool_test_flags { + ETH_TEST_FL_OFFLINE = (1 << 0), + ETH_TEST_FL_FAILED = (1 << 1), + ETH_TEST_FL_EXTERNAL_LB = (1 << 2), + ETH_TEST_FL_EXTERNAL_LB_DONE = (1 << 3), +}; + +/** + * struct ethtool_test - device self-test invocation + * @cmd: Command number = %ETHTOOL_TEST + * @flags: A bitmask of flags from &enum ethtool_test_flags. Some + * flags may be set by the user on entry; others may be set by + * the driver on return. + * @reserved: Reserved for future use; see the note on reserved space. + * @len: On return, the number of test results + * @data: Array of test results + * + * Users must use %ETHTOOL_GSSET_INFO or %ETHTOOL_GDRVINFO to find the + * number of test results that will be returned. They must allocate a + * buffer of the appropriate size (8 * number of results) immediately + * following this structure. + */ +struct ethtool_test { + __u32 cmd; + __u32 flags; + __u32 reserved; + __u32 len; + __u64 data[]; +}; + +/** + * struct ethtool_stats - device-specific statistics + * @cmd: Command number = %ETHTOOL_GSTATS + * @n_stats: On return, the number of statistics + * @data: Array of statistics + * + * Users must use %ETHTOOL_GSSET_INFO or %ETHTOOL_GDRVINFO to find the + * number of statistics that will be returned. They must allocate a + * buffer of the appropriate size (8 * number of statistics) + * immediately following this structure. + */ +struct ethtool_stats { + __u32 cmd; + __u32 n_stats; + __u64 data[]; +}; + +/** + * struct ethtool_perm_addr - permanent hardware address + * @cmd: Command number = %ETHTOOL_GPERMADDR + * @size: On entry, the size of the buffer. On return, the size of the + * address. The command fails if the buffer is too small. + * @data: Buffer for the address + * + * Users must allocate the buffer immediately following this structure. + * A buffer size of %MAX_ADDR_LEN should be sufficient for any address + * type. + */ +struct ethtool_perm_addr { + __u32 cmd; + __u32 size; + __u8 data[]; +}; + +/* boolean flags controlling per-interface behavior characteristics. + * When reading, the flag indicates whether or not a certain behavior + * is enabled/present. When writing, the flag indicates whether + * or not the driver should turn on (set) or off (clear) a behavior. + * + * Some behaviors may read-only (unconditionally absent or present). + * If such is the case, return EINVAL in the set-flags operation if the + * flag differs from the read-only value. + */ +enum ethtool_flags { + ETH_FLAG_TXVLAN = (1 << 7), /* TX VLAN offload enabled */ + ETH_FLAG_RXVLAN = (1 << 8), /* RX VLAN offload enabled */ + ETH_FLAG_LRO = (1 << 15), /* LRO is enabled */ + ETH_FLAG_NTUPLE = (1 << 27), /* N-tuple filters enabled */ + ETH_FLAG_RXHASH = (1 << 28), +}; + +/* The following structures are for supporting RX network flow + * classification and RX n-tuple configuration. Note, all multibyte + * fields, e.g., ip4src, ip4dst, psrc, pdst, spi, etc. are expected to + * be in network byte order. + */ + +/** + * struct ethtool_tcpip4_spec - flow specification for TCP/IPv4 etc. + * @ip4src: Source host + * @ip4dst: Destination host + * @psrc: Source port + * @pdst: Destination port + * @tos: Type-of-service + * + * This can be used to specify a TCP/IPv4, UDP/IPv4 or SCTP/IPv4 flow. + */ +struct ethtool_tcpip4_spec { + __be32 ip4src; + __be32 ip4dst; + __be16 psrc; + __be16 pdst; + __u8 tos; +}; + +/** + * struct ethtool_ah_espip4_spec - flow specification for IPsec/IPv4 + * @ip4src: Source host + * @ip4dst: Destination host + * @spi: Security parameters index + * @tos: Type-of-service + * + * This can be used to specify an IPsec transport or tunnel over IPv4. + */ +struct ethtool_ah_espip4_spec { + __be32 ip4src; + __be32 ip4dst; + __be32 spi; + __u8 tos; +}; + +#define ETH_RX_NFC_IP4 1 + +/** + * struct ethtool_usrip4_spec - general flow specification for IPv4 + * @ip4src: Source host + * @ip4dst: Destination host + * @l4_4_bytes: First 4 bytes of transport (layer 4) header + * @tos: Type-of-service + * @ip_ver: Value must be %ETH_RX_NFC_IP4; mask must be 0 + * @proto: Transport protocol number; mask must be 0 + */ +struct ethtool_usrip4_spec { + __be32 ip4src; + __be32 ip4dst; + __be32 l4_4_bytes; + __u8 tos; + __u8 ip_ver; + __u8 proto; +}; + +/** + * struct ethtool_tcpip6_spec - flow specification for TCP/IPv6 etc. + * @ip6src: Source host + * @ip6dst: Destination host + * @psrc: Source port + * @pdst: Destination port + * @tclass: Traffic Class + * + * This can be used to specify a TCP/IPv6, UDP/IPv6 or SCTP/IPv6 flow. + */ +struct ethtool_tcpip6_spec { + __be32 ip6src[4]; + __be32 ip6dst[4]; + __be16 psrc; + __be16 pdst; + __u8 tclass; +}; + +/** + * struct ethtool_ah_espip6_spec - flow specification for IPsec/IPv6 + * @ip6src: Source host + * @ip6dst: Destination host + * @spi: Security parameters index + * @tclass: Traffic Class + * + * This can be used to specify an IPsec transport or tunnel over IPv6. + */ +struct ethtool_ah_espip6_spec { + __be32 ip6src[4]; + __be32 ip6dst[4]; + __be32 spi; + __u8 tclass; +}; + +/** + * struct ethtool_usrip6_spec - general flow specification for IPv6 + * @ip6src: Source host + * @ip6dst: Destination host + * @l4_4_bytes: First 4 bytes of transport (layer 4) header + * @tclass: Traffic Class + * @l4_proto: Transport protocol number (nexthdr after any Extension Headers) + */ +struct ethtool_usrip6_spec { + __be32 ip6src[4]; + __be32 ip6dst[4]; + __be32 l4_4_bytes; + __u8 tclass; + __u8 l4_proto; +}; + +union ethtool_flow_union { + struct ethtool_tcpip4_spec tcp_ip4_spec; + struct ethtool_tcpip4_spec udp_ip4_spec; + struct ethtool_tcpip4_spec sctp_ip4_spec; + struct ethtool_ah_espip4_spec ah_ip4_spec; + struct ethtool_ah_espip4_spec esp_ip4_spec; + struct ethtool_usrip4_spec usr_ip4_spec; + struct ethtool_tcpip6_spec tcp_ip6_spec; + struct ethtool_tcpip6_spec udp_ip6_spec; + struct ethtool_tcpip6_spec sctp_ip6_spec; + struct ethtool_ah_espip6_spec ah_ip6_spec; + struct ethtool_ah_espip6_spec esp_ip6_spec; + struct ethtool_usrip6_spec usr_ip6_spec; + struct ethhdr ether_spec; + __u8 hdata[52]; +}; + +/** + * struct ethtool_flow_ext - additional RX flow fields + * @h_dest: destination MAC address + * @vlan_etype: VLAN EtherType + * @vlan_tci: VLAN tag control information + * @data: user defined data + * @padding: Reserved for future use; see the note on reserved space. + * + * Note, @vlan_etype, @vlan_tci, and @data are only valid if %FLOW_EXT + * is set in &struct ethtool_rx_flow_spec @flow_type. + * @h_dest is valid if %FLOW_MAC_EXT is set. + */ +struct ethtool_flow_ext { + __u8 padding[2]; + unsigned char h_dest[ETH_ALEN]; + __be16 vlan_etype; + __be16 vlan_tci; + __be32 data[2]; +}; + +/** + * struct ethtool_rx_flow_spec - classification rule for RX flows + * @flow_type: Type of match to perform, e.g. %TCP_V4_FLOW + * @h_u: Flow fields to match (dependent on @flow_type) + * @h_ext: Additional fields to match + * @m_u: Masks for flow field bits to be matched + * @m_ext: Masks for additional field bits to be matched + * Note, all additional fields must be ignored unless @flow_type + * includes the %FLOW_EXT or %FLOW_MAC_EXT flag + * (see &struct ethtool_flow_ext description). + * @ring_cookie: RX ring/queue index to deliver to, or %RX_CLS_FLOW_DISC + * if packets should be discarded, or %RX_CLS_FLOW_WAKE if the + * packets should be used for Wake-on-LAN with %WAKE_FILTER + * @location: Location of rule in the table. Locations must be + * numbered such that a flow matching multiple rules will be + * classified according to the first (lowest numbered) rule. + */ +struct ethtool_rx_flow_spec { + __u32 flow_type; + union ethtool_flow_union h_u; + struct ethtool_flow_ext h_ext; + union ethtool_flow_union m_u; + struct ethtool_flow_ext m_ext; + __u64 ring_cookie; + __u32 location; +}; + +/* How rings are laid out when accessing virtual functions or + * offloaded queues is device specific. To allow users to do flow + * steering and specify these queues the ring cookie is partitioned + * into a 32bit queue index with an 8 bit virtual function id. + * This also leaves the 3bytes for further specifiers. It is possible + * future devices may support more than 256 virtual functions if + * devices start supporting PCIe w/ARI. However at the moment I + * do not know of any devices that support this so I do not reserve + * space for this at this time. If a future patch consumes the next + * byte it should be aware of this possibility. + */ +#define ETHTOOL_RX_FLOW_SPEC_RING 0x00000000FFFFFFFFLL +#define ETHTOOL_RX_FLOW_SPEC_RING_VF 0x000000FF00000000LL +#define ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF 32 +static __inline__ __u64 ethtool_get_flow_spec_ring(__u64 ring_cookie) +{ + return ETHTOOL_RX_FLOW_SPEC_RING & ring_cookie; +} + +static __inline__ __u64 ethtool_get_flow_spec_ring_vf(__u64 ring_cookie) +{ + return (ETHTOOL_RX_FLOW_SPEC_RING_VF & ring_cookie) >> + ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF; +} + +/** + * struct ethtool_rxnfc - command to get or set RX flow classification rules + * @cmd: Specific command number - %ETHTOOL_GRXFH, %ETHTOOL_SRXFH, + * %ETHTOOL_GRXRINGS, %ETHTOOL_GRXCLSRLCNT, %ETHTOOL_GRXCLSRULE, + * %ETHTOOL_GRXCLSRLALL, %ETHTOOL_SRXCLSRLDEL or %ETHTOOL_SRXCLSRLINS + * @flow_type: Type of flow to be affected, e.g. %TCP_V4_FLOW + * @data: Command-dependent value + * @fs: Flow classification rule + * @rss_context: RSS context to be affected + * @rule_cnt: Number of rules to be affected + * @rule_locs: Array of used rule locations + * + * For %ETHTOOL_GRXFH and %ETHTOOL_SRXFH, @data is a bitmask indicating + * the fields included in the flow hash, e.g. %RXH_IP_SRC. The following + * structure fields must not be used, except that if @flow_type includes + * the %FLOW_RSS flag, then @rss_context determines which RSS context to + * act on. + * + * For %ETHTOOL_GRXRINGS, @data is set to the number of RX rings/queues + * on return. + * + * For %ETHTOOL_GRXCLSRLCNT, @rule_cnt is set to the number of defined + * rules on return. If @data is non-zero on return then it is the + * size of the rule table, plus the flag %RX_CLS_LOC_SPECIAL if the + * driver supports any special location values. If that flag is not + * set in @data then special location values should not be used. + * + * For %ETHTOOL_GRXCLSRULE, @fs.@location specifies the location of an + * existing rule on entry and @fs contains the rule on return; if + * @fs.@flow_type includes the %FLOW_RSS flag, then @rss_context is + * filled with the RSS context ID associated with the rule. + * + * For %ETHTOOL_GRXCLSRLALL, @rule_cnt specifies the array size of the + * user buffer for @rule_locs on entry. On return, @data is the size + * of the rule table, @rule_cnt is the number of defined rules, and + * @rule_locs contains the locations of the defined rules. Drivers + * must use the second parameter to get_rxnfc() instead of @rule_locs. + * + * For %ETHTOOL_SRXCLSRLINS, @fs specifies the rule to add or update. + * @fs.@location either specifies the location to use or is a special + * location value with %RX_CLS_LOC_SPECIAL flag set. On return, + * @fs.@location is the actual rule location. If @fs.@flow_type + * includes the %FLOW_RSS flag, @rss_context is the RSS context ID to + * use for flow spreading traffic which matches this rule. The value + * from the rxfh indirection table will be added to @fs.@ring_cookie + * to choose which ring to deliver to. + * + * For %ETHTOOL_SRXCLSRLDEL, @fs.@location specifies the location of an + * existing rule on entry. + * + * A driver supporting the special location values for + * %ETHTOOL_SRXCLSRLINS may add the rule at any suitable unused + * location, and may remove a rule at a later location (lower + * priority) that matches exactly the same set of flows. The special + * values are %RX_CLS_LOC_ANY, selecting any location; + * %RX_CLS_LOC_FIRST, selecting the first suitable location (maximum + * priority); and %RX_CLS_LOC_LAST, selecting the last suitable + * location (minimum priority). Additional special values may be + * defined in future and drivers must return -%EINVAL for any + * unrecognised value. + */ +struct ethtool_rxnfc { + __u32 cmd; + __u32 flow_type; + __u64 data; + struct ethtool_rx_flow_spec fs; + union { + __u32 rule_cnt; + __u32 rss_context; + }; + __u32 rule_locs[]; +}; + + +/** + * struct ethtool_rxfh_indir - command to get or set RX flow hash indirection + * @cmd: Specific command number - %ETHTOOL_GRXFHINDIR or %ETHTOOL_SRXFHINDIR + * @size: On entry, the array size of the user buffer, which may be zero. + * On return from %ETHTOOL_GRXFHINDIR, the array size of the hardware + * indirection table. + * @ring_index: RX ring/queue index for each hash value + * + * For %ETHTOOL_GRXFHINDIR, a @size of zero means that only the size + * should be returned. For %ETHTOOL_SRXFHINDIR, a @size of zero means + * the table should be reset to default values. This last feature + * is not supported by the original implementations. + */ +struct ethtool_rxfh_indir { + __u32 cmd; + __u32 size; + __u32 ring_index[]; +}; + +/** + * struct ethtool_rxfh - command to get/set RX flow hash indir or/and hash key. + * @cmd: Specific command number - %ETHTOOL_GRSSH or %ETHTOOL_SRSSH + * @rss_context: RSS context identifier. Context 0 is the default for normal + * traffic; other contexts can be referenced as the destination for RX flow + * classification rules. %ETH_RXFH_CONTEXT_ALLOC is used with command + * %ETHTOOL_SRSSH to allocate a new RSS context; on return this field will + * contain the ID of the newly allocated context. + * @indir_size: On entry, the array size of the user buffer for the + * indirection table, which may be zero, or (for %ETHTOOL_SRSSH), + * %ETH_RXFH_INDIR_NO_CHANGE. On return from %ETHTOOL_GRSSH, + * the array size of the hardware indirection table. + * @key_size: On entry, the array size of the user buffer for the hash key, + * which may be zero. On return from %ETHTOOL_GRSSH, the size of the + * hardware hash key. + * @hfunc: Defines the current RSS hash function used by HW (or to be set to). + * Valid values are one of the %ETH_RSS_HASH_*. + * @rsvd8: Reserved for future use; see the note on reserved space. + * @rsvd32: Reserved for future use; see the note on reserved space. + * @rss_config: RX ring/queue index for each hash value i.e., indirection table + * of @indir_size __u32 elements, followed by hash key of @key_size + * bytes. + * + * For %ETHTOOL_GRSSH, a @indir_size and key_size of zero means that only the + * size should be returned. For %ETHTOOL_SRSSH, an @indir_size of + * %ETH_RXFH_INDIR_NO_CHANGE means that indir table setting is not requested + * and a @indir_size of zero means the indir table should be reset to default + * values (if @rss_context == 0) or that the RSS context should be deleted. + * An hfunc of zero means that hash function setting is not requested. + */ +struct ethtool_rxfh { + __u32 cmd; + __u32 rss_context; + __u32 indir_size; + __u32 key_size; + __u8 hfunc; + __u8 rsvd8[3]; + __u32 rsvd32; + __u32 rss_config[]; +}; +#define ETH_RXFH_CONTEXT_ALLOC 0xffffffff +#define ETH_RXFH_INDIR_NO_CHANGE 0xffffffff + +/** + * struct ethtool_rx_ntuple_flow_spec - specification for RX flow filter + * @flow_type: Type of match to perform, e.g. %TCP_V4_FLOW + * @h_u: Flow field values to match (dependent on @flow_type) + * @m_u: Masks for flow field value bits to be ignored + * @vlan_tag: VLAN tag to match + * @vlan_tag_mask: Mask for VLAN tag bits to be ignored + * @data: Driver-dependent data to match + * @data_mask: Mask for driver-dependent data bits to be ignored + * @action: RX ring/queue index to deliver to (non-negative) or other action + * (negative, e.g. %ETHTOOL_RXNTUPLE_ACTION_DROP) + * + * For flow types %TCP_V4_FLOW, %UDP_V4_FLOW and %SCTP_V4_FLOW, where + * a field value and mask are both zero this is treated as if all mask + * bits are set i.e. the field is ignored. + */ +struct ethtool_rx_ntuple_flow_spec { + __u32 flow_type; + union { + struct ethtool_tcpip4_spec tcp_ip4_spec; + struct ethtool_tcpip4_spec udp_ip4_spec; + struct ethtool_tcpip4_spec sctp_ip4_spec; + struct ethtool_ah_espip4_spec ah_ip4_spec; + struct ethtool_ah_espip4_spec esp_ip4_spec; + struct ethtool_usrip4_spec usr_ip4_spec; + struct ethhdr ether_spec; + __u8 hdata[72]; + } h_u, m_u; + + __u16 vlan_tag; + __u16 vlan_tag_mask; + __u64 data; + __u64 data_mask; + + __s32 action; +#define ETHTOOL_RXNTUPLE_ACTION_DROP (-1) /* drop packet */ +#define ETHTOOL_RXNTUPLE_ACTION_CLEAR (-2) /* clear filter */ +}; + +/** + * struct ethtool_rx_ntuple - command to set or clear RX flow filter + * @cmd: Command number - %ETHTOOL_SRXNTUPLE + * @fs: Flow filter specification + */ +struct ethtool_rx_ntuple { + __u32 cmd; + struct ethtool_rx_ntuple_flow_spec fs; +}; + +#define ETHTOOL_FLASH_MAX_FILENAME 128 +enum ethtool_flash_op_type { + ETHTOOL_FLASH_ALL_REGIONS = 0, +}; + +/* for passing firmware flashing related parameters */ +struct ethtool_flash { + __u32 cmd; + __u32 region; + char data[ETHTOOL_FLASH_MAX_FILENAME]; +}; + +/** + * struct ethtool_dump - used for retrieving, setting device dump + * @cmd: Command number - %ETHTOOL_GET_DUMP_FLAG, %ETHTOOL_GET_DUMP_DATA, or + * %ETHTOOL_SET_DUMP + * @version: FW version of the dump, filled in by driver + * @flag: driver dependent flag for dump setting, filled in by driver during + * get and filled in by ethtool for set operation. + * flag must be initialized by macro ETH_FW_DUMP_DISABLE value when + * firmware dump is disabled. + * @len: length of dump data, used as the length of the user buffer on entry to + * %ETHTOOL_GET_DUMP_DATA and this is returned as dump length by driver + * for %ETHTOOL_GET_DUMP_FLAG command + * @data: data collected for get dump data operation + */ +struct ethtool_dump { + __u32 cmd; + __u32 version; + __u32 flag; + __u32 len; + __u8 data[]; +}; + +#define ETH_FW_DUMP_DISABLE 0 + +/* for returning and changing feature sets */ + +/** + * struct ethtool_get_features_block - block with state of 32 features + * @available: mask of changeable features + * @requested: mask of features requested to be enabled if possible + * @active: mask of currently enabled features + * @never_changed: mask of features not changeable for any device + */ +struct ethtool_get_features_block { + __u32 available; + __u32 requested; + __u32 active; + __u32 never_changed; +}; + +/** + * struct ethtool_gfeatures - command to get state of device's features + * @cmd: command number = %ETHTOOL_GFEATURES + * @size: On entry, the number of elements in the features[] array; + * on return, the number of elements in features[] needed to hold + * all features + * @features: state of features + */ +struct ethtool_gfeatures { + __u32 cmd; + __u32 size; + struct ethtool_get_features_block features[]; +}; + +/** + * struct ethtool_set_features_block - block with request for 32 features + * @valid: mask of features to be changed + * @requested: values of features to be changed + */ +struct ethtool_set_features_block { + __u32 valid; + __u32 requested; +}; + +/** + * struct ethtool_sfeatures - command to request change in device's features + * @cmd: command number = %ETHTOOL_SFEATURES + * @size: array size of the features[] array + * @features: feature change masks + */ +struct ethtool_sfeatures { + __u32 cmd; + __u32 size; + struct ethtool_set_features_block features[]; +}; + +/** + * struct ethtool_ts_info - holds a device's timestamping and PHC association + * @cmd: command number = %ETHTOOL_GET_TS_INFO + * @so_timestamping: bit mask of the sum of the supported SO_TIMESTAMPING flags + * @phc_index: device index of the associated PHC, or -1 if there is none + * @tx_types: bit mask of the supported hwtstamp_tx_types enumeration values + * @tx_reserved: Reserved for future use; see the note on reserved space. + * @rx_filters: bit mask of the supported hwtstamp_rx_filters enumeration values + * @rx_reserved: Reserved for future use; see the note on reserved space. + * + * The bits in the 'tx_types' and 'rx_filters' fields correspond to + * the 'hwtstamp_tx_types' and 'hwtstamp_rx_filters' enumeration values, + * respectively. For example, if the device supports HWTSTAMP_TX_ON, + * then (1 << HWTSTAMP_TX_ON) in 'tx_types' will be set. + * + * Drivers should only report the filters they actually support without + * upscaling in the SIOCSHWTSTAMP ioctl. If the SIOCSHWSTAMP request for + * HWTSTAMP_FILTER_V1_SYNC is supported by HWTSTAMP_FILTER_V1_EVENT, then the + * driver should only report HWTSTAMP_FILTER_V1_EVENT in this op. + */ +struct ethtool_ts_info { + __u32 cmd; + __u32 so_timestamping; + __s32 phc_index; + __u32 tx_types; + __u32 tx_reserved[3]; + __u32 rx_filters; + __u32 rx_reserved[3]; +}; + +/* + * %ETHTOOL_SFEATURES changes features present in features[].valid to the + * values of corresponding bits in features[].requested. Bits in .requested + * not set in .valid or not changeable are ignored. + * + * Returns %EINVAL when .valid contains undefined or never-changeable bits + * or size is not equal to required number of features words (32-bit blocks). + * Returns >= 0 if request was completed; bits set in the value mean: + * %ETHTOOL_F_UNSUPPORTED - there were bits set in .valid that are not + * changeable (not present in %ETHTOOL_GFEATURES' features[].available) + * those bits were ignored. + * %ETHTOOL_F_WISH - some or all changes requested were recorded but the + * resulting state of bits masked by .valid is not equal to .requested. + * Probably there are other device-specific constraints on some features + * in the set. When %ETHTOOL_F_UNSUPPORTED is set, .valid is considered + * here as though ignored bits were cleared. + * %ETHTOOL_F_COMPAT - some or all changes requested were made by calling + * compatibility functions. Requested offload state cannot be properly + * managed by kernel. + * + * Meaning of bits in the masks are obtained by %ETHTOOL_GSSET_INFO (number of + * bits in the arrays - always multiple of 32) and %ETHTOOL_GSTRINGS commands + * for ETH_SS_FEATURES string set. First entry in the table corresponds to least + * significant bit in features[0] fields. Empty strings mark undefined features. + */ +enum ethtool_sfeatures_retval_bits { + ETHTOOL_F_UNSUPPORTED__BIT, + ETHTOOL_F_WISH__BIT, + ETHTOOL_F_COMPAT__BIT, +}; + +#define ETHTOOL_F_UNSUPPORTED (1 << ETHTOOL_F_UNSUPPORTED__BIT) +#define ETHTOOL_F_WISH (1 << ETHTOOL_F_WISH__BIT) +#define ETHTOOL_F_COMPAT (1 << ETHTOOL_F_COMPAT__BIT) + +#define MAX_NUM_QUEUE 4096 + +/** + * struct ethtool_per_queue_op - apply sub command to the queues in mask. + * @cmd: ETHTOOL_PERQUEUE + * @sub_command: the sub command which apply to each queues + * @queue_mask: Bitmap of the queues which sub command apply to + * @data: A complete command structure following for each of the queues addressed + */ +struct ethtool_per_queue_op { + __u32 cmd; + __u32 sub_command; + __u32 queue_mask[__KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32)]; + char data[]; +}; + +/** + * struct ethtool_fecparam - Ethernet Forward Error Correction parameters + * @cmd: Command number = %ETHTOOL_GFECPARAM or %ETHTOOL_SFECPARAM + * @active_fec: FEC mode which is active on the port, single bit set, GET only. + * @fec: Bitmask of configured FEC modes. + * @reserved: Reserved for future extensions, ignore on GET, write 0 for SET. + * + * Note that @reserved was never validated on input and ethtool user space + * left it uninitialized when calling SET. Hence going forward it can only be + * used to return a value to userspace with GET. + * + * FEC modes supported by the device can be read via %ETHTOOL_GLINKSETTINGS. + * FEC settings are configured by link autonegotiation whenever it's enabled. + * With autoneg on %ETHTOOL_GFECPARAM can be used to read the current mode. + * + * When autoneg is disabled %ETHTOOL_SFECPARAM controls the FEC settings. + * It is recommended that drivers only accept a single bit set in @fec. + * When multiple bits are set in @fec drivers may pick mode in an implementation + * dependent way. Drivers should reject mixing %ETHTOOL_FEC_AUTO_BIT with other + * FEC modes, because it's unclear whether in this case other modes constrain + * AUTO or are independent choices. + * Drivers must reject SET requests if they support none of the requested modes. + * + * If device does not support FEC drivers may use %ETHTOOL_FEC_NONE instead + * of returning %EOPNOTSUPP from %ETHTOOL_GFECPARAM. + * + * See enum ethtool_fec_config_bits for definition of valid bits for both + * @fec and @active_fec. + */ +struct ethtool_fecparam { + __u32 cmd; + /* bitmask of FEC modes */ + __u32 active_fec; + __u32 fec; + __u32 reserved; +}; + +/** + * enum ethtool_fec_config_bits - flags definition of ethtool_fec_configuration + * @ETHTOOL_FEC_NONE_BIT: FEC mode configuration is not supported. Should not + * be used together with other bits. GET only. + * @ETHTOOL_FEC_AUTO_BIT: Select default/best FEC mode automatically, usually + * based link mode and SFP parameters read from module's + * EEPROM. This bit does _not_ mean autonegotiation. + * @ETHTOOL_FEC_OFF_BIT: No FEC Mode + * @ETHTOOL_FEC_RS_BIT: Reed-Solomon FEC Mode + * @ETHTOOL_FEC_BASER_BIT: Base-R/Reed-Solomon FEC Mode + * @ETHTOOL_FEC_LLRS_BIT: Low Latency Reed Solomon FEC Mode (25G/50G Ethernet + * Consortium) + */ +enum ethtool_fec_config_bits { + ETHTOOL_FEC_NONE_BIT, + ETHTOOL_FEC_AUTO_BIT, + ETHTOOL_FEC_OFF_BIT, + ETHTOOL_FEC_RS_BIT, + ETHTOOL_FEC_BASER_BIT, + ETHTOOL_FEC_LLRS_BIT, +}; + +#define ETHTOOL_FEC_NONE (1 << ETHTOOL_FEC_NONE_BIT) +#define ETHTOOL_FEC_AUTO (1 << ETHTOOL_FEC_AUTO_BIT) +#define ETHTOOL_FEC_OFF (1 << ETHTOOL_FEC_OFF_BIT) +#define ETHTOOL_FEC_RS (1 << ETHTOOL_FEC_RS_BIT) +#define ETHTOOL_FEC_BASER (1 << ETHTOOL_FEC_BASER_BIT) +#define ETHTOOL_FEC_LLRS (1 << ETHTOOL_FEC_LLRS_BIT) + +/* CMDs currently supported */ +#define ETHTOOL_GSET 0x00000001 /* DEPRECATED, Get settings. + * Please use ETHTOOL_GLINKSETTINGS + */ +#define ETHTOOL_SSET 0x00000002 /* DEPRECATED, Set settings. + * Please use ETHTOOL_SLINKSETTINGS + */ +#define ETHTOOL_GDRVINFO 0x00000003 /* Get driver info. */ +#define ETHTOOL_GREGS 0x00000004 /* Get NIC registers. */ +#define ETHTOOL_GWOL 0x00000005 /* Get wake-on-lan options. */ +#define ETHTOOL_SWOL 0x00000006 /* Set wake-on-lan options. */ +#define ETHTOOL_GMSGLVL 0x00000007 /* Get driver message level */ +#define ETHTOOL_SMSGLVL 0x00000008 /* Set driver msg level. */ +#define ETHTOOL_NWAY_RST 0x00000009 /* Restart autonegotiation. */ +/* Get link status for host, i.e. whether the interface *and* the + * physical port (if there is one) are up (ethtool_value). */ +#define ETHTOOL_GLINK 0x0000000a +#define ETHTOOL_GEEPROM 0x0000000b /* Get EEPROM data */ +#define ETHTOOL_SEEPROM 0x0000000c /* Set EEPROM data. */ +#define ETHTOOL_GCOALESCE 0x0000000e /* Get coalesce config */ +#define ETHTOOL_SCOALESCE 0x0000000f /* Set coalesce config. */ +#define ETHTOOL_GRINGPARAM 0x00000010 /* Get ring parameters */ +#define ETHTOOL_SRINGPARAM 0x00000011 /* Set ring parameters. */ +#define ETHTOOL_GPAUSEPARAM 0x00000012 /* Get pause parameters */ +#define ETHTOOL_SPAUSEPARAM 0x00000013 /* Set pause parameters. */ +#define ETHTOOL_GRXCSUM 0x00000014 /* Get RX hw csum enable (ethtool_value) */ +#define ETHTOOL_SRXCSUM 0x00000015 /* Set RX hw csum enable (ethtool_value) */ +#define ETHTOOL_GTXCSUM 0x00000016 /* Get TX hw csum enable (ethtool_value) */ +#define ETHTOOL_STXCSUM 0x00000017 /* Set TX hw csum enable (ethtool_value) */ +#define ETHTOOL_GSG 0x00000018 /* Get scatter-gather enable + * (ethtool_value) */ +#define ETHTOOL_SSG 0x00000019 /* Set scatter-gather enable + * (ethtool_value). */ +#define ETHTOOL_TEST 0x0000001a /* execute NIC self-test. */ +#define ETHTOOL_GSTRINGS 0x0000001b /* get specified string set */ +#define ETHTOOL_PHYS_ID 0x0000001c /* identify the NIC */ +#define ETHTOOL_GSTATS 0x0000001d /* get NIC-specific statistics */ +#define ETHTOOL_GTSO 0x0000001e /* Get TSO enable (ethtool_value) */ +#define ETHTOOL_STSO 0x0000001f /* Set TSO enable (ethtool_value) */ +#define ETHTOOL_GPERMADDR 0x00000020 /* Get permanent hardware address */ +#define ETHTOOL_GUFO 0x00000021 /* Get UFO enable (ethtool_value) */ +#define ETHTOOL_SUFO 0x00000022 /* Set UFO enable (ethtool_value) */ +#define ETHTOOL_GGSO 0x00000023 /* Get GSO enable (ethtool_value) */ +#define ETHTOOL_SGSO 0x00000024 /* Set GSO enable (ethtool_value) */ +#define ETHTOOL_GFLAGS 0x00000025 /* Get flags bitmap(ethtool_value) */ +#define ETHTOOL_SFLAGS 0x00000026 /* Set flags bitmap(ethtool_value) */ +#define ETHTOOL_GPFLAGS 0x00000027 /* Get driver-private flags bitmap */ +#define ETHTOOL_SPFLAGS 0x00000028 /* Set driver-private flags bitmap */ + +#define ETHTOOL_GRXFH 0x00000029 /* Get RX flow hash configuration */ +#define ETHTOOL_SRXFH 0x0000002a /* Set RX flow hash configuration */ +#define ETHTOOL_GGRO 0x0000002b /* Get GRO enable (ethtool_value) */ +#define ETHTOOL_SGRO 0x0000002c /* Set GRO enable (ethtool_value) */ +#define ETHTOOL_GRXRINGS 0x0000002d /* Get RX rings available for LB */ +#define ETHTOOL_GRXCLSRLCNT 0x0000002e /* Get RX class rule count */ +#define ETHTOOL_GRXCLSRULE 0x0000002f /* Get RX classification rule */ +#define ETHTOOL_GRXCLSRLALL 0x00000030 /* Get all RX classification rule */ +#define ETHTOOL_SRXCLSRLDEL 0x00000031 /* Delete RX classification rule */ +#define ETHTOOL_SRXCLSRLINS 0x00000032 /* Insert RX classification rule */ +#define ETHTOOL_FLASHDEV 0x00000033 /* Flash firmware to device */ +#define ETHTOOL_RESET 0x00000034 /* Reset hardware */ +#define ETHTOOL_SRXNTUPLE 0x00000035 /* Add an n-tuple filter to device */ +#define ETHTOOL_GRXNTUPLE 0x00000036 /* deprecated */ +#define ETHTOOL_GSSET_INFO 0x00000037 /* Get string set info */ +#define ETHTOOL_GRXFHINDIR 0x00000038 /* Get RX flow hash indir'n table */ +#define ETHTOOL_SRXFHINDIR 0x00000039 /* Set RX flow hash indir'n table */ + +#define ETHTOOL_GFEATURES 0x0000003a /* Get device offload settings */ +#define ETHTOOL_SFEATURES 0x0000003b /* Change device offload settings */ +#define ETHTOOL_GCHANNELS 0x0000003c /* Get no of channels */ +#define ETHTOOL_SCHANNELS 0x0000003d /* Set no of channels */ +#define ETHTOOL_SET_DUMP 0x0000003e /* Set dump settings */ +#define ETHTOOL_GET_DUMP_FLAG 0x0000003f /* Get dump settings */ +#define ETHTOOL_GET_DUMP_DATA 0x00000040 /* Get dump data */ +#define ETHTOOL_GET_TS_INFO 0x00000041 /* Get time stamping and PHC info */ +#define ETHTOOL_GMODULEINFO 0x00000042 /* Get plug-in module information */ +#define ETHTOOL_GMODULEEEPROM 0x00000043 /* Get plug-in module eeprom */ +#define ETHTOOL_GEEE 0x00000044 /* Get EEE settings */ +#define ETHTOOL_SEEE 0x00000045 /* Set EEE settings */ + +#define ETHTOOL_GRSSH 0x00000046 /* Get RX flow hash configuration */ +#define ETHTOOL_SRSSH 0x00000047 /* Set RX flow hash configuration */ +#define ETHTOOL_GTUNABLE 0x00000048 /* Get tunable configuration */ +#define ETHTOOL_STUNABLE 0x00000049 /* Set tunable configuration */ +#define ETHTOOL_GPHYSTATS 0x0000004a /* get PHY-specific statistics */ + +#define ETHTOOL_PERQUEUE 0x0000004b /* Set per queue options */ + +#define ETHTOOL_GLINKSETTINGS 0x0000004c /* Get ethtool_link_settings */ +#define ETHTOOL_SLINKSETTINGS 0x0000004d /* Set ethtool_link_settings */ +#define ETHTOOL_PHY_GTUNABLE 0x0000004e /* Get PHY tunable configuration */ +#define ETHTOOL_PHY_STUNABLE 0x0000004f /* Set PHY tunable configuration */ +#define ETHTOOL_GFECPARAM 0x00000050 /* Get FEC settings */ +#define ETHTOOL_SFECPARAM 0x00000051 /* Set FEC settings */ + +/* compatibility with older code */ +#define SPARC_ETH_GSET ETHTOOL_GSET +#define SPARC_ETH_SSET ETHTOOL_SSET + +/* Link mode bit indices */ +enum ethtool_link_mode_bit_indices { + ETHTOOL_LINK_MODE_10baseT_Half_BIT = 0, + ETHTOOL_LINK_MODE_10baseT_Full_BIT = 1, + ETHTOOL_LINK_MODE_100baseT_Half_BIT = 2, + ETHTOOL_LINK_MODE_100baseT_Full_BIT = 3, + ETHTOOL_LINK_MODE_1000baseT_Half_BIT = 4, + ETHTOOL_LINK_MODE_1000baseT_Full_BIT = 5, + ETHTOOL_LINK_MODE_Autoneg_BIT = 6, + ETHTOOL_LINK_MODE_TP_BIT = 7, + ETHTOOL_LINK_MODE_AUI_BIT = 8, + ETHTOOL_LINK_MODE_MII_BIT = 9, + ETHTOOL_LINK_MODE_FIBRE_BIT = 10, + ETHTOOL_LINK_MODE_BNC_BIT = 11, + ETHTOOL_LINK_MODE_10000baseT_Full_BIT = 12, + ETHTOOL_LINK_MODE_Pause_BIT = 13, + ETHTOOL_LINK_MODE_Asym_Pause_BIT = 14, + ETHTOOL_LINK_MODE_2500baseX_Full_BIT = 15, + ETHTOOL_LINK_MODE_Backplane_BIT = 16, + ETHTOOL_LINK_MODE_1000baseKX_Full_BIT = 17, + ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT = 18, + ETHTOOL_LINK_MODE_10000baseKR_Full_BIT = 19, + ETHTOOL_LINK_MODE_10000baseR_FEC_BIT = 20, + ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT = 21, + ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT = 22, + ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT = 23, + ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT = 24, + ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT = 25, + ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT = 26, + ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT = 27, + ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT = 28, + ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT = 29, + ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT = 30, + ETHTOOL_LINK_MODE_25000baseCR_Full_BIT = 31, + + /* Last allowed bit for __ETHTOOL_LINK_MODE_LEGACY_MASK is bit + * 31. Please do NOT define any SUPPORTED_* or ADVERTISED_* + * macro for bits > 31. The only way to use indices > 31 is to + * use the new ETHTOOL_GLINKSETTINGS/ETHTOOL_SLINKSETTINGS API. + */ + + ETHTOOL_LINK_MODE_25000baseKR_Full_BIT = 32, + ETHTOOL_LINK_MODE_25000baseSR_Full_BIT = 33, + ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT = 34, + ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT = 35, + ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT = 36, + ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT = 37, + ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT = 38, + ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT = 39, + ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT = 40, + ETHTOOL_LINK_MODE_1000baseX_Full_BIT = 41, + ETHTOOL_LINK_MODE_10000baseCR_Full_BIT = 42, + ETHTOOL_LINK_MODE_10000baseSR_Full_BIT = 43, + ETHTOOL_LINK_MODE_10000baseLR_Full_BIT = 44, + ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT = 45, + ETHTOOL_LINK_MODE_10000baseER_Full_BIT = 46, + ETHTOOL_LINK_MODE_2500baseT_Full_BIT = 47, + ETHTOOL_LINK_MODE_5000baseT_Full_BIT = 48, + + ETHTOOL_LINK_MODE_FEC_NONE_BIT = 49, + ETHTOOL_LINK_MODE_FEC_RS_BIT = 50, + ETHTOOL_LINK_MODE_FEC_BASER_BIT = 51, + ETHTOOL_LINK_MODE_50000baseKR_Full_BIT = 52, + ETHTOOL_LINK_MODE_50000baseSR_Full_BIT = 53, + ETHTOOL_LINK_MODE_50000baseCR_Full_BIT = 54, + ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT = 55, + ETHTOOL_LINK_MODE_50000baseDR_Full_BIT = 56, + ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT = 57, + ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT = 58, + ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT = 59, + ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT = 60, + ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT = 61, + ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT = 62, + ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT = 63, + ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT = 64, + ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT = 65, + ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT = 66, + ETHTOOL_LINK_MODE_100baseT1_Full_BIT = 67, + ETHTOOL_LINK_MODE_1000baseT1_Full_BIT = 68, + ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT = 69, + ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT = 70, + ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT = 71, + ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT = 72, + ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT = 73, + ETHTOOL_LINK_MODE_FEC_LLRS_BIT = 74, + ETHTOOL_LINK_MODE_100000baseKR_Full_BIT = 75, + ETHTOOL_LINK_MODE_100000baseSR_Full_BIT = 76, + ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT = 77, + ETHTOOL_LINK_MODE_100000baseCR_Full_BIT = 78, + ETHTOOL_LINK_MODE_100000baseDR_Full_BIT = 79, + ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT = 80, + ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT = 81, + ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT = 82, + ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT = 83, + ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT = 84, + ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT = 85, + ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT = 86, + ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT = 87, + ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT = 88, + ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT = 89, + ETHTOOL_LINK_MODE_100baseFX_Half_BIT = 90, + ETHTOOL_LINK_MODE_100baseFX_Full_BIT = 91, + ETHTOOL_LINK_MODE_10baseT1L_Full_BIT = 92, + ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT = 93, + ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT = 94, + ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT = 95, + ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT = 96, + ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT = 97, + ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT = 98, + ETHTOOL_LINK_MODE_10baseT1S_Full_BIT = 99, + ETHTOOL_LINK_MODE_10baseT1S_Half_BIT = 100, + ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT = 101, + + /* must be last entry */ + __ETHTOOL_LINK_MODE_MASK_NBITS +}; + +#define __ETHTOOL_LINK_MODE_LEGACY_MASK(base_name) \ + (1UL << (ETHTOOL_LINK_MODE_ ## base_name ## _BIT)) + +/* DEPRECATED macros. Please migrate to + * ETHTOOL_GLINKSETTINGS/ETHTOOL_SLINKSETTINGS API. Please do NOT + * define any new SUPPORTED_* macro for bits > 31. + */ +#define SUPPORTED_10baseT_Half __ETHTOOL_LINK_MODE_LEGACY_MASK(10baseT_Half) +#define SUPPORTED_10baseT_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(10baseT_Full) +#define SUPPORTED_100baseT_Half __ETHTOOL_LINK_MODE_LEGACY_MASK(100baseT_Half) +#define SUPPORTED_100baseT_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(100baseT_Full) +#define SUPPORTED_1000baseT_Half __ETHTOOL_LINK_MODE_LEGACY_MASK(1000baseT_Half) +#define SUPPORTED_1000baseT_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(1000baseT_Full) +#define SUPPORTED_Autoneg __ETHTOOL_LINK_MODE_LEGACY_MASK(Autoneg) +#define SUPPORTED_TP __ETHTOOL_LINK_MODE_LEGACY_MASK(TP) +#define SUPPORTED_AUI __ETHTOOL_LINK_MODE_LEGACY_MASK(AUI) +#define SUPPORTED_MII __ETHTOOL_LINK_MODE_LEGACY_MASK(MII) +#define SUPPORTED_FIBRE __ETHTOOL_LINK_MODE_LEGACY_MASK(FIBRE) +#define SUPPORTED_BNC __ETHTOOL_LINK_MODE_LEGACY_MASK(BNC) +#define SUPPORTED_10000baseT_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(10000baseT_Full) +#define SUPPORTED_Pause __ETHTOOL_LINK_MODE_LEGACY_MASK(Pause) +#define SUPPORTED_Asym_Pause __ETHTOOL_LINK_MODE_LEGACY_MASK(Asym_Pause) +#define SUPPORTED_2500baseX_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(2500baseX_Full) +#define SUPPORTED_Backplane __ETHTOOL_LINK_MODE_LEGACY_MASK(Backplane) +#define SUPPORTED_1000baseKX_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(1000baseKX_Full) +#define SUPPORTED_10000baseKX4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(10000baseKX4_Full) +#define SUPPORTED_10000baseKR_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(10000baseKR_Full) +#define SUPPORTED_10000baseR_FEC __ETHTOOL_LINK_MODE_LEGACY_MASK(10000baseR_FEC) +#define SUPPORTED_20000baseMLD2_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(20000baseMLD2_Full) +#define SUPPORTED_20000baseKR2_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(20000baseKR2_Full) +#define SUPPORTED_40000baseKR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(40000baseKR4_Full) +#define SUPPORTED_40000baseCR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(40000baseCR4_Full) +#define SUPPORTED_40000baseSR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(40000baseSR4_Full) +#define SUPPORTED_40000baseLR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(40000baseLR4_Full) +#define SUPPORTED_56000baseKR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(56000baseKR4_Full) +#define SUPPORTED_56000baseCR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(56000baseCR4_Full) +#define SUPPORTED_56000baseSR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(56000baseSR4_Full) +#define SUPPORTED_56000baseLR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(56000baseLR4_Full) +/* Please do not define any new SUPPORTED_* macro for bits > 31, see + * notice above. + */ + +/* + * DEPRECATED macros. Please migrate to + * ETHTOOL_GLINKSETTINGS/ETHTOOL_SLINKSETTINGS API. Please do NOT + * define any new ADERTISE_* macro for bits > 31. + */ +#define ADVERTISED_10baseT_Half __ETHTOOL_LINK_MODE_LEGACY_MASK(10baseT_Half) +#define ADVERTISED_10baseT_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(10baseT_Full) +#define ADVERTISED_100baseT_Half __ETHTOOL_LINK_MODE_LEGACY_MASK(100baseT_Half) +#define ADVERTISED_100baseT_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(100baseT_Full) +#define ADVERTISED_1000baseT_Half __ETHTOOL_LINK_MODE_LEGACY_MASK(1000baseT_Half) +#define ADVERTISED_1000baseT_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(1000baseT_Full) +#define ADVERTISED_Autoneg __ETHTOOL_LINK_MODE_LEGACY_MASK(Autoneg) +#define ADVERTISED_TP __ETHTOOL_LINK_MODE_LEGACY_MASK(TP) +#define ADVERTISED_AUI __ETHTOOL_LINK_MODE_LEGACY_MASK(AUI) +#define ADVERTISED_MII __ETHTOOL_LINK_MODE_LEGACY_MASK(MII) +#define ADVERTISED_FIBRE __ETHTOOL_LINK_MODE_LEGACY_MASK(FIBRE) +#define ADVERTISED_BNC __ETHTOOL_LINK_MODE_LEGACY_MASK(BNC) +#define ADVERTISED_10000baseT_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(10000baseT_Full) +#define ADVERTISED_Pause __ETHTOOL_LINK_MODE_LEGACY_MASK(Pause) +#define ADVERTISED_Asym_Pause __ETHTOOL_LINK_MODE_LEGACY_MASK(Asym_Pause) +#define ADVERTISED_2500baseX_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(2500baseX_Full) +#define ADVERTISED_Backplane __ETHTOOL_LINK_MODE_LEGACY_MASK(Backplane) +#define ADVERTISED_1000baseKX_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(1000baseKX_Full) +#define ADVERTISED_10000baseKX4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(10000baseKX4_Full) +#define ADVERTISED_10000baseKR_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(10000baseKR_Full) +#define ADVERTISED_10000baseR_FEC __ETHTOOL_LINK_MODE_LEGACY_MASK(10000baseR_FEC) +#define ADVERTISED_20000baseMLD2_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(20000baseMLD2_Full) +#define ADVERTISED_20000baseKR2_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(20000baseKR2_Full) +#define ADVERTISED_40000baseKR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(40000baseKR4_Full) +#define ADVERTISED_40000baseCR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(40000baseCR4_Full) +#define ADVERTISED_40000baseSR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(40000baseSR4_Full) +#define ADVERTISED_40000baseLR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(40000baseLR4_Full) +#define ADVERTISED_56000baseKR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(56000baseKR4_Full) +#define ADVERTISED_56000baseCR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(56000baseCR4_Full) +#define ADVERTISED_56000baseSR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(56000baseSR4_Full) +#define ADVERTISED_56000baseLR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(56000baseLR4_Full) +/* Please do not define any new ADVERTISED_* macro for bits > 31, see + * notice above. + */ + +/* The following are all involved in forcing a particular link + * mode for the device for setting things. When getting the + * devices settings, these indicate the current mode and whether + * it was forced up into this mode or autonegotiated. + */ + +/* The forced speed, in units of 1Mb. All values 0 to INT_MAX are legal. + * Update drivers/net/phy/phy.c:phy_speed_to_str() and + * drivers/net/bonding/bond_3ad.c:__get_link_speed() when adding new values. + */ +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 +#define SPEED_2500 2500 +#define SPEED_5000 5000 +#define SPEED_10000 10000 +#define SPEED_14000 14000 +#define SPEED_20000 20000 +#define SPEED_25000 25000 +#define SPEED_40000 40000 +#define SPEED_50000 50000 +#define SPEED_56000 56000 +#define SPEED_100000 100000 +#define SPEED_200000 200000 +#define SPEED_400000 400000 +#define SPEED_800000 800000 + +#define SPEED_UNKNOWN -1 + +static __inline__ int ethtool_validate_speed(__u32 speed) +{ + return speed <= INT_MAX || speed == (__u32)SPEED_UNKNOWN; +} + +/* Duplex, half or full. */ +#define DUPLEX_HALF 0x00 +#define DUPLEX_FULL 0x01 +#define DUPLEX_UNKNOWN 0xff + +static __inline__ int ethtool_validate_duplex(__u8 duplex) +{ + switch (duplex) { + case DUPLEX_HALF: + case DUPLEX_FULL: + case DUPLEX_UNKNOWN: + return 1; + } + + return 0; +} + +#define MASTER_SLAVE_CFG_UNSUPPORTED 0 +#define MASTER_SLAVE_CFG_UNKNOWN 1 +#define MASTER_SLAVE_CFG_MASTER_PREFERRED 2 +#define MASTER_SLAVE_CFG_SLAVE_PREFERRED 3 +#define MASTER_SLAVE_CFG_MASTER_FORCE 4 +#define MASTER_SLAVE_CFG_SLAVE_FORCE 5 +#define MASTER_SLAVE_STATE_UNSUPPORTED 0 +#define MASTER_SLAVE_STATE_UNKNOWN 1 +#define MASTER_SLAVE_STATE_MASTER 2 +#define MASTER_SLAVE_STATE_SLAVE 3 +#define MASTER_SLAVE_STATE_ERR 4 + +/* These are used to throttle the rate of data on the phy interface when the + * native speed of the interface is higher than the link speed. These should + * not be used for phy interfaces which natively support multiple speeds (e.g. + * MII or SGMII). + */ +/* No rate matching performed. */ +#define RATE_MATCH_NONE 0 +/* The phy sends pause frames to throttle the MAC. */ +#define RATE_MATCH_PAUSE 1 +/* The phy asserts CRS to prevent the MAC from transmitting. */ +#define RATE_MATCH_CRS 2 +/* The MAC is programmed with a sufficiently-large IPG. */ +#define RATE_MATCH_OPEN_LOOP 3 + +/* Which connector port. */ +#define PORT_TP 0x00 +#define PORT_AUI 0x01 +#define PORT_MII 0x02 +#define PORT_FIBRE 0x03 +#define PORT_BNC 0x04 +#define PORT_DA 0x05 +#define PORT_NONE 0xef +#define PORT_OTHER 0xff + +/* Which transceiver to use. */ +#define XCVR_INTERNAL 0x00 /* PHY and MAC are in the same package */ +#define XCVR_EXTERNAL 0x01 /* PHY and MAC are in different packages */ +#define XCVR_DUMMY1 0x02 +#define XCVR_DUMMY2 0x03 +#define XCVR_DUMMY3 0x04 + +/* Enable or disable autonegotiation. */ +#define AUTONEG_DISABLE 0x00 +#define AUTONEG_ENABLE 0x01 + +/* MDI or MDI-X status/control - if MDI/MDI_X/AUTO is set then + * the driver is required to renegotiate link + */ +#define ETH_TP_MDI_INVALID 0x00 /* status: unknown; control: unsupported */ +#define ETH_TP_MDI 0x01 /* status: MDI; control: force MDI */ +#define ETH_TP_MDI_X 0x02 /* status: MDI-X; control: force MDI-X */ +#define ETH_TP_MDI_AUTO 0x03 /* control: auto-select */ + +/* Wake-On-Lan options. */ +#define WAKE_PHY (1 << 0) +#define WAKE_UCAST (1 << 1) +#define WAKE_MCAST (1 << 2) +#define WAKE_BCAST (1 << 3) +#define WAKE_ARP (1 << 4) +#define WAKE_MAGIC (1 << 5) +#define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */ +#define WAKE_FILTER (1 << 7) + +#define WOL_MODE_COUNT 8 + +/* L2-L4 network traffic flow types */ +#define TCP_V4_FLOW 0x01 /* hash or spec (tcp_ip4_spec) */ +#define UDP_V4_FLOW 0x02 /* hash or spec (udp_ip4_spec) */ +#define SCTP_V4_FLOW 0x03 /* hash or spec (sctp_ip4_spec) */ +#define AH_ESP_V4_FLOW 0x04 /* hash only */ +#define TCP_V6_FLOW 0x05 /* hash or spec (tcp_ip6_spec; nfc only) */ +#define UDP_V6_FLOW 0x06 /* hash or spec (udp_ip6_spec; nfc only) */ +#define SCTP_V6_FLOW 0x07 /* hash or spec (sctp_ip6_spec; nfc only) */ +#define AH_ESP_V6_FLOW 0x08 /* hash only */ +#define AH_V4_FLOW 0x09 /* hash or spec (ah_ip4_spec) */ +#define ESP_V4_FLOW 0x0a /* hash or spec (esp_ip4_spec) */ +#define AH_V6_FLOW 0x0b /* hash or spec (ah_ip6_spec; nfc only) */ +#define ESP_V6_FLOW 0x0c /* hash or spec (esp_ip6_spec; nfc only) */ +#define IPV4_USER_FLOW 0x0d /* spec only (usr_ip4_spec) */ +#define IP_USER_FLOW IPV4_USER_FLOW +#define IPV6_USER_FLOW 0x0e /* spec only (usr_ip6_spec; nfc only) */ +#define IPV4_FLOW 0x10 /* hash only */ +#define IPV6_FLOW 0x11 /* hash only */ +#define ETHER_FLOW 0x12 /* spec only (ether_spec) */ +/* Flag to enable additional fields in struct ethtool_rx_flow_spec */ +#define FLOW_EXT 0x80000000 +#define FLOW_MAC_EXT 0x40000000 +/* Flag to enable RSS spreading of traffic matching rule (nfc only) */ +#define FLOW_RSS 0x20000000 + +/* L3-L4 network traffic flow hash options */ +#define RXH_L2DA (1 << 1) +#define RXH_VLAN (1 << 2) +#define RXH_L3_PROTO (1 << 3) +#define RXH_IP_SRC (1 << 4) +#define RXH_IP_DST (1 << 5) +#define RXH_L4_B_0_1 (1 << 6) /* src port in case of TCP/UDP/SCTP */ +#define RXH_L4_B_2_3 (1 << 7) /* dst port in case of TCP/UDP/SCTP */ +#define RXH_DISCARD (1 << 31) + +#define RX_CLS_FLOW_DISC 0xffffffffffffffffULL +#define RX_CLS_FLOW_WAKE 0xfffffffffffffffeULL + +/* Special RX classification rule insert location values */ +#define RX_CLS_LOC_SPECIAL 0x80000000 /* flag */ +#define RX_CLS_LOC_ANY 0xffffffff +#define RX_CLS_LOC_FIRST 0xfffffffe +#define RX_CLS_LOC_LAST 0xfffffffd + +/* EEPROM Standards for plug in modules */ +#define ETH_MODULE_SFF_8079 0x1 +#define ETH_MODULE_SFF_8079_LEN 256 +#define ETH_MODULE_SFF_8472 0x2 +#define ETH_MODULE_SFF_8472_LEN 512 +#define ETH_MODULE_SFF_8636 0x3 +#define ETH_MODULE_SFF_8636_LEN 256 +#define ETH_MODULE_SFF_8436 0x4 +#define ETH_MODULE_SFF_8436_LEN 256 + +#define ETH_MODULE_SFF_8636_MAX_LEN 640 +#define ETH_MODULE_SFF_8436_MAX_LEN 640 + +/* Reset flags */ +/* The reset() operation must clear the flags for the components which + * were actually reset. On successful return, the flags indicate the + * components which were not reset, either because they do not exist + * in the hardware or because they cannot be reset independently. The + * driver must never reset any components that were not requested. + */ +enum ethtool_reset_flags { + /* These flags represent components dedicated to the interface + * the command is addressed to. Shift any flag left by + * ETH_RESET_SHARED_SHIFT to reset a shared component of the + * same type. + */ + ETH_RESET_MGMT = 1 << 0, /* Management processor */ + ETH_RESET_IRQ = 1 << 1, /* Interrupt requester */ + ETH_RESET_DMA = 1 << 2, /* DMA engine */ + ETH_RESET_FILTER = 1 << 3, /* Filtering/flow direction */ + ETH_RESET_OFFLOAD = 1 << 4, /* Protocol offload */ + ETH_RESET_MAC = 1 << 5, /* Media access controller */ + ETH_RESET_PHY = 1 << 6, /* Transceiver/PHY */ + ETH_RESET_RAM = 1 << 7, /* RAM shared between + * multiple components */ + ETH_RESET_AP = 1 << 8, /* Application processor */ + + ETH_RESET_DEDICATED = 0x0000ffff, /* All components dedicated to + * this interface */ + ETH_RESET_ALL = 0xffffffff, /* All components used by this + * interface, even if shared */ +}; +#define ETH_RESET_SHARED_SHIFT 16 + + +/** + * struct ethtool_link_settings - link control and status + * + * IMPORTANT, Backward compatibility notice: When implementing new + * user-space tools, please first try %ETHTOOL_GLINKSETTINGS, and + * if it succeeds use %ETHTOOL_SLINKSETTINGS to change link + * settings; do not use %ETHTOOL_SSET if %ETHTOOL_GLINKSETTINGS + * succeeded: stick to %ETHTOOL_GLINKSETTINGS/%SLINKSETTINGS in + * that case. Conversely, if %ETHTOOL_GLINKSETTINGS fails, use + * %ETHTOOL_GSET to query and %ETHTOOL_SSET to change link + * settings; do not use %ETHTOOL_SLINKSETTINGS if + * %ETHTOOL_GLINKSETTINGS failed: stick to + * %ETHTOOL_GSET/%ETHTOOL_SSET in that case. + * + * @cmd: Command number = %ETHTOOL_GLINKSETTINGS or %ETHTOOL_SLINKSETTINGS + * @speed: Link speed (Mbps) + * @duplex: Duplex mode; one of %DUPLEX_* + * @port: Physical connector type; one of %PORT_* + * @phy_address: MDIO address of PHY (transceiver); 0 or 255 if not + * applicable. For clause 45 PHYs this is the PRTAD. + * @autoneg: Enable/disable autonegotiation and auto-detection; + * either %AUTONEG_DISABLE or %AUTONEG_ENABLE + * @mdio_support: Bitmask of %ETH_MDIO_SUPPORTS_* flags for the MDIO + * protocols supported by the interface; 0 if unknown. + * Read-only. + * @eth_tp_mdix: Ethernet twisted-pair MDI(-X) status; one of + * %ETH_TP_MDI_*. If the status is unknown or not applicable, the + * value will be %ETH_TP_MDI_INVALID. Read-only. + * @eth_tp_mdix_ctrl: Ethernet twisted pair MDI(-X) control; one of + * %ETH_TP_MDI_*. If MDI(-X) control is not implemented, reads + * yield %ETH_TP_MDI_INVALID and writes may be ignored or rejected. + * When written successfully, the link should be renegotiated if + * necessary. + * @link_mode_masks_nwords: Number of 32-bit words for each of the + * supported, advertising, lp_advertising link mode bitmaps. For + * %ETHTOOL_GLINKSETTINGS: on entry, number of words passed by user + * (>= 0); on return, if handshake in progress, negative if + * request size unsupported by kernel: absolute value indicates + * kernel expected size and all the other fields but cmd + * are 0; otherwise (handshake completed), strictly positive + * to indicate size used by kernel and cmd field stays + * %ETHTOOL_GLINKSETTINGS, all other fields populated by driver. For + * %ETHTOOL_SLINKSETTINGS: must be valid on entry, ie. a positive + * value returned previously by %ETHTOOL_GLINKSETTINGS, otherwise + * refused. For drivers: ignore this field (use kernel's + * __ETHTOOL_LINK_MODE_MASK_NBITS instead), any change to it will + * be overwritten by kernel. + * @supported: Bitmap with each bit meaning given by + * %ethtool_link_mode_bit_indices for the link modes, physical + * connectors and other link features for which the interface + * supports autonegotiation or auto-detection. Read-only. + * @advertising: Bitmap with each bit meaning given by + * %ethtool_link_mode_bit_indices for the link modes, physical + * connectors and other link features that are advertised through + * autonegotiation or enabled for auto-detection. + * @lp_advertising: Bitmap with each bit meaning given by + * %ethtool_link_mode_bit_indices for the link modes, and other + * link features that the link partner advertised through + * autonegotiation; 0 if unknown or not applicable. Read-only. + * @transceiver: Used to distinguish different possible PHY types, + * reported consistently by PHYLIB. Read-only. + * @master_slave_cfg: Master/slave port mode. + * @master_slave_state: Master/slave port state. + * @rate_matching: Rate adaptation performed by the PHY + * @reserved: Reserved for future use; see the note on reserved space. + * @link_mode_masks: Variable length bitmaps. + * + * If autonegotiation is disabled, the speed and @duplex represent the + * fixed link mode and are writable if the driver supports multiple + * link modes. If it is enabled then they are read-only; if the link + * is up they represent the negotiated link mode; if the link is down, + * the speed is 0, %SPEED_UNKNOWN or the highest enabled speed and + * @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode. + * + * Some hardware interfaces may have multiple PHYs and/or physical + * connectors fitted or do not allow the driver to detect which are + * fitted. For these interfaces @port and/or @phy_address may be + * writable, possibly dependent on @autoneg being %AUTONEG_DISABLE. + * Otherwise, attempts to write different values may be ignored or + * rejected. + * + * Deprecated %ethtool_cmd fields transceiver, maxtxpkt and maxrxpkt + * are not available in %ethtool_link_settings. These fields will be + * always set to zero in %ETHTOOL_GSET reply and %ETHTOOL_SSET will + * fail if any of them is set to non-zero value. + * + * Users should assume that all fields not marked read-only are + * writable and subject to validation by the driver. They should use + * %ETHTOOL_GLINKSETTINGS to get the current values before making specific + * changes and then applying them with %ETHTOOL_SLINKSETTINGS. + * + * Drivers that implement %get_link_ksettings and/or + * %set_link_ksettings should ignore the @cmd + * and @link_mode_masks_nwords fields (any change to them overwritten + * by kernel), and rely only on kernel's internal + * %__ETHTOOL_LINK_MODE_MASK_NBITS and + * %ethtool_link_mode_mask_t. Drivers that implement + * %set_link_ksettings() should validate all fields other than @cmd + * and @link_mode_masks_nwords that are not described as read-only or + * deprecated, and must ignore all fields described as read-only. + */ +struct ethtool_link_settings { + __u32 cmd; + __u32 speed; + __u8 duplex; + __u8 port; + __u8 phy_address; + __u8 autoneg; + __u8 mdio_support; + __u8 eth_tp_mdix; + __u8 eth_tp_mdix_ctrl; + __s8 link_mode_masks_nwords; + __u8 transceiver; + __u8 master_slave_cfg; + __u8 master_slave_state; + __u8 rate_matching; + __u32 reserved[7]; + __u32 link_mode_masks[]; + /* layout of link_mode_masks fields: + * __u32 map_supported[link_mode_masks_nwords]; + * __u32 map_advertising[link_mode_masks_nwords]; + * __u32 map_lp_advertising[link_mode_masks_nwords]; + */ +}; +#endif /* _LINUX_ETHTOOL_H */ diff --git a/libnl/include/linux-private/linux/falloc.h b/libnl/include/linux-private/linux/falloc.h new file mode 100644 index 0000000..ca3904d --- /dev/null +++ b/libnl/include/linux-private/linux/falloc.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _FALLOC_H_ +#define _FALLOC_H_ + +#define FALLOC_FL_KEEP_SIZE 0x01 /* default is extend size */ +#define FALLOC_FL_PUNCH_HOLE 0x02 /* de-allocates range */ +#define FALLOC_FL_NO_HIDE_STALE 0x04 /* reserved codepoint */ + +/* + * FALLOC_FL_COLLAPSE_RANGE is used to remove a range of a file + * without leaving a hole in the file. The contents of the file beyond + * the range being removed is appended to the start offset of the range + * being removed (i.e. the hole that was punched is "collapsed"), + * resulting in a file layout that looks like the range that was + * removed never existed. As such collapsing a range of a file changes + * the size of the file, reducing it by the same length of the range + * that has been removed by the operation. + * + * Different filesystems may implement different limitations on the + * granularity of the operation. Most will limit operations to + * filesystem block size boundaries, but this boundary may be larger or + * smaller depending on the filesystem and/or the configuration of the + * filesystem or file. + * + * Attempting to collapse a range that crosses the end of the file is + * considered an illegal operation - just use ftruncate(2) if you need + * to collapse a range that crosses EOF. + */ +#define FALLOC_FL_COLLAPSE_RANGE 0x08 + +/* + * FALLOC_FL_ZERO_RANGE is used to convert a range of file to zeros preferably + * without issuing data IO. Blocks should be preallocated for the regions that + * span holes in the file, and the entire range is preferable converted to + * unwritten extents - even though file system may choose to zero out the + * extent or do whatever which will result in reading zeros from the range + * while the range remains allocated for the file. + * + * This can be also used to preallocate blocks past EOF in the same way as + * with fallocate. Flag FALLOC_FL_KEEP_SIZE should cause the inode + * size to remain the same. + */ +#define FALLOC_FL_ZERO_RANGE 0x10 + +/* + * FALLOC_FL_INSERT_RANGE is use to insert space within the file size without + * overwriting any existing data. The contents of the file beyond offset are + * shifted towards right by len bytes to create a hole. As such, this + * operation will increase the size of the file by len bytes. + * + * Different filesystems may implement different limitations on the granularity + * of the operation. Most will limit operations to filesystem block size + * boundaries, but this boundary may be larger or smaller depending on + * the filesystem and/or the configuration of the filesystem or file. + * + * Attempting to insert space using this flag at OR beyond the end of + * the file is considered an illegal operation - just use ftruncate(2) or + * fallocate(2) with mode 0 for such type of operations. + */ +#define FALLOC_FL_INSERT_RANGE 0x20 + +/* + * FALLOC_FL_UNSHARE_RANGE is used to unshare shared blocks within the + * file size without overwriting any existing data. The purpose of this + * call is to preemptively reallocate any blocks that are subject to + * copy-on-write. + * + * Different filesystems may implement different limitations on the + * granularity of the operation. Most will limit operations to filesystem + * block size boundaries, but this boundary may be larger or smaller + * depending on the filesystem and/or the configuration of the filesystem + * or file. + * + * This flag can only be used with allocate-mode fallocate, which is + * to say that it cannot be used with the punch, zero, collapse, or + * insert range modes. + */ +#define FALLOC_FL_UNSHARE_RANGE 0x40 + +#endif /* _FALLOC_H_ */ diff --git a/libnl/include/linux-private/linux/fib_rules.h b/libnl/include/linux-private/linux/fib_rules.h new file mode 100644 index 0000000..232df14 --- /dev/null +++ b/libnl/include/linux-private/linux/fib_rules.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_FIB_RULES_H +#define __LINUX_FIB_RULES_H + +#include +#include + +/* rule is permanent, and cannot be deleted */ +#define FIB_RULE_PERMANENT 0x00000001 +#define FIB_RULE_INVERT 0x00000002 +#define FIB_RULE_UNRESOLVED 0x00000004 +#define FIB_RULE_IIF_DETACHED 0x00000008 +#define FIB_RULE_DEV_DETACHED FIB_RULE_IIF_DETACHED +#define FIB_RULE_OIF_DETACHED 0x00000010 + +/* try to find source address in routing lookups */ +#define FIB_RULE_FIND_SADDR 0x00010000 + +struct fib_rule_hdr { + __u8 family; + __u8 dst_len; + __u8 src_len; + __u8 tos; + + __u8 table; + __u8 res1; /* reserved */ + __u8 res2; /* reserved */ + __u8 action; + + __u32 flags; +}; + +struct fib_rule_uid_range { + __u32 start; + __u32 end; +}; + +struct fib_rule_port_range { + __u16 start; + __u16 end; +}; + +enum { + FRA_UNSPEC, + FRA_DST, /* destination address */ + FRA_SRC, /* source address */ + FRA_IIFNAME, /* interface name */ +#define FRA_IFNAME FRA_IIFNAME + FRA_GOTO, /* target to jump to (FR_ACT_GOTO) */ + FRA_UNUSED2, + FRA_PRIORITY, /* priority/preference */ + FRA_UNUSED3, + FRA_UNUSED4, + FRA_UNUSED5, + FRA_FWMARK, /* mark */ + FRA_FLOW, /* flow/class id */ + FRA_TUN_ID, + FRA_SUPPRESS_IFGROUP, + FRA_SUPPRESS_PREFIXLEN, + FRA_TABLE, /* Extended table id */ + FRA_FWMASK, /* mask for netfilter mark */ + FRA_OIFNAME, + FRA_PAD, + FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ + FRA_UID_RANGE, /* UID range */ + FRA_PROTOCOL, /* Originator of the rule */ + FRA_IP_PROTO, /* ip proto */ + FRA_SPORT_RANGE, /* sport */ + FRA_DPORT_RANGE, /* dport */ + __FRA_MAX +}; + +#define FRA_MAX (__FRA_MAX - 1) + +enum { + FR_ACT_UNSPEC, + FR_ACT_TO_TBL, /* Pass to fixed table */ + FR_ACT_GOTO, /* Jump to another rule */ + FR_ACT_NOP, /* No operation */ + FR_ACT_RES3, + FR_ACT_RES4, + FR_ACT_BLACKHOLE, /* Drop without notification */ + FR_ACT_UNREACHABLE, /* Drop with ENETUNREACH */ + FR_ACT_PROHIBIT, /* Drop with EACCES */ + __FR_ACT_MAX, +}; + +#define FR_ACT_MAX (__FR_ACT_MAX - 1) + +#endif diff --git a/libnl/include/linux-private/linux/gen_stats.h b/libnl/include/linux-private/linux/gen_stats.h new file mode 100644 index 0000000..852f234 --- /dev/null +++ b/libnl/include/linux-private/linux/gen_stats.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_GEN_STATS_H +#define __LINUX_GEN_STATS_H + +#include + +enum { + TCA_STATS_UNSPEC, + TCA_STATS_BASIC, + TCA_STATS_RATE_EST, + TCA_STATS_QUEUE, + TCA_STATS_APP, + TCA_STATS_RATE_EST64, + TCA_STATS_PAD, + TCA_STATS_BASIC_HW, + TCA_STATS_PKT64, + __TCA_STATS_MAX, +}; +#define TCA_STATS_MAX (__TCA_STATS_MAX - 1) + +/** + * struct gnet_stats_basic - byte/packet throughput statistics + * @bytes: number of seen bytes + * @packets: number of seen packets + */ +struct gnet_stats_basic { + __u64 bytes; + __u32 packets; +}; + +/** + * struct gnet_stats_rate_est - rate estimator + * @bps: current byte rate + * @pps: current packet rate + */ +struct gnet_stats_rate_est { + __u32 bps; + __u32 pps; +}; + +/** + * struct gnet_stats_rate_est64 - rate estimator + * @bps: current byte rate + * @pps: current packet rate + */ +struct gnet_stats_rate_est64 { + __u64 bps; + __u64 pps; +}; + +/** + * struct gnet_stats_queue - queuing statistics + * @qlen: queue length + * @backlog: backlog size of queue + * @drops: number of dropped packets + * @requeues: number of requeues + * @overlimits: number of enqueues over the limit + */ +struct gnet_stats_queue { + __u32 qlen; + __u32 backlog; + __u32 drops; + __u32 requeues; + __u32 overlimits; +}; + +/** + * struct gnet_estimator - rate estimator configuration + * @interval: sampling period + * @ewma_log: the log of measurement window weight + */ +struct gnet_estimator { + signed char interval; + unsigned char ewma_log; +}; + + +#endif /* __LINUX_GEN_STATS_H */ diff --git a/libnl/include/linux-private/linux/genetlink.h b/libnl/include/linux-private/linux/genetlink.h new file mode 100644 index 0000000..e9b8117 --- /dev/null +++ b/libnl/include/linux-private/linux/genetlink.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_GENERIC_NETLINK_H +#define __LINUX_GENERIC_NETLINK_H + +#include +#include + +#define GENL_NAMSIZ 16 /* length of family name */ + +#define GENL_MIN_ID NLMSG_MIN_TYPE +#define GENL_MAX_ID 1023 + +struct genlmsghdr { + __u8 cmd; + __u8 version; + __u16 reserved; +}; + +#define GENL_HDRLEN NLMSG_ALIGN(sizeof(struct genlmsghdr)) + +#define GENL_ADMIN_PERM 0x01 +#define GENL_CMD_CAP_DO 0x02 +#define GENL_CMD_CAP_DUMP 0x04 +#define GENL_CMD_CAP_HASPOL 0x08 +#define GENL_UNS_ADMIN_PERM 0x10 + +/* + * List of reserved static generic netlink identifiers: + */ +#define GENL_ID_CTRL NLMSG_MIN_TYPE +#define GENL_ID_VFS_DQUOT (NLMSG_MIN_TYPE + 1) +#define GENL_ID_PMCRAID (NLMSG_MIN_TYPE + 2) +/* must be last reserved + 1 */ +#define GENL_START_ALLOC (NLMSG_MIN_TYPE + 3) + +/************************************************************************** + * Controller + **************************************************************************/ + +enum { + CTRL_CMD_UNSPEC, + CTRL_CMD_NEWFAMILY, + CTRL_CMD_DELFAMILY, + CTRL_CMD_GETFAMILY, + CTRL_CMD_NEWOPS, + CTRL_CMD_DELOPS, + CTRL_CMD_GETOPS, + CTRL_CMD_NEWMCAST_GRP, + CTRL_CMD_DELMCAST_GRP, + CTRL_CMD_GETMCAST_GRP, /* unused */ + CTRL_CMD_GETPOLICY, + __CTRL_CMD_MAX, +}; + +#define CTRL_CMD_MAX (__CTRL_CMD_MAX - 1) + +enum { + CTRL_ATTR_UNSPEC, + CTRL_ATTR_FAMILY_ID, + CTRL_ATTR_FAMILY_NAME, + CTRL_ATTR_VERSION, + CTRL_ATTR_HDRSIZE, + CTRL_ATTR_MAXATTR, + CTRL_ATTR_OPS, + CTRL_ATTR_MCAST_GROUPS, + CTRL_ATTR_POLICY, + CTRL_ATTR_OP_POLICY, + CTRL_ATTR_OP, + __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) + +enum { + CTRL_ATTR_POLICY_UNSPEC, + CTRL_ATTR_POLICY_DO, + CTRL_ATTR_POLICY_DUMP, + + __CTRL_ATTR_POLICY_DUMP_MAX, + CTRL_ATTR_POLICY_DUMP_MAX = __CTRL_ATTR_POLICY_DUMP_MAX - 1 +}; + +#define CTRL_ATTR_POLICY_MAX (__CTRL_ATTR_POLICY_DUMP_MAX - 1) + +#endif /* __LINUX_GENERIC_NETLINK_H */ diff --git a/libnl/include/linux-private/linux/hdlc/ioctl.h b/libnl/include/linux-private/linux/hdlc/ioctl.h new file mode 100644 index 0000000..b06341a --- /dev/null +++ b/libnl/include/linux-private/linux/hdlc/ioctl.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __HDLC_IOCTL_H__ +#define __HDLC_IOCTL_H__ + + +#define GENERIC_HDLC_VERSION 4 /* For synchronization with sethdlc utility */ + +#define CLOCK_DEFAULT 0 /* Default setting */ +#define CLOCK_EXT 1 /* External TX and RX clock - DTE */ +#define CLOCK_INT 2 /* Internal TX and RX clock - DCE */ +#define CLOCK_TXINT 3 /* Internal TX and external RX clock */ +#define CLOCK_TXFROMRX 4 /* TX clock derived from external RX clock */ + + +#define ENCODING_DEFAULT 0 /* Default setting */ +#define ENCODING_NRZ 1 +#define ENCODING_NRZI 2 +#define ENCODING_FM_MARK 3 +#define ENCODING_FM_SPACE 4 +#define ENCODING_MANCHESTER 5 + + +#define PARITY_DEFAULT 0 /* Default setting */ +#define PARITY_NONE 1 /* No parity */ +#define PARITY_CRC16_PR0 2 /* CRC16, initial value 0x0000 */ +#define PARITY_CRC16_PR1 3 /* CRC16, initial value 0xFFFF */ +#define PARITY_CRC16_PR0_CCITT 4 /* CRC16, initial 0x0000, ITU-T version */ +#define PARITY_CRC16_PR1_CCITT 5 /* CRC16, initial 0xFFFF, ITU-T version */ +#define PARITY_CRC32_PR0_CCITT 6 /* CRC32, initial value 0x00000000 */ +#define PARITY_CRC32_PR1_CCITT 7 /* CRC32, initial value 0xFFFFFFFF */ + +#define LMI_DEFAULT 0 /* Default setting */ +#define LMI_NONE 1 /* No LMI, all PVCs are static */ +#define LMI_ANSI 2 /* ANSI Annex D */ +#define LMI_CCITT 3 /* ITU-T Annex A */ +#define LMI_CISCO 4 /* The "original" LMI, aka Gang of Four */ + +#ifndef __ASSEMBLY__ + +typedef struct { + unsigned int clock_rate; /* bits per second */ + unsigned int clock_type; /* internal, external, TX-internal etc. */ + unsigned short loopback; +} sync_serial_settings; /* V.35, V.24, X.21 */ + +typedef struct { + unsigned int clock_rate; /* bits per second */ + unsigned int clock_type; /* internal, external, TX-internal etc. */ + unsigned short loopback; + unsigned int slot_map; +} te1_settings; /* T1, E1 */ + +typedef struct { + unsigned short encoding; + unsigned short parity; +} raw_hdlc_proto; + +typedef struct { + unsigned int t391; + unsigned int t392; + unsigned int n391; + unsigned int n392; + unsigned int n393; + unsigned short lmi; + unsigned short dce; /* 1 for DCE (network side) operation */ +} fr_proto; + +typedef struct { + unsigned int dlci; +} fr_proto_pvc; /* for creating/deleting FR PVCs */ + +typedef struct { + unsigned int dlci; + char master[IFNAMSIZ]; /* Name of master FRAD device */ +}fr_proto_pvc_info; /* for returning PVC information only */ + +typedef struct { + unsigned int interval; + unsigned int timeout; +} cisco_proto; + +typedef struct { + unsigned short dce; /* 1 for DCE (network side) operation */ + unsigned int modulo; /* modulo (8 = basic / 128 = extended) */ + unsigned int window; /* frame window size */ + unsigned int t1; /* timeout t1 */ + unsigned int t2; /* timeout t2 */ + unsigned int n2; /* frame retry counter */ +} x25_hdlc_proto; + +/* PPP doesn't need any info now - supply length = 0 to ioctl */ + +#endif /* __ASSEMBLY__ */ +#endif /* __HDLC_IOCTL_H__ */ diff --git a/libnl/include/linux-private/linux/if.h b/libnl/include/linux-private/linux/if.h new file mode 100644 index 0000000..b287b2a --- /dev/null +++ b/libnl/include/linux-private/linux/if.h @@ -0,0 +1,296 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Global definitions for the INET interface module. + * + * Version: @(#)if.h 1.0.2 04/18/93 + * + * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1982-1988 + * Ross Biro + * Fred N. van Kempen, + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _LINUX_IF_H +#define _LINUX_IF_H + +#include /* for compatibility with glibc */ +#include /* for "__kernel_caddr_t" et al */ +#include /* for "struct sockaddr" et al */ + /* for "__user" et al */ + +#include /* for struct sockaddr. */ + +#if __UAPI_DEF_IF_IFNAMSIZ +#define IFNAMSIZ 16 +#endif /* __UAPI_DEF_IF_IFNAMSIZ */ +#define IFALIASZ 256 +#define ALTIFNAMSIZ 128 +#include + +/* For glibc compatibility. An empty enum does not compile. */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO != 0 || \ + __UAPI_DEF_IF_NET_DEVICE_FLAGS != 0 +/** + * enum net_device_flags - &struct net_device flags + * + * These are the &struct net_device flags, they can be set by drivers, the + * kernel and some can be triggered by userspace. Userspace can query and + * set these flags using userspace utilities but there is also a sysfs + * entry available for all dev flags which can be queried and set. These flags + * are shared for all types of net_devices. The sysfs entries are available + * via /sys/class/net//flags. Flags which can be toggled through sysfs + * are annotated below, note that only a few flags can be toggled and some + * other flags are always preserved from the original net_device flags + * even if you try to set them via sysfs. Flags which are always preserved + * are kept under the flag grouping @IFF_VOLATILE. Flags which are __volatile__ + * are annotated below as such. + * + * You should have a pretty good reason to be extending these flags. + * + * @IFF_UP: interface is up. Can be toggled through sysfs. + * @IFF_BROADCAST: broadcast address valid. Volatile. + * @IFF_DEBUG: turn on debugging. Can be toggled through sysfs. + * @IFF_LOOPBACK: is a loopback net. Volatile. + * @IFF_POINTOPOINT: interface is has p-p link. Volatile. + * @IFF_NOTRAILERS: avoid use of trailers. Can be toggled through sysfs. + * Volatile. + * @IFF_RUNNING: interface RFC2863 OPER_UP. Volatile. + * @IFF_NOARP: no ARP protocol. Can be toggled through sysfs. Volatile. + * @IFF_PROMISC: receive all packets. Can be toggled through sysfs. + * @IFF_ALLMULTI: receive all multicast packets. Can be toggled through + * sysfs. + * @IFF_MASTER: master of a load balancer. Volatile. + * @IFF_SLAVE: slave of a load balancer. Volatile. + * @IFF_MULTICAST: Supports multicast. Can be toggled through sysfs. + * @IFF_PORTSEL: can set media type. Can be toggled through sysfs. + * @IFF_AUTOMEDIA: auto media select active. Can be toggled through sysfs. + * @IFF_DYNAMIC: dialup device with changing addresses. Can be toggled + * through sysfs. + * @IFF_LOWER_UP: driver signals L1 up. Volatile. + * @IFF_DORMANT: driver signals dormant. Volatile. + * @IFF_ECHO: echo sent packets. Volatile. + */ +enum net_device_flags { +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS + IFF_UP = 1<<0, /* sysfs */ + IFF_BROADCAST = 1<<1, /* __volatile__ */ + IFF_DEBUG = 1<<2, /* sysfs */ + IFF_LOOPBACK = 1<<3, /* __volatile__ */ + IFF_POINTOPOINT = 1<<4, /* __volatile__ */ + IFF_NOTRAILERS = 1<<5, /* sysfs */ + IFF_RUNNING = 1<<6, /* __volatile__ */ + IFF_NOARP = 1<<7, /* sysfs */ + IFF_PROMISC = 1<<8, /* sysfs */ + IFF_ALLMULTI = 1<<9, /* sysfs */ + IFF_MASTER = 1<<10, /* __volatile__ */ + IFF_SLAVE = 1<<11, /* __volatile__ */ + IFF_MULTICAST = 1<<12, /* sysfs */ + IFF_PORTSEL = 1<<13, /* sysfs */ + IFF_AUTOMEDIA = 1<<14, /* sysfs */ + IFF_DYNAMIC = 1<<15, /* sysfs */ +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO + IFF_LOWER_UP = 1<<16, /* __volatile__ */ + IFF_DORMANT = 1<<17, /* __volatile__ */ + IFF_ECHO = 1<<18, /* __volatile__ */ +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ +}; +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO != 0 || __UAPI_DEF_IF_NET_DEVICE_FLAGS != 0 */ + +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS +#define IFF_UP IFF_UP +#define IFF_BROADCAST IFF_BROADCAST +#define IFF_DEBUG IFF_DEBUG +#define IFF_LOOPBACK IFF_LOOPBACK +#define IFF_POINTOPOINT IFF_POINTOPOINT +#define IFF_NOTRAILERS IFF_NOTRAILERS +#define IFF_RUNNING IFF_RUNNING +#define IFF_NOARP IFF_NOARP +#define IFF_PROMISC IFF_PROMISC +#define IFF_ALLMULTI IFF_ALLMULTI +#define IFF_MASTER IFF_MASTER +#define IFF_SLAVE IFF_SLAVE +#define IFF_MULTICAST IFF_MULTICAST +#define IFF_PORTSEL IFF_PORTSEL +#define IFF_AUTOMEDIA IFF_AUTOMEDIA +#define IFF_DYNAMIC IFF_DYNAMIC +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS */ + +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO +#define IFF_LOWER_UP IFF_LOWER_UP +#define IFF_DORMANT IFF_DORMANT +#define IFF_ECHO IFF_ECHO +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ + +#define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\ + IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT) + +#define IF_GET_IFACE 0x0001 /* for querying only */ +#define IF_GET_PROTO 0x0002 + +/* For definitions see hdlc.h */ +#define IF_IFACE_V35 0x1000 /* V.35 serial interface */ +#define IF_IFACE_V24 0x1001 /* V.24 serial interface */ +#define IF_IFACE_X21 0x1002 /* X.21 serial interface */ +#define IF_IFACE_T1 0x1003 /* T1 telco serial interface */ +#define IF_IFACE_E1 0x1004 /* E1 telco serial interface */ +#define IF_IFACE_SYNC_SERIAL 0x1005 /* can't be set by software */ +#define IF_IFACE_X21D 0x1006 /* X.21 Dual Clocking (FarSite) */ + +/* For definitions see hdlc.h */ +#define IF_PROTO_HDLC 0x2000 /* raw HDLC protocol */ +#define IF_PROTO_PPP 0x2001 /* PPP protocol */ +#define IF_PROTO_CISCO 0x2002 /* Cisco HDLC protocol */ +#define IF_PROTO_FR 0x2003 /* Frame Relay protocol */ +#define IF_PROTO_FR_ADD_PVC 0x2004 /* Create FR PVC */ +#define IF_PROTO_FR_DEL_PVC 0x2005 /* Delete FR PVC */ +#define IF_PROTO_X25 0x2006 /* X.25 */ +#define IF_PROTO_HDLC_ETH 0x2007 /* raw HDLC, Ethernet emulation */ +#define IF_PROTO_FR_ADD_ETH_PVC 0x2008 /* Create FR Ethernet-bridged PVC */ +#define IF_PROTO_FR_DEL_ETH_PVC 0x2009 /* Delete FR Ethernet-bridged PVC */ +#define IF_PROTO_FR_PVC 0x200A /* for reading PVC status */ +#define IF_PROTO_FR_ETH_PVC 0x200B +#define IF_PROTO_RAW 0x200C /* RAW Socket */ + +/* RFC 2863 operational status */ +enum { + IF_OPER_UNKNOWN, + IF_OPER_NOTPRESENT, + IF_OPER_DOWN, + IF_OPER_LOWERLAYERDOWN, + IF_OPER_TESTING, + IF_OPER_DORMANT, + IF_OPER_UP, +}; + +/* link modes */ +enum { + IF_LINK_MODE_DEFAULT, + IF_LINK_MODE_DORMANT, /* limit upward transition to dormant */ + IF_LINK_MODE_TESTING, /* limit upward transition to testing */ +}; + +/* + * Device mapping structure. I'd just gone off and designed a + * beautiful scheme using only loadable modules with arguments + * for driver options and along come the PCMCIA people 8) + * + * Ah well. The get() side of this is good for WDSETUP, and it'll + * be handy for debugging things. The set side is fine for now and + * being very small might be worth keeping for clean configuration. + */ + +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_IFMAP +struct ifmap { + unsigned long mem_start; + unsigned long mem_end; + unsigned short base_addr; + unsigned char irq; + unsigned char dma; + unsigned char port; + /* 3 bytes spare */ +}; +#endif /* __UAPI_DEF_IF_IFMAP */ + +struct if_settings { + unsigned int type; /* Type of physical device or protocol */ + unsigned int size; /* Size of the data allocated by the caller */ + union { + /* {atm/eth/dsl}_settings anyone ? */ + raw_hdlc_proto *raw_hdlc; + cisco_proto *cisco; + fr_proto *fr; + fr_proto_pvc *fr_pvc; + fr_proto_pvc_info *fr_pvc_info; + x25_hdlc_proto *x25; + + /* interface settings */ + sync_serial_settings *sync; + te1_settings *te1; + } ifs_ifsu; +}; + +/* + * Interface request structure used for socket + * ioctl's. All interface ioctl's must have parameter + * definitions which begin with ifr_name. The + * remainder may be interface specific. + */ + +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_IFREQ +struct ifreq { +#define IFHWADDRLEN 6 + union + { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + } ifr_ifrn; + + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_dstaddr; + struct sockaddr ifru_broadaddr; + struct sockaddr ifru_netmask; + struct sockaddr ifru_hwaddr; + short ifru_flags; + int ifru_ivalue; + int ifru_mtu; + struct ifmap ifru_map; + char ifru_slave[IFNAMSIZ]; /* Just fits the size */ + char ifru_newname[IFNAMSIZ]; + void * ifru_data; + struct if_settings ifru_settings; + } ifr_ifru; +}; +#endif /* __UAPI_DEF_IF_IFREQ */ + +#define ifr_name ifr_ifrn.ifrn_name /* interface name */ +#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ +#define ifr_addr ifr_ifru.ifru_addr /* address */ +#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */ +#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ +#define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */ +#define ifr_flags ifr_ifru.ifru_flags /* flags */ +#define ifr_metric ifr_ifru.ifru_ivalue /* metric */ +#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */ +#define ifr_map ifr_ifru.ifru_map /* device map */ +#define ifr_slave ifr_ifru.ifru_slave /* slave device */ +#define ifr_data ifr_ifru.ifru_data /* for use by interface */ +#define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */ +#define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */ +#define ifr_qlen ifr_ifru.ifru_ivalue /* Queue length */ +#define ifr_newname ifr_ifru.ifru_newname /* New name */ +#define ifr_settings ifr_ifru.ifru_settings /* Device/proto settings*/ + +/* + * Structure used in SIOCGIFCONF request. + * Used to retrieve interface configuration + * for machine (useful for programs which + * must know all networks accessible). + */ + +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_IFCONF +struct ifconf { + int ifc_len; /* size of buffer */ + union { + char *ifcu_buf; + struct ifreq *ifcu_req; + } ifc_ifcu; +}; +#endif /* __UAPI_DEF_IF_IFCONF */ + +#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */ +#define ifc_req ifc_ifcu.ifcu_req /* array of structures */ + +#endif /* _LINUX_IF_H */ diff --git a/libnl/include/linux-private/linux/if_addr.h b/libnl/include/linux-private/linux/if_addr.h new file mode 100644 index 0000000..d6db3ff --- /dev/null +++ b/libnl/include/linux-private/linux/if_addr.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_IF_ADDR_H +#define __LINUX_IF_ADDR_H + +#include +#include + +struct ifaddrmsg { + __u8 ifa_family; + __u8 ifa_prefixlen; /* The prefix length */ + __u8 ifa_flags; /* Flags */ + __u8 ifa_scope; /* Address scope */ + __u32 ifa_index; /* Link index */ +}; + +/* + * Important comment: + * IFA_ADDRESS is prefix address, rather than local interface address. + * It makes no difference for normally configured broadcast interfaces, + * but for point-to-point IFA_ADDRESS is DESTINATION address, + * local address is supplied in IFA_LOCAL attribute. + * + * IFA_FLAGS is a u32 attribute that extends the u8 field ifa_flags. + * If present, the value from struct ifaddrmsg will be ignored. + */ +enum { + IFA_UNSPEC, + IFA_ADDRESS, + IFA_LOCAL, + IFA_LABEL, + IFA_BROADCAST, + IFA_ANYCAST, + IFA_CACHEINFO, + IFA_MULTICAST, + IFA_FLAGS, + IFA_RT_PRIORITY, /* u32, priority/metric for prefix route */ + IFA_TARGET_NETNSID, + IFA_PROTO, /* u8, address protocol */ + __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)) + +/* ifa_proto */ +#define IFAPROT_UNSPEC 0 +#define IFAPROT_KERNEL_LO 1 /* loopback */ +#define IFAPROT_KERNEL_RA 2 /* set by kernel from router announcement */ +#define IFAPROT_KERNEL_LL 3 /* link-local set by kernel */ + +#endif diff --git a/libnl/include/linux-private/linux/if_arp.h b/libnl/include/linux-private/linux/if_arp.h new file mode 100644 index 0000000..12d06bb --- /dev/null +++ b/libnl/include/linux-private/linux/if_arp.h @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Global definitions for the ARP (RFC 826) protocol. + * + * Version: @(#)if_arp.h 1.0.1 04/16/93 + * + * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1986-1988 + * Portions taken from the KA9Q/NOS (v2.00m PA0GRI) source. + * Ross Biro + * Fred N. van Kempen, + * Florian La Roche, + * Jonathan Layes + * Arnaldo Carvalho de Melo ARPHRD_HWX25 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _LINUX_IF_ARP_H +#define _LINUX_IF_ARP_H + +#include + +/* ARP protocol HARDWARE identifiers. */ +#define ARPHRD_NETROM 0 /* from KA9Q: NET/ROM pseudo */ +#define ARPHRD_ETHER 1 /* Ethernet 10Mbps */ +#define ARPHRD_EETHER 2 /* Experimental Ethernet */ +#define ARPHRD_AX25 3 /* AX.25 Level 2 */ +#define ARPHRD_PRONET 4 /* PROnet token ring */ +#define ARPHRD_CHAOS 5 /* Chaosnet */ +#define ARPHRD_IEEE802 6 /* IEEE 802.2 Ethernet/TR/TB */ +#define ARPHRD_ARCNET 7 /* ARCnet */ +#define ARPHRD_APPLETLK 8 /* APPLEtalk */ +#define ARPHRD_DLCI 15 /* Frame Relay DLCI */ +#define ARPHRD_ATM 19 /* ATM */ +#define ARPHRD_METRICOM 23 /* Metricom STRIP (new IANA id) */ +#define ARPHRD_IEEE1394 24 /* IEEE 1394 IPv4 - RFC 2734 */ +#define ARPHRD_EUI64 27 /* EUI-64 */ +#define ARPHRD_INFINIBAND 32 /* InfiniBand */ + +/* Dummy types for non ARP hardware */ +#define ARPHRD_SLIP 256 +#define ARPHRD_CSLIP 257 +#define ARPHRD_SLIP6 258 +#define ARPHRD_CSLIP6 259 +#define ARPHRD_RSRVD 260 /* Notional KISS type */ +#define ARPHRD_ADAPT 264 +#define ARPHRD_ROSE 270 +#define ARPHRD_X25 271 /* CCITT X.25 */ +#define ARPHRD_HWX25 272 /* Boards with X.25 in firmware */ +#define ARPHRD_CAN 280 /* Controller Area Network */ +#define ARPHRD_MCTP 290 +#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/libnl/include/linux-private/linux/if_bridge.h b/libnl/include/linux-private/linux/if_bridge.h new file mode 100644 index 0000000..e3d55b1 --- /dev/null +++ b/libnl/include/linux-private/linux/if_bridge.h @@ -0,0 +1,839 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_IF_BRIDGE_H +#define _LINUX_IF_BRIDGE_H + +#include +#include +#include + +#define SYSFS_BRIDGE_ATTR "bridge" +#define SYSFS_BRIDGE_FDB "brforward" +#define SYSFS_BRIDGE_PORT_SUBDIR "brif" +#define SYSFS_BRIDGE_PORT_ATTR "brport" +#define SYSFS_BRIDGE_PORT_LINK "bridge" + +#define BRCTL_VERSION 1 + +#define BRCTL_GET_VERSION 0 +#define BRCTL_GET_BRIDGES 1 +#define BRCTL_ADD_BRIDGE 2 +#define BRCTL_DEL_BRIDGE 3 +#define BRCTL_ADD_IF 4 +#define BRCTL_DEL_IF 5 +#define BRCTL_GET_BRIDGE_INFO 6 +#define BRCTL_GET_PORT_LIST 7 +#define BRCTL_SET_BRIDGE_FORWARD_DELAY 8 +#define BRCTL_SET_BRIDGE_HELLO_TIME 9 +#define BRCTL_SET_BRIDGE_MAX_AGE 10 +#define BRCTL_SET_AGEING_TIME 11 +#define BRCTL_SET_GC_INTERVAL 12 +#define BRCTL_GET_PORT_INFO 13 +#define BRCTL_SET_BRIDGE_STP_STATE 14 +#define BRCTL_SET_BRIDGE_PRIORITY 15 +#define BRCTL_SET_PORT_PRIORITY 16 +#define BRCTL_SET_PATH_COST 17 +#define BRCTL_GET_FDB_ENTRIES 18 + +#define BR_STATE_DISABLED 0 +#define BR_STATE_LISTENING 1 +#define BR_STATE_LEARNING 2 +#define BR_STATE_FORWARDING 3 +#define BR_STATE_BLOCKING 4 + +struct __bridge_info { + __u64 designated_root; + __u64 bridge_id; + __u32 root_path_cost; + __u32 max_age; + __u32 hello_time; + __u32 forward_delay; + __u32 bridge_max_age; + __u32 bridge_hello_time; + __u32 bridge_forward_delay; + __u8 topology_change; + __u8 topology_change_detected; + __u8 root_port; + __u8 stp_enabled; + __u32 ageing_time; + __u32 gc_interval; + __u32 hello_timer_value; + __u32 tcn_timer_value; + __u32 topology_change_timer_value; + __u32 gc_timer_value; +}; + +struct __port_info { + __u64 designated_root; + __u64 designated_bridge; + __u16 port_id; + __u16 designated_port; + __u32 path_cost; + __u32 designated_cost; + __u8 state; + __u8 top_change_ack; + __u8 config_pending; + __u8 unused0; + __u32 message_age_timer_value; + __u32 forward_delay_timer_value; + __u32 hold_timer_value; +}; + +struct __fdb_entry { + __u8 mac_addr[ETH_ALEN]; + __u8 port_no; + __u8 is_local; + __u32 ageing_timer_value; + __u8 port_hi; + __u8 pad0; + __u16 unused; +}; + +/* Bridge Flags */ +#define BRIDGE_FLAGS_MASTER 1 /* Bridge command to/from master */ +#define BRIDGE_FLAGS_SELF 2 /* Bridge command to/from lowerdev */ + +#define BRIDGE_MODE_VEB 0 /* Default loopback mode */ +#define BRIDGE_MODE_VEPA 1 /* 802.1Qbg defined VEPA mode */ +#define BRIDGE_MODE_UNDEF 0xFFFF /* mode undefined */ + +/* Bridge management nested attributes + * [IFLA_AF_SPEC] = { + * [IFLA_BRIDGE_FLAGS] + * [IFLA_BRIDGE_MODE] + * [IFLA_BRIDGE_VLAN_INFO] + * } + */ +enum { + IFLA_BRIDGE_FLAGS, + IFLA_BRIDGE_MODE, + IFLA_BRIDGE_VLAN_INFO, + IFLA_BRIDGE_VLAN_TUNNEL_INFO, + IFLA_BRIDGE_MRP, + IFLA_BRIDGE_CFM, + IFLA_BRIDGE_MST, + __IFLA_BRIDGE_MAX, +}; +#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1) + +#define BRIDGE_VLAN_INFO_MASTER (1<<0) /* Operate on Bridge device as well */ +#define BRIDGE_VLAN_INFO_PVID (1<<1) /* VLAN is PVID, ingress untagged */ +#define BRIDGE_VLAN_INFO_UNTAGGED (1<<2) /* VLAN egresses untagged */ +#define BRIDGE_VLAN_INFO_RANGE_BEGIN (1<<3) /* VLAN is start of vlan range */ +#define BRIDGE_VLAN_INFO_RANGE_END (1<<4) /* VLAN is end of vlan range */ +#define BRIDGE_VLAN_INFO_BRENTRY (1<<5) /* Global bridge VLAN entry */ +#define BRIDGE_VLAN_INFO_ONLY_OPTS (1<<6) /* Skip create/delete/flags */ + +struct bridge_vlan_info { + __u16 flags; + __u16 vid; +}; + +enum { + IFLA_BRIDGE_VLAN_TUNNEL_UNSPEC, + IFLA_BRIDGE_VLAN_TUNNEL_ID, + IFLA_BRIDGE_VLAN_TUNNEL_VID, + IFLA_BRIDGE_VLAN_TUNNEL_FLAGS, + __IFLA_BRIDGE_VLAN_TUNNEL_MAX, +}; + +#define IFLA_BRIDGE_VLAN_TUNNEL_MAX (__IFLA_BRIDGE_VLAN_TUNNEL_MAX - 1) + +struct bridge_vlan_xstats { + __u64 rx_bytes; + __u64 rx_packets; + __u64 tx_bytes; + __u64 tx_packets; + __u16 vid; + __u16 flags; + __u32 pad2; +}; + +enum { + IFLA_BRIDGE_MRP_UNSPEC, + IFLA_BRIDGE_MRP_INSTANCE, + IFLA_BRIDGE_MRP_PORT_STATE, + IFLA_BRIDGE_MRP_PORT_ROLE, + IFLA_BRIDGE_MRP_RING_STATE, + IFLA_BRIDGE_MRP_RING_ROLE, + IFLA_BRIDGE_MRP_START_TEST, + IFLA_BRIDGE_MRP_INFO, + IFLA_BRIDGE_MRP_IN_ROLE, + IFLA_BRIDGE_MRP_IN_STATE, + IFLA_BRIDGE_MRP_START_IN_TEST, + __IFLA_BRIDGE_MRP_MAX, +}; + +#define IFLA_BRIDGE_MRP_MAX (__IFLA_BRIDGE_MRP_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_INSTANCE_UNSPEC, + IFLA_BRIDGE_MRP_INSTANCE_RING_ID, + IFLA_BRIDGE_MRP_INSTANCE_P_IFINDEX, + IFLA_BRIDGE_MRP_INSTANCE_S_IFINDEX, + IFLA_BRIDGE_MRP_INSTANCE_PRIO, + __IFLA_BRIDGE_MRP_INSTANCE_MAX, +}; + +#define IFLA_BRIDGE_MRP_INSTANCE_MAX (__IFLA_BRIDGE_MRP_INSTANCE_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_PORT_STATE_UNSPEC, + IFLA_BRIDGE_MRP_PORT_STATE_STATE, + __IFLA_BRIDGE_MRP_PORT_STATE_MAX, +}; + +#define IFLA_BRIDGE_MRP_PORT_STATE_MAX (__IFLA_BRIDGE_MRP_PORT_STATE_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_PORT_ROLE_UNSPEC, + IFLA_BRIDGE_MRP_PORT_ROLE_ROLE, + __IFLA_BRIDGE_MRP_PORT_ROLE_MAX, +}; + +#define IFLA_BRIDGE_MRP_PORT_ROLE_MAX (__IFLA_BRIDGE_MRP_PORT_ROLE_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_RING_STATE_UNSPEC, + IFLA_BRIDGE_MRP_RING_STATE_RING_ID, + IFLA_BRIDGE_MRP_RING_STATE_STATE, + __IFLA_BRIDGE_MRP_RING_STATE_MAX, +}; + +#define IFLA_BRIDGE_MRP_RING_STATE_MAX (__IFLA_BRIDGE_MRP_RING_STATE_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_RING_ROLE_UNSPEC, + IFLA_BRIDGE_MRP_RING_ROLE_RING_ID, + IFLA_BRIDGE_MRP_RING_ROLE_ROLE, + __IFLA_BRIDGE_MRP_RING_ROLE_MAX, +}; + +#define IFLA_BRIDGE_MRP_RING_ROLE_MAX (__IFLA_BRIDGE_MRP_RING_ROLE_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_START_TEST_UNSPEC, + IFLA_BRIDGE_MRP_START_TEST_RING_ID, + IFLA_BRIDGE_MRP_START_TEST_INTERVAL, + IFLA_BRIDGE_MRP_START_TEST_MAX_MISS, + IFLA_BRIDGE_MRP_START_TEST_PERIOD, + IFLA_BRIDGE_MRP_START_TEST_MONITOR, + __IFLA_BRIDGE_MRP_START_TEST_MAX, +}; + +#define IFLA_BRIDGE_MRP_START_TEST_MAX (__IFLA_BRIDGE_MRP_START_TEST_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_INFO_UNSPEC, + IFLA_BRIDGE_MRP_INFO_RING_ID, + IFLA_BRIDGE_MRP_INFO_P_IFINDEX, + IFLA_BRIDGE_MRP_INFO_S_IFINDEX, + IFLA_BRIDGE_MRP_INFO_PRIO, + IFLA_BRIDGE_MRP_INFO_RING_STATE, + IFLA_BRIDGE_MRP_INFO_RING_ROLE, + IFLA_BRIDGE_MRP_INFO_TEST_INTERVAL, + IFLA_BRIDGE_MRP_INFO_TEST_MAX_MISS, + IFLA_BRIDGE_MRP_INFO_TEST_MONITOR, + IFLA_BRIDGE_MRP_INFO_I_IFINDEX, + IFLA_BRIDGE_MRP_INFO_IN_STATE, + IFLA_BRIDGE_MRP_INFO_IN_ROLE, + IFLA_BRIDGE_MRP_INFO_IN_TEST_INTERVAL, + IFLA_BRIDGE_MRP_INFO_IN_TEST_MAX_MISS, + __IFLA_BRIDGE_MRP_INFO_MAX, +}; + +#define IFLA_BRIDGE_MRP_INFO_MAX (__IFLA_BRIDGE_MRP_INFO_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_IN_STATE_UNSPEC, + IFLA_BRIDGE_MRP_IN_STATE_IN_ID, + IFLA_BRIDGE_MRP_IN_STATE_STATE, + __IFLA_BRIDGE_MRP_IN_STATE_MAX, +}; + +#define IFLA_BRIDGE_MRP_IN_STATE_MAX (__IFLA_BRIDGE_MRP_IN_STATE_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_IN_ROLE_UNSPEC, + IFLA_BRIDGE_MRP_IN_ROLE_RING_ID, + IFLA_BRIDGE_MRP_IN_ROLE_IN_ID, + IFLA_BRIDGE_MRP_IN_ROLE_ROLE, + IFLA_BRIDGE_MRP_IN_ROLE_I_IFINDEX, + __IFLA_BRIDGE_MRP_IN_ROLE_MAX, +}; + +#define IFLA_BRIDGE_MRP_IN_ROLE_MAX (__IFLA_BRIDGE_MRP_IN_ROLE_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_START_IN_TEST_UNSPEC, + IFLA_BRIDGE_MRP_START_IN_TEST_IN_ID, + IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL, + IFLA_BRIDGE_MRP_START_IN_TEST_MAX_MISS, + IFLA_BRIDGE_MRP_START_IN_TEST_PERIOD, + __IFLA_BRIDGE_MRP_START_IN_TEST_MAX, +}; + +#define IFLA_BRIDGE_MRP_START_IN_TEST_MAX (__IFLA_BRIDGE_MRP_START_IN_TEST_MAX - 1) + +struct br_mrp_instance { + __u32 ring_id; + __u32 p_ifindex; + __u32 s_ifindex; + __u16 prio; +}; + +struct br_mrp_ring_state { + __u32 ring_id; + __u32 ring_state; +}; + +struct br_mrp_ring_role { + __u32 ring_id; + __u32 ring_role; +}; + +struct br_mrp_start_test { + __u32 ring_id; + __u32 interval; + __u32 max_miss; + __u32 period; + __u32 monitor; +}; + +struct br_mrp_in_state { + __u32 in_state; + __u16 in_id; +}; + +struct br_mrp_in_role { + __u32 ring_id; + __u32 in_role; + __u32 i_ifindex; + __u16 in_id; +}; + +struct br_mrp_start_in_test { + __u32 interval; + __u32 max_miss; + __u32 period; + __u16 in_id; +}; + +enum { + IFLA_BRIDGE_CFM_UNSPEC, + IFLA_BRIDGE_CFM_MEP_CREATE, + IFLA_BRIDGE_CFM_MEP_DELETE, + IFLA_BRIDGE_CFM_MEP_CONFIG, + IFLA_BRIDGE_CFM_CC_CONFIG, + IFLA_BRIDGE_CFM_CC_PEER_MEP_ADD, + IFLA_BRIDGE_CFM_CC_PEER_MEP_REMOVE, + IFLA_BRIDGE_CFM_CC_RDI, + IFLA_BRIDGE_CFM_CC_CCM_TX, + IFLA_BRIDGE_CFM_MEP_CREATE_INFO, + IFLA_BRIDGE_CFM_MEP_CONFIG_INFO, + IFLA_BRIDGE_CFM_CC_CONFIG_INFO, + IFLA_BRIDGE_CFM_CC_RDI_INFO, + IFLA_BRIDGE_CFM_CC_CCM_TX_INFO, + IFLA_BRIDGE_CFM_CC_PEER_MEP_INFO, + IFLA_BRIDGE_CFM_MEP_STATUS_INFO, + IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO, + __IFLA_BRIDGE_CFM_MAX, +}; + +#define IFLA_BRIDGE_CFM_MAX (__IFLA_BRIDGE_CFM_MAX - 1) + +enum { + IFLA_BRIDGE_CFM_MEP_CREATE_UNSPEC, + IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE, + IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN, + IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION, + IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX, + __IFLA_BRIDGE_CFM_MEP_CREATE_MAX, +}; + +#define IFLA_BRIDGE_CFM_MEP_CREATE_MAX (__IFLA_BRIDGE_CFM_MEP_CREATE_MAX - 1) + +enum { + IFLA_BRIDGE_CFM_MEP_DELETE_UNSPEC, + IFLA_BRIDGE_CFM_MEP_DELETE_INSTANCE, + __IFLA_BRIDGE_CFM_MEP_DELETE_MAX, +}; + +#define IFLA_BRIDGE_CFM_MEP_DELETE_MAX (__IFLA_BRIDGE_CFM_MEP_DELETE_MAX - 1) + +enum { + IFLA_BRIDGE_CFM_MEP_CONFIG_UNSPEC, + IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE, + IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC, + IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL, + IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID, + __IFLA_BRIDGE_CFM_MEP_CONFIG_MAX, +}; + +#define IFLA_BRIDGE_CFM_MEP_CONFIG_MAX (__IFLA_BRIDGE_CFM_MEP_CONFIG_MAX - 1) + +enum { + IFLA_BRIDGE_CFM_CC_CONFIG_UNSPEC, + IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE, + IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE, + IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL, + IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID, + __IFLA_BRIDGE_CFM_CC_CONFIG_MAX, +}; + +#define IFLA_BRIDGE_CFM_CC_CONFIG_MAX (__IFLA_BRIDGE_CFM_CC_CONFIG_MAX - 1) + +enum { + IFLA_BRIDGE_CFM_CC_PEER_MEP_UNSPEC, + IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE, + IFLA_BRIDGE_CFM_CC_PEER_MEPID, + __IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX, +}; + +#define IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX (__IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX - 1) + +enum { + IFLA_BRIDGE_CFM_CC_RDI_UNSPEC, + IFLA_BRIDGE_CFM_CC_RDI_INSTANCE, + IFLA_BRIDGE_CFM_CC_RDI_RDI, + __IFLA_BRIDGE_CFM_CC_RDI_MAX, +}; + +#define IFLA_BRIDGE_CFM_CC_RDI_MAX (__IFLA_BRIDGE_CFM_CC_RDI_MAX - 1) + +enum { + IFLA_BRIDGE_CFM_CC_CCM_TX_UNSPEC, + IFLA_BRIDGE_CFM_CC_CCM_TX_INSTANCE, + IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC, + IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE, + IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD, + IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV, + IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE, + IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV, + IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE, + __IFLA_BRIDGE_CFM_CC_CCM_TX_MAX, +}; + +#define IFLA_BRIDGE_CFM_CC_CCM_TX_MAX (__IFLA_BRIDGE_CFM_CC_CCM_TX_MAX - 1) + +enum { + IFLA_BRIDGE_CFM_MEP_STATUS_UNSPEC, + IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE, + IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN, + IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN, + IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN, + __IFLA_BRIDGE_CFM_MEP_STATUS_MAX, +}; + +#define IFLA_BRIDGE_CFM_MEP_STATUS_MAX (__IFLA_BRIDGE_CFM_MEP_STATUS_MAX - 1) + +enum { + IFLA_BRIDGE_CFM_CC_PEER_STATUS_UNSPEC, + IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE, + IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID, + IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT, + IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI, + IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE, + IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE, + IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN, + IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN, + IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN, + __IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX, +}; + +#define IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX (__IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX - 1) + +enum { + IFLA_BRIDGE_MST_UNSPEC, + IFLA_BRIDGE_MST_ENTRY, + __IFLA_BRIDGE_MST_MAX, +}; +#define IFLA_BRIDGE_MST_MAX (__IFLA_BRIDGE_MST_MAX - 1) + +enum { + IFLA_BRIDGE_MST_ENTRY_UNSPEC, + IFLA_BRIDGE_MST_ENTRY_MSTI, + IFLA_BRIDGE_MST_ENTRY_STATE, + __IFLA_BRIDGE_MST_ENTRY_MAX, +}; +#define IFLA_BRIDGE_MST_ENTRY_MAX (__IFLA_BRIDGE_MST_ENTRY_MAX - 1) + +struct bridge_stp_xstats { + __u64 transition_blk; + __u64 transition_fwd; + __u64 rx_bpdu; + __u64 tx_bpdu; + __u64 rx_tcn; + __u64 tx_tcn; +}; + +/* Bridge vlan RTM header */ +struct br_vlan_msg { + __u8 family; + __u8 reserved1; + __u16 reserved2; + __u32 ifindex; +}; + +enum { + BRIDGE_VLANDB_DUMP_UNSPEC, + BRIDGE_VLANDB_DUMP_FLAGS, + __BRIDGE_VLANDB_DUMP_MAX, +}; +#define BRIDGE_VLANDB_DUMP_MAX (__BRIDGE_VLANDB_DUMP_MAX - 1) + +/* flags used in BRIDGE_VLANDB_DUMP_FLAGS attribute to affect dumps */ +#define BRIDGE_VLANDB_DUMPF_STATS (1 << 0) /* Include stats in the dump */ +#define BRIDGE_VLANDB_DUMPF_GLOBAL (1 << 1) /* Dump global vlan options only */ + +/* Bridge vlan RTM attributes + * [BRIDGE_VLANDB_ENTRY] = { + * [BRIDGE_VLANDB_ENTRY_INFO] + * ... + * } + * [BRIDGE_VLANDB_GLOBAL_OPTIONS] = { + * [BRIDGE_VLANDB_GOPTS_ID] + * ... + * } + */ +enum { + BRIDGE_VLANDB_UNSPEC, + BRIDGE_VLANDB_ENTRY, + BRIDGE_VLANDB_GLOBAL_OPTIONS, + __BRIDGE_VLANDB_MAX, +}; +#define BRIDGE_VLANDB_MAX (__BRIDGE_VLANDB_MAX - 1) + +enum { + BRIDGE_VLANDB_ENTRY_UNSPEC, + BRIDGE_VLANDB_ENTRY_INFO, + BRIDGE_VLANDB_ENTRY_RANGE, + BRIDGE_VLANDB_ENTRY_STATE, + BRIDGE_VLANDB_ENTRY_TUNNEL_INFO, + BRIDGE_VLANDB_ENTRY_STATS, + BRIDGE_VLANDB_ENTRY_MCAST_ROUTER, + BRIDGE_VLANDB_ENTRY_MCAST_N_GROUPS, + BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS, + BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS, + __BRIDGE_VLANDB_ENTRY_MAX, +}; +#define BRIDGE_VLANDB_ENTRY_MAX (__BRIDGE_VLANDB_ENTRY_MAX - 1) + +/* [BRIDGE_VLANDB_ENTRY] = { + * [BRIDGE_VLANDB_ENTRY_TUNNEL_INFO] = { + * [BRIDGE_VLANDB_TINFO_ID] + * ... + * } + * } + */ +enum { + BRIDGE_VLANDB_TINFO_UNSPEC, + BRIDGE_VLANDB_TINFO_ID, + BRIDGE_VLANDB_TINFO_CMD, + __BRIDGE_VLANDB_TINFO_MAX, +}; +#define BRIDGE_VLANDB_TINFO_MAX (__BRIDGE_VLANDB_TINFO_MAX - 1) + +/* [BRIDGE_VLANDB_ENTRY] = { + * [BRIDGE_VLANDB_ENTRY_STATS] = { + * [BRIDGE_VLANDB_STATS_RX_BYTES] + * ... + * } + * ... + * } + */ +enum { + BRIDGE_VLANDB_STATS_UNSPEC, + BRIDGE_VLANDB_STATS_RX_BYTES, + BRIDGE_VLANDB_STATS_RX_PACKETS, + BRIDGE_VLANDB_STATS_TX_BYTES, + BRIDGE_VLANDB_STATS_TX_PACKETS, + BRIDGE_VLANDB_STATS_PAD, + __BRIDGE_VLANDB_STATS_MAX, +}; +#define BRIDGE_VLANDB_STATS_MAX (__BRIDGE_VLANDB_STATS_MAX - 1) + +enum { + BRIDGE_VLANDB_GOPTS_UNSPEC, + BRIDGE_VLANDB_GOPTS_ID, + BRIDGE_VLANDB_GOPTS_RANGE, + BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING, + BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION, + BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION, + BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT, + BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT, + BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL, + BRIDGE_VLANDB_GOPTS_PAD, + BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL, + BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL, + BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL, + BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL, + BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL, + BRIDGE_VLANDB_GOPTS_MCAST_QUERIER, + BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS, + BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_STATE, + BRIDGE_VLANDB_GOPTS_MSTI, + __BRIDGE_VLANDB_GOPTS_MAX +}; +#define BRIDGE_VLANDB_GOPTS_MAX (__BRIDGE_VLANDB_GOPTS_MAX - 1) + +/* Bridge multicast database attributes + * [MDBA_MDB] = { + * [MDBA_MDB_ENTRY] = { + * [MDBA_MDB_ENTRY_INFO] { + * struct br_mdb_entry + * [MDBA_MDB_EATTR attributes] + * } + * } + * } + * [MDBA_ROUTER] = { + * [MDBA_ROUTER_PORT] = { + * u32 ifindex + * [MDBA_ROUTER_PATTR attributes] + * } + * } + */ +enum { + MDBA_UNSPEC, + MDBA_MDB, + MDBA_ROUTER, + __MDBA_MAX, +}; +#define MDBA_MAX (__MDBA_MAX - 1) + +enum { + MDBA_MDB_UNSPEC, + MDBA_MDB_ENTRY, + __MDBA_MDB_MAX, +}; +#define MDBA_MDB_MAX (__MDBA_MDB_MAX - 1) + +enum { + MDBA_MDB_ENTRY_UNSPEC, + MDBA_MDB_ENTRY_INFO, + __MDBA_MDB_ENTRY_MAX, +}; +#define MDBA_MDB_ENTRY_MAX (__MDBA_MDB_ENTRY_MAX - 1) + +/* per mdb entry additional attributes */ +enum { + MDBA_MDB_EATTR_UNSPEC, + MDBA_MDB_EATTR_TIMER, + MDBA_MDB_EATTR_SRC_LIST, + MDBA_MDB_EATTR_GROUP_MODE, + MDBA_MDB_EATTR_SOURCE, + MDBA_MDB_EATTR_RTPROT, + MDBA_MDB_EATTR_DST, + MDBA_MDB_EATTR_DST_PORT, + MDBA_MDB_EATTR_VNI, + MDBA_MDB_EATTR_IFINDEX, + MDBA_MDB_EATTR_SRC_VNI, + __MDBA_MDB_EATTR_MAX +}; +#define MDBA_MDB_EATTR_MAX (__MDBA_MDB_EATTR_MAX - 1) + +/* per mdb entry source */ +enum { + MDBA_MDB_SRCLIST_UNSPEC, + MDBA_MDB_SRCLIST_ENTRY, + __MDBA_MDB_SRCLIST_MAX +}; +#define MDBA_MDB_SRCLIST_MAX (__MDBA_MDB_SRCLIST_MAX - 1) + +/* per mdb entry per source attributes + * these are embedded in MDBA_MDB_SRCLIST_ENTRY + */ +enum { + MDBA_MDB_SRCATTR_UNSPEC, + MDBA_MDB_SRCATTR_ADDRESS, + MDBA_MDB_SRCATTR_TIMER, + __MDBA_MDB_SRCATTR_MAX +}; +#define MDBA_MDB_SRCATTR_MAX (__MDBA_MDB_SRCATTR_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_INET_TIMER, + MDBA_ROUTER_PATTR_INET6_TIMER, + MDBA_ROUTER_PATTR_VID, + __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) +#define MDB_FLAGS_STAR_EXCL (1 << 2) +#define MDB_FLAGS_BLOCKED (1 << 3) + __u8 flags; + __u16 vid; + struct { + union { + __be32 ip4; + struct in6_addr ip6; + unsigned char mac_addr[ETH_ALEN]; + } u; + __be16 proto; + } addr; +}; + +enum { + MDBA_SET_ENTRY_UNSPEC, + MDBA_SET_ENTRY, + MDBA_SET_ENTRY_ATTRS, + __MDBA_SET_ENTRY_MAX, +}; +#define MDBA_SET_ENTRY_MAX (__MDBA_SET_ENTRY_MAX - 1) + +/* [MDBA_SET_ENTRY_ATTRS] = { + * [MDBE_ATTR_xxx] + * ... + * } + */ +enum { + MDBE_ATTR_UNSPEC, + MDBE_ATTR_SOURCE, + MDBE_ATTR_SRC_LIST, + MDBE_ATTR_GROUP_MODE, + MDBE_ATTR_RTPROT, + MDBE_ATTR_DST, + MDBE_ATTR_DST_PORT, + MDBE_ATTR_VNI, + MDBE_ATTR_IFINDEX, + MDBE_ATTR_SRC_VNI, + __MDBE_ATTR_MAX, +}; +#define MDBE_ATTR_MAX (__MDBE_ATTR_MAX - 1) + +/* per mdb entry source */ +enum { + MDBE_SRC_LIST_UNSPEC, + MDBE_SRC_LIST_ENTRY, + __MDBE_SRC_LIST_MAX, +}; +#define MDBE_SRC_LIST_MAX (__MDBE_SRC_LIST_MAX - 1) + +/* per mdb entry per source attributes + * these are embedded in MDBE_SRC_LIST_ENTRY + */ +enum { + MDBE_SRCATTR_UNSPEC, + MDBE_SRCATTR_ADDRESS, + __MDBE_SRCATTR_MAX, +}; +#define MDBE_SRCATTR_MAX (__MDBE_SRCATTR_MAX - 1) + +/* Embedded inside LINK_XSTATS_TYPE_BRIDGE */ +enum { + BRIDGE_XSTATS_UNSPEC, + BRIDGE_XSTATS_VLAN, + BRIDGE_XSTATS_MCAST, + BRIDGE_XSTATS_PAD, + BRIDGE_XSTATS_STP, + __BRIDGE_XSTATS_MAX +}; +#define BRIDGE_XSTATS_MAX (__BRIDGE_XSTATS_MAX - 1) + +enum { + BR_MCAST_DIR_RX, + BR_MCAST_DIR_TX, + BR_MCAST_DIR_SIZE +}; + +/* IGMP/MLD statistics */ +struct br_mcast_stats { + __u64 igmp_v1queries[BR_MCAST_DIR_SIZE]; + __u64 igmp_v2queries[BR_MCAST_DIR_SIZE]; + __u64 igmp_v3queries[BR_MCAST_DIR_SIZE]; + __u64 igmp_leaves[BR_MCAST_DIR_SIZE]; + __u64 igmp_v1reports[BR_MCAST_DIR_SIZE]; + __u64 igmp_v2reports[BR_MCAST_DIR_SIZE]; + __u64 igmp_v3reports[BR_MCAST_DIR_SIZE]; + __u64 igmp_parse_errors; + + __u64 mld_v1queries[BR_MCAST_DIR_SIZE]; + __u64 mld_v2queries[BR_MCAST_DIR_SIZE]; + __u64 mld_leaves[BR_MCAST_DIR_SIZE]; + __u64 mld_v1reports[BR_MCAST_DIR_SIZE]; + __u64 mld_v2reports[BR_MCAST_DIR_SIZE]; + __u64 mld_parse_errors; + + __u64 mcast_bytes[BR_MCAST_DIR_SIZE]; + __u64 mcast_packets[BR_MCAST_DIR_SIZE]; +}; + +/* bridge boolean options + * BR_BOOLOPT_NO_LL_LEARN - disable learning from link-local packets + * BR_BOOLOPT_MCAST_VLAN_SNOOPING - control vlan multicast snooping + * + * 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_MCAST_VLAN_SNOOPING, + BR_BOOLOPT_MST_ENABLE, + 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; +}; + +enum { + BRIDGE_QUERIER_UNSPEC, + BRIDGE_QUERIER_IP_ADDRESS, + BRIDGE_QUERIER_IP_PORT, + BRIDGE_QUERIER_IP_OTHER_TIMER, + BRIDGE_QUERIER_PAD, + BRIDGE_QUERIER_IPV6_ADDRESS, + BRIDGE_QUERIER_IPV6_PORT, + BRIDGE_QUERIER_IPV6_OTHER_TIMER, + __BRIDGE_QUERIER_MAX +}; +#define BRIDGE_QUERIER_MAX (__BRIDGE_QUERIER_MAX - 1) +#endif /* _LINUX_IF_BRIDGE_H */ diff --git a/libnl/include/linux-private/linux/if_ether.h b/libnl/include/linux-private/linux/if_ether.h new file mode 100644 index 0000000..a1aff8e --- /dev/null +++ b/libnl/include/linux-private/linux/if_ether.h @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Global definitions for the Ethernet IEEE 802.3 interface. + * + * Version: @(#)if_ether.h 1.0.1a 02/08/94 + * + * Author: Fred N. van Kempen, + * Donald Becker, + * Alan Cox, + * Steve Whitehouse, + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_IF_ETHER_H +#define _LINUX_IF_ETHER_H + +#include + +/* + * IEEE 802.3 Ethernet magic constants. The frame sizes omit the preamble + * and FCS/CRC (frame check sequence). + */ + +#define ETH_ALEN 6 /* Octets in one ethernet addr */ +#define ETH_TLEN 2 /* Octets in ethernet type field */ +#define ETH_HLEN 14 /* Total octets in header. */ +#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */ +#define ETH_DATA_LEN 1500 /* Max. octets in payload */ +#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ +#define ETH_FCS_LEN 4 /* Octets in the FCS */ + +#define ETH_MIN_MTU 68 /* Min IPv4 MTU per RFC791 */ +#define ETH_MAX_MTU 0xFFFFU /* 65535, same as IP_MAX_MTU */ + +/* + * These are the defined Ethernet Protocol ID's. + */ + +#define ETH_P_LOOP 0x0060 /* Ethernet Loopback packet */ +#define ETH_P_PUP 0x0200 /* Xerox PUP packet */ +#define ETH_P_PUPAT 0x0201 /* Xerox PUP Addr Trans packet */ +#define ETH_P_TSN 0x22F0 /* TSN (IEEE 1722) packet */ +#define ETH_P_ERSPAN2 0x22EB /* ERSPAN version 2 (type III) */ +#define ETH_P_IP 0x0800 /* Internet Protocol packet */ +#define ETH_P_X25 0x0805 /* CCITT X.25 */ +#define ETH_P_ARP 0x0806 /* Address Resolution packet */ +#define ETH_P_BPQ 0x08FF /* G8BPQ AX.25 Ethernet Packet [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_IEEEPUP 0x0a00 /* Xerox IEEE802.3 PUP packet */ +#define ETH_P_IEEEPUPAT 0x0a01 /* Xerox IEEE802.3 PUP Addr Trans packet */ +#define ETH_P_BATMAN 0x4305 /* B.A.T.M.A.N.-Advanced packet [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_DEC 0x6000 /* DEC Assigned proto */ +#define ETH_P_DNA_DL 0x6001 /* DEC DNA Dump/Load */ +#define ETH_P_DNA_RC 0x6002 /* DEC DNA Remote Console */ +#define ETH_P_DNA_RT 0x6003 /* DEC DNA Routing */ +#define ETH_P_LAT 0x6004 /* DEC LAT */ +#define ETH_P_DIAG 0x6005 /* DEC Diagnostics */ +#define ETH_P_CUST 0x6006 /* DEC Customer use */ +#define ETH_P_SCA 0x6007 /* DEC Systems Comms Arch */ +#define ETH_P_TEB 0x6558 /* Trans Ether Bridging */ +#define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */ +#define ETH_P_ATALK 0x809B /* Appletalk DDP */ +#define ETH_P_AARP 0x80F3 /* Appletalk AARP */ +#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ +#define ETH_P_ERSPAN 0x88BE /* ERSPAN type II */ +#define ETH_P_IPX 0x8137 /* IPX over DIX */ +#define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */ +#define ETH_P_PAUSE 0x8808 /* IEEE Pause frames. See 802.3 31B */ +#define ETH_P_SLOW 0x8809 /* Slow Protocol. See 802.3ad 43B */ +#define ETH_P_WCCP 0x883E /* Web-cache coordination protocol + * defined in draft-wilson-wrec-wccp-v2-00.txt */ +#define ETH_P_MPLS_UC 0x8847 /* MPLS Unicast traffic */ +#define ETH_P_MPLS_MC 0x8848 /* MPLS Multicast traffic */ +#define ETH_P_ATMMPOA 0x884c /* MultiProtocol Over ATM */ +#define ETH_P_PPP_DISC 0x8863 /* PPPoE discovery messages */ +#define ETH_P_PPP_SES 0x8864 /* PPPoE session messages */ +#define ETH_P_LINK_CTL 0x886c /* HPNA, wlan link local tunnel */ +#define ETH_P_ATMFATE 0x8884 /* Frame-based ATM Transport + * over Ethernet + */ +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#define ETH_P_PROFINET 0x8892 /* PROFINET */ +#define ETH_P_REALTEK 0x8899 /* Multiple proprietary protocols */ +#define ETH_P_AOE 0x88A2 /* ATA over Ethernet */ +#define ETH_P_ETHERCAT 0x88A4 /* EtherCAT */ +#define ETH_P_8021AD 0x88A8 /* 802.1ad Service VLAN */ +#define ETH_P_802_EX1 0x88B5 /* 802.1 Local Experimental 1. */ +#define ETH_P_PREAUTH 0x88C7 /* 802.11 Preauthentication */ +#define ETH_P_TIPC 0x88CA /* TIPC */ +#define ETH_P_LLDP 0x88CC /* Link Layer Discovery Protocol */ +#define ETH_P_MRP 0x88E3 /* Media Redundancy Protocol */ +#define ETH_P_MACSEC 0x88E5 /* 802.1ae MACsec */ +#define ETH_P_8021AH 0x88E7 /* 802.1ah Backbone Service Tag */ +#define ETH_P_MVRP 0x88F5 /* 802.1Q MVRP */ +#define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */ +#define ETH_P_NCSI 0x88F8 /* NCSI protocol */ +#define ETH_P_PRP 0x88FB /* IEC 62439-3 PRP/HSRv0 */ +#define ETH_P_CFM 0x8902 /* Connectivity Fault Management */ +#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_DSA_A5PSW 0xE001 /* A5PSW Tag Value [ 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 more 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_CANXL 0x000E /* CANXL: eXtended frame Length */ +#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 + */ +#define ETH_P_MCTP 0x00FA /* Management component transport + * protocol packets + */ + +/* + * 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/libnl/include/linux-private/linux/if_link.h b/libnl/include/linux-private/linux/if_link.h new file mode 100644 index 0000000..94fb7ef --- /dev/null +++ b/libnl/include/linux-private/linux/if_link.h @@ -0,0 +1,1397 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_IF_LINK_H +#define _LINUX_IF_LINK_H + +#include +#include + +/* This struct should be in sync with struct rtnl_link_stats64 */ +struct rtnl_link_stats { + __u32 rx_packets; + __u32 tx_packets; + __u32 rx_bytes; + __u32 tx_bytes; + __u32 rx_errors; + __u32 tx_errors; + __u32 rx_dropped; + __u32 tx_dropped; + __u32 multicast; + __u32 collisions; + /* detailed rx_errors: */ + __u32 rx_length_errors; + __u32 rx_over_errors; + __u32 rx_crc_errors; + __u32 rx_frame_errors; + __u32 rx_fifo_errors; + __u32 rx_missed_errors; + + /* 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; +}; + +/** + * struct rtnl_link_stats64 - The main device statistics structure. + * + * @rx_packets: Number of good packets received by the interface. + * For hardware interfaces counts all good packets received from the device + * by the host, including packets which host had to drop at various stages + * of processing (even in the driver). + * + * @tx_packets: Number of packets successfully transmitted. + * For hardware interfaces counts packets which host was able to successfully + * hand over to the device, which does not necessarily mean that packets + * had been successfully transmitted out of the device, only that device + * acknowledged it copied them out of host memory. + * + * @rx_bytes: Number of good received bytes, corresponding to @rx_packets. + * + * For IEEE 802.3 devices should count the length of Ethernet Frames + * excluding the FCS. + * + * @tx_bytes: Number of good transmitted bytes, corresponding to @tx_packets. + * + * For IEEE 802.3 devices should count the length of Ethernet Frames + * excluding the FCS. + * + * @rx_errors: Total number of bad packets received on this network device. + * This counter must include events counted by @rx_length_errors, + * @rx_crc_errors, @rx_frame_errors and other errors not otherwise + * counted. + * + * @tx_errors: Total number of transmit problems. + * This counter must include events counter by @tx_aborted_errors, + * @tx_carrier_errors, @tx_fifo_errors, @tx_heartbeat_errors, + * @tx_window_errors and other errors not otherwise counted. + * + * @rx_dropped: Number of packets received but not processed, + * e.g. due to lack of resources or unsupported protocol. + * For hardware interfaces this counter may include packets discarded + * due to L2 address filtering but should not include packets dropped + * by the device due to buffer exhaustion which are counted separately in + * @rx_missed_errors (since procfs folds those two counters together). + * + * @tx_dropped: Number of packets dropped on their way to transmission, + * e.g. due to lack of resources. + * + * @multicast: Multicast packets received. + * For hardware interfaces this statistic is commonly calculated + * at the device level (unlike @rx_packets) and therefore may include + * packets which did not reach the host. + * + * For IEEE 802.3 devices this counter may be equivalent to: + * + * - 30.3.1.1.21 aMulticastFramesReceivedOK + * + * @collisions: Number of collisions during packet transmissions. + * + * @rx_length_errors: Number of packets dropped due to invalid length. + * Part of aggregate "frame" errors in `/proc/net/dev`. + * + * For IEEE 802.3 devices this counter should be equivalent to a sum + * of the following attributes: + * + * - 30.3.1.1.23 aInRangeLengthErrors + * - 30.3.1.1.24 aOutOfRangeLengthField + * - 30.3.1.1.25 aFrameTooLongErrors + * + * @rx_over_errors: Receiver FIFO overflow event counter. + * + * Historically the count of overflow events. Such events may be + * reported in the receive descriptors or via interrupts, and may + * not correspond one-to-one with dropped packets. + * + * The recommended interpretation for high speed interfaces is - + * number of packets dropped because they did not fit into buffers + * provided by the host, e.g. packets larger than MTU or next buffer + * in the ring was not available for a scatter transfer. + * + * Part of aggregate "frame" errors in `/proc/net/dev`. + * + * This statistics was historically used interchangeably with + * @rx_fifo_errors. + * + * This statistic corresponds to hardware events and is not commonly used + * on software devices. + * + * @rx_crc_errors: Number of packets received with a CRC error. + * Part of aggregate "frame" errors in `/proc/net/dev`. + * + * For IEEE 802.3 devices this counter must be equivalent to: + * + * - 30.3.1.1.6 aFrameCheckSequenceErrors + * + * @rx_frame_errors: Receiver frame alignment errors. + * Part of aggregate "frame" errors in `/proc/net/dev`. + * + * For IEEE 802.3 devices this counter should be equivalent to: + * + * - 30.3.1.1.7 aAlignmentErrors + * + * @rx_fifo_errors: Receiver FIFO error counter. + * + * Historically the count of overflow events. Those events may be + * reported in the receive descriptors or via interrupts, and may + * not correspond one-to-one with dropped packets. + * + * This statistics was used interchangeably with @rx_over_errors. + * Not recommended for use in drivers for high speed interfaces. + * + * This statistic is used on software devices, e.g. to count software + * packet queue overflow (can) or sequencing errors (GRE). + * + * @rx_missed_errors: Count of packets missed by the host. + * Folded into the "drop" counter in `/proc/net/dev`. + * + * Counts number of packets dropped by the device due to lack + * of buffer space. This usually indicates that the host interface + * is slower than the network interface, or host is not keeping up + * with the receive packet rate. + * + * This statistic corresponds to hardware events and is not used + * on software devices. + * + * @tx_aborted_errors: + * Part of aggregate "carrier" errors in `/proc/net/dev`. + * For IEEE 802.3 devices capable of half-duplex operation this counter + * must be equivalent to: + * + * - 30.3.1.1.11 aFramesAbortedDueToXSColls + * + * High speed interfaces may use this counter as a general device + * discard counter. + * + * @tx_carrier_errors: Number of frame transmission errors due to loss + * of carrier during transmission. + * Part of aggregate "carrier" errors in `/proc/net/dev`. + * + * For IEEE 802.3 devices this counter must be equivalent to: + * + * - 30.3.1.1.13 aCarrierSenseErrors + * + * @tx_fifo_errors: Number of frame transmission errors due to device + * FIFO underrun / underflow. This condition occurs when the device + * begins transmission of a frame but is unable to deliver the + * entire frame to the transmitter in time for transmission. + * Part of aggregate "carrier" errors in `/proc/net/dev`. + * + * @tx_heartbeat_errors: Number of Heartbeat / SQE Test errors for + * old half-duplex Ethernet. + * Part of aggregate "carrier" errors in `/proc/net/dev`. + * + * For IEEE 802.3 devices possibly equivalent to: + * + * - 30.3.2.1.4 aSQETestErrors + * + * @tx_window_errors: Number of frame transmission errors due + * to late collisions (for Ethernet - after the first 64B of transmission). + * Part of aggregate "carrier" errors in `/proc/net/dev`. + * + * For IEEE 802.3 devices this counter must be equivalent to: + * + * - 30.3.1.1.10 aLateCollisions + * + * @rx_compressed: Number of correctly received compressed packets. + * This counters is only meaningful for interfaces which support + * packet compression (e.g. CSLIP, PPP). + * + * @tx_compressed: Number of transmitted compressed packets. + * This counters is only meaningful for interfaces which support + * packet compression (e.g. CSLIP, PPP). + * + * @rx_nohandler: Number of packets received on the interface + * but dropped by the networking stack because the device is + * not designated to receive packets (e.g. backup link in a bond). + * + * @rx_otherhost_dropped: Number of packets dropped due to mismatch + * in destination MAC address. + */ +struct rtnl_link_stats64 { + __u64 rx_packets; + __u64 tx_packets; + __u64 rx_bytes; + __u64 tx_bytes; + __u64 rx_errors; + __u64 tx_errors; + __u64 rx_dropped; + __u64 tx_dropped; + __u64 multicast; + __u64 collisions; + + /* detailed rx_errors: */ + __u64 rx_length_errors; + __u64 rx_over_errors; + __u64 rx_crc_errors; + __u64 rx_frame_errors; + __u64 rx_fifo_errors; + __u64 rx_missed_errors; + + /* 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; + + __u64 rx_otherhost_dropped; +}; + +/* Subset of link stats useful for in-HW collection. Meaning of the fields is as + * for struct rtnl_link_stats64. + */ +struct rtnl_hw_stats64 { + __u64 rx_packets; + __u64 tx_packets; + __u64 rx_bytes; + __u64 tx_bytes; + __u64 rx_errors; + __u64 tx_errors; + __u64 rx_dropped; + __u64 tx_dropped; + __u64 multicast; +}; + +/* The struct should be in sync with struct ifmap */ +struct rtnl_link_ifmap { + __u64 mem_start; + __u64 mem_end; + __u64 base_addr; + __u16 irq; + __u8 dma; + __u8 port; +}; + +/* + * IFLA_AF_SPEC + * Contains nested attributes for address family specific attributes. + * Each address family may create a attribute with the address family + * number as type and create its own attribute structure in it. + * + * Example: + * [IFLA_AF_SPEC] = { + * [AF_INET] = { + * [IFLA_INET_CONF] = ..., + * }, + * [AF_INET6] = { + * [IFLA_INET6_FLAGS] = ..., + * [IFLA_INET6_CONF] = ..., + * } + * } + */ + +enum { + IFLA_UNSPEC, + IFLA_ADDRESS, + IFLA_BROADCAST, + IFLA_IFNAME, + IFLA_MTU, + IFLA_LINK, + IFLA_QDISC, + IFLA_STATS, + IFLA_COST, +#define IFLA_COST IFLA_COST + IFLA_PRIORITY, +#define IFLA_PRIORITY IFLA_PRIORITY + IFLA_MASTER, +#define IFLA_MASTER IFLA_MASTER + IFLA_WIRELESS, /* Wireless Extension event - see wireless.h */ +#define IFLA_WIRELESS IFLA_WIRELESS + IFLA_PROTINFO, /* Protocol specific information for a link */ +#define IFLA_PROTINFO IFLA_PROTINFO + IFLA_TXQLEN, +#define IFLA_TXQLEN IFLA_TXQLEN + IFLA_MAP, +#define IFLA_MAP IFLA_MAP + IFLA_WEIGHT, +#define IFLA_WEIGHT IFLA_WEIGHT + IFLA_OPERSTATE, + IFLA_LINKMODE, + IFLA_LINKINFO, +#define IFLA_LINKINFO IFLA_LINKINFO + IFLA_NET_NS_PID, + IFLA_IFALIAS, + IFLA_NUM_VF, /* Number of VFs if device is SR-IOV PF */ + IFLA_VFINFO_LIST, + IFLA_STATS64, + IFLA_VF_PORTS, + IFLA_PORT_SELF, + IFLA_AF_SPEC, + IFLA_GROUP, /* Group the device belongs to */ + IFLA_NET_NS_FD, + IFLA_EXT_MASK, /* Extended info mask, VFs, etc */ + IFLA_PROMISCUITY, /* Promiscuity count: > 0 means acts PROMISC */ +#define IFLA_PROMISCUITY IFLA_PROMISCUITY + IFLA_NUM_TX_QUEUES, + IFLA_NUM_RX_QUEUES, + IFLA_CARRIER, + IFLA_PHYS_PORT_ID, + IFLA_CARRIER_CHANGES, + IFLA_PHYS_SWITCH_ID, + IFLA_LINK_NETNSID, + IFLA_PHYS_PORT_NAME, + IFLA_PROTO_DOWN, + IFLA_GSO_MAX_SEGS, + IFLA_GSO_MAX_SIZE, + IFLA_PAD, + IFLA_XDP, + IFLA_EVENT, + IFLA_NEW_NETNSID, + IFLA_IF_NETNSID, + IFLA_TARGET_NETNSID = IFLA_IF_NETNSID, /* new alias */ + IFLA_CARRIER_UP_COUNT, + IFLA_CARRIER_DOWN_COUNT, + IFLA_NEW_IFINDEX, + IFLA_MIN_MTU, + IFLA_MAX_MTU, + IFLA_PROP_LIST, + IFLA_ALT_IFNAME, /* Alternative ifname */ + IFLA_PERM_ADDRESS, + IFLA_PROTO_DOWN_REASON, + + /* device (sysfs) name as parent, used instead + * of IFLA_LINK where there's no parent netdev + */ + IFLA_PARENT_DEV_NAME, + IFLA_PARENT_DEV_BUS_NAME, + IFLA_GRO_MAX_SIZE, + IFLA_TSO_MAX_SIZE, + IFLA_TSO_MAX_SEGS, + IFLA_ALLMULTI, /* Allmulti count: > 0 means acts ALLMULTI */ + + IFLA_DEVLINK_PORT, + + IFLA_GSO_IPV4_MAX_SIZE, + IFLA_GRO_IPV4_MAX_SIZE, + + __IFLA_MAX +}; + + +#define IFLA_MAX (__IFLA_MAX - 1) + +enum { + IFLA_PROTO_DOWN_REASON_UNSPEC, + IFLA_PROTO_DOWN_REASON_MASK, /* u32, mask for reason bits */ + IFLA_PROTO_DOWN_REASON_VALUE, /* u32, reason bit value */ + + __IFLA_PROTO_DOWN_REASON_CNT, + IFLA_PROTO_DOWN_REASON_MAX = __IFLA_PROTO_DOWN_REASON_CNT - 1 +}; + +/* backwards compatibility for userspace */ +#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) +#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) + +enum { + IFLA_INET_UNSPEC, + IFLA_INET_CONF, + __IFLA_INET_MAX, +}; + +#define IFLA_INET_MAX (__IFLA_INET_MAX - 1) + +/* ifi_flags. + + IFF_* flags. + + The only change is: + IFF_LOOPBACK, IFF_BROADCAST and IFF_POINTOPOINT are + more not changeable by user. They describe link media + characteristics and set by device driver. + + Comments: + - Combination IFF_BROADCAST|IFF_POINTOPOINT is invalid + - If neither of these three flags are set; + the interface is NBMA. + + - IFF_MULTICAST does not mean anything special: + multicasts can be used on all not-NBMA links. + IFF_MULTICAST means that this media uses special encapsulation + for multicast frames. Apparently, all IFF_POINTOPOINT and + IFF_BROADCAST devices are able to use multicasts too. + */ + +/* IFLA_LINK. + For usual devices it is equal ifi_index. + If it is a "virtual interface" (f.e. tunnel), ifi_link + can point to real physical interface (f.e. for bandwidth calculations), + or maybe 0, what means, that real media is unknown (usual + for IPIP tunnels, when route to endpoint is allowed to change) + */ + +/* Subtype attributes for IFLA_PROTINFO */ +enum { + IFLA_INET6_UNSPEC, + IFLA_INET6_FLAGS, /* link flags */ + IFLA_INET6_CONF, /* sysctl parameters */ + IFLA_INET6_STATS, /* statistics */ + IFLA_INET6_MCAST, /* MC things. What of them? */ + IFLA_INET6_CACHEINFO, /* time values and max reasm size */ + IFLA_INET6_ICMP6STATS, /* statistics (icmpv6) */ + IFLA_INET6_TOKEN, /* device token */ + IFLA_INET6_ADDR_GEN_MODE, /* implicit address generator mode */ + IFLA_INET6_RA_MTU, /* mtu carried in the RA message */ + __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_MCAST_QUERIER_STATE, + __IFLA_BR_MAX, +}; + +#define IFLA_BR_MAX (__IFLA_BR_MAX - 1) + +struct ifla_bridge_id { + __u8 prio[2]; + __u8 addr[6]; /* ETH_ALEN */ +}; + +enum { + BRIDGE_MODE_UNSPEC, + BRIDGE_MODE_HAIRPIN, +}; + +enum { + IFLA_BRPORT_UNSPEC, + IFLA_BRPORT_STATE, /* Spanning tree state */ + IFLA_BRPORT_PRIORITY, /* " priority */ + IFLA_BRPORT_COST, /* " cost */ + IFLA_BRPORT_MODE, /* mode (hairpin) */ + IFLA_BRPORT_GUARD, /* bpdu guard */ + IFLA_BRPORT_PROTECT, /* root port protection */ + IFLA_BRPORT_FAST_LEAVE, /* multicast fast leave */ + IFLA_BRPORT_LEARNING, /* mac learning */ + IFLA_BRPORT_UNICAST_FLOOD, /* flood unicast traffic */ + IFLA_BRPORT_PROXYARP, /* proxy ARP */ + IFLA_BRPORT_LEARNING_SYNC, /* mac learning sync from device */ + IFLA_BRPORT_PROXYARP_WIFI, /* proxy ARP for Wi-Fi */ + IFLA_BRPORT_ROOT_ID, /* designated root */ + IFLA_BRPORT_BRIDGE_ID, /* designated bridge */ + IFLA_BRPORT_DESIGNATED_PORT, + IFLA_BRPORT_DESIGNATED_COST, + IFLA_BRPORT_ID, + IFLA_BRPORT_NO, + IFLA_BRPORT_TOPOLOGY_CHANGE_ACK, + IFLA_BRPORT_CONFIG_PENDING, + IFLA_BRPORT_MESSAGE_AGE_TIMER, + IFLA_BRPORT_FORWARD_DELAY_TIMER, + IFLA_BRPORT_HOLD_TIMER, + IFLA_BRPORT_FLUSH, + IFLA_BRPORT_MULTICAST_ROUTER, + IFLA_BRPORT_PAD, + IFLA_BRPORT_MCAST_FLOOD, + IFLA_BRPORT_MCAST_TO_UCAST, + IFLA_BRPORT_VLAN_TUNNEL, + IFLA_BRPORT_BCAST_FLOOD, + IFLA_BRPORT_GROUP_FWD_MASK, + IFLA_BRPORT_NEIGH_SUPPRESS, + IFLA_BRPORT_ISOLATED, + IFLA_BRPORT_BACKUP_PORT, + IFLA_BRPORT_MRP_RING_OPEN, + IFLA_BRPORT_MRP_IN_OPEN, + IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT, + IFLA_BRPORT_MCAST_EHT_HOSTS_CNT, + IFLA_BRPORT_LOCKED, + IFLA_BRPORT_MAB, + IFLA_BRPORT_MCAST_N_GROUPS, + IFLA_BRPORT_MCAST_MAX_GROUPS, + IFLA_BRPORT_NEIGH_VLAN_SUPPRESS, + __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_BC_QUEUE_LEN, + IFLA_MACVLAN_BC_QUEUE_LEN_USED, + IFLA_MACVLAN_BC_CUTOFF, + __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 +#define MACVLAN_FLAG_NODST 2 /* skip dst macvlan if matching src macvlan */ + +/* VRF section */ +enum { + IFLA_VRF_UNSPEC, + IFLA_VRF_TABLE, + __IFLA_VRF_MAX +}; + +#define IFLA_VRF_MAX (__IFLA_VRF_MAX - 1) + +enum { + IFLA_VRF_PORT_UNSPEC, + IFLA_VRF_PORT_TABLE, + __IFLA_VRF_PORT_MAX +}; + +#define IFLA_VRF_PORT_MAX (__IFLA_VRF_PORT_MAX - 1) + +/* MACSEC section */ +enum { + IFLA_MACSEC_UNSPEC, + IFLA_MACSEC_SCI, + IFLA_MACSEC_PORT, + IFLA_MACSEC_ICV_LEN, + IFLA_MACSEC_CIPHER_SUITE, + IFLA_MACSEC_WINDOW, + IFLA_MACSEC_ENCODING_SA, + IFLA_MACSEC_ENCRYPT, + IFLA_MACSEC_PROTECT, + IFLA_MACSEC_INC_SCI, + IFLA_MACSEC_ES, + IFLA_MACSEC_SCB, + IFLA_MACSEC_REPLAY_PROTECT, + IFLA_MACSEC_VALIDATION, + IFLA_MACSEC_PAD, + IFLA_MACSEC_OFFLOAD, + __IFLA_MACSEC_MAX, +}; + +#define IFLA_MACSEC_MAX (__IFLA_MACSEC_MAX - 1) + +/* XFRM section */ +enum { + IFLA_XFRM_UNSPEC, + IFLA_XFRM_LINK, + IFLA_XFRM_IF_ID, + IFLA_XFRM_COLLECT_METADATA, + __IFLA_XFRM_MAX +}; + +#define IFLA_XFRM_MAX (__IFLA_XFRM_MAX - 1) + +enum macsec_validation_type { + MACSEC_VALIDATE_DISABLED = 0, + MACSEC_VALIDATE_CHECK = 1, + MACSEC_VALIDATE_STRICT = 2, + __MACSEC_VALIDATE_END, + MACSEC_VALIDATE_MAX = __MACSEC_VALIDATE_END - 1, +}; + +enum macsec_offload { + MACSEC_OFFLOAD_OFF = 0, + MACSEC_OFFLOAD_PHY = 1, + MACSEC_OFFLOAD_MAC = 2, + __MACSEC_OFFLOAD_END, + MACSEC_OFFLOAD_MAX = __MACSEC_OFFLOAD_END - 1, +}; + +/* IPVLAN section */ +enum { + IFLA_IPVLAN_UNSPEC, + IFLA_IPVLAN_MODE, + IFLA_IPVLAN_FLAGS, + __IFLA_IPVLAN_MAX +}; + +#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1) + +enum ipvlan_mode { + IPVLAN_MODE_L2 = 0, + IPVLAN_MODE_L3, + IPVLAN_MODE_L3S, + IPVLAN_MODE_MAX +}; + +#define IPVLAN_F_PRIVATE 0x01 +#define IPVLAN_F_VEPA 0x02 + +/* Tunnel RTM header */ +struct tunnel_msg { + __u8 family; + __u8 flags; + __u16 reserved2; + __u32 ifindex; +}; + +/* VXLAN section */ + +/* include statistics in the dump */ +#define TUNNEL_MSG_FLAG_STATS 0x01 + +#define TUNNEL_MSG_VALID_USER_FLAGS TUNNEL_MSG_FLAG_STATS + +/* Embedded inside VXLAN_VNIFILTER_ENTRY_STATS */ +enum { + VNIFILTER_ENTRY_STATS_UNSPEC, + VNIFILTER_ENTRY_STATS_RX_BYTES, + VNIFILTER_ENTRY_STATS_RX_PKTS, + VNIFILTER_ENTRY_STATS_RX_DROPS, + VNIFILTER_ENTRY_STATS_RX_ERRORS, + VNIFILTER_ENTRY_STATS_TX_BYTES, + VNIFILTER_ENTRY_STATS_TX_PKTS, + VNIFILTER_ENTRY_STATS_TX_DROPS, + VNIFILTER_ENTRY_STATS_TX_ERRORS, + VNIFILTER_ENTRY_STATS_PAD, + __VNIFILTER_ENTRY_STATS_MAX +}; +#define VNIFILTER_ENTRY_STATS_MAX (__VNIFILTER_ENTRY_STATS_MAX - 1) + +enum { + VXLAN_VNIFILTER_ENTRY_UNSPEC, + VXLAN_VNIFILTER_ENTRY_START, + VXLAN_VNIFILTER_ENTRY_END, + VXLAN_VNIFILTER_ENTRY_GROUP, + VXLAN_VNIFILTER_ENTRY_GROUP6, + VXLAN_VNIFILTER_ENTRY_STATS, + __VXLAN_VNIFILTER_ENTRY_MAX +}; +#define VXLAN_VNIFILTER_ENTRY_MAX (__VXLAN_VNIFILTER_ENTRY_MAX - 1) + +enum { + VXLAN_VNIFILTER_UNSPEC, + VXLAN_VNIFILTER_ENTRY, + __VXLAN_VNIFILTER_MAX +}; +#define VXLAN_VNIFILTER_MAX (__VXLAN_VNIFILTER_MAX - 1) + +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_VNIFILTER, /* only applicable with COLLECT_METADATA mode */ + __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_INNER_PROTO_INHERIT, + __IFLA_GENEVE_MAX +}; +#define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1) + +enum ifla_geneve_df { + GENEVE_DF_UNSET = 0, + GENEVE_DF_SET, + GENEVE_DF_INHERIT, + __GENEVE_DF_END, + GENEVE_DF_MAX = __GENEVE_DF_END - 1, +}; + +/* Bareudp section */ +enum { + IFLA_BAREUDP_UNSPEC, + IFLA_BAREUDP_PORT, + IFLA_BAREUDP_ETHERTYPE, + IFLA_BAREUDP_SRCPORT_MIN, + IFLA_BAREUDP_MULTIPROTO_MODE, + __IFLA_BAREUDP_MAX +}; + +#define IFLA_BAREUDP_MAX (__IFLA_BAREUDP_MAX - 1) + +/* PPP section */ +enum { + IFLA_PPP_UNSPEC, + IFLA_PPP_DEV_FD, + __IFLA_PPP_MAX +}; +#define IFLA_PPP_MAX (__IFLA_PPP_MAX - 1) + +/* GTP section */ + +enum ifla_gtp_role { + GTP_ROLE_GGSN = 0, + GTP_ROLE_SGSN, +}; + +enum { + IFLA_GTP_UNSPEC, + IFLA_GTP_FD0, + IFLA_GTP_FD1, + IFLA_GTP_PDP_HASHSIZE, + IFLA_GTP_ROLE, + IFLA_GTP_CREATE_SOCKETS, + IFLA_GTP_RESTART_COUNT, + __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_AD_LACP_ACTIVE, + IFLA_BOND_MISSED_MAX, + IFLA_BOND_NS_IP6_TARGET, + __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_PRIO, + __IFLA_BOND_SLAVE_MAX, +}; + +#define IFLA_BOND_SLAVE_MAX (__IFLA_BOND_SLAVE_MAX - 1) + +/* SR-IOV virtual function management section */ + +enum { + IFLA_VF_INFO_UNSPEC, + IFLA_VF_INFO, + __IFLA_VF_INFO_MAX, +}; + +#define IFLA_VF_INFO_MAX (__IFLA_VF_INFO_MAX - 1) + +enum { + IFLA_VF_UNSPEC, + IFLA_VF_MAC, /* Hardware queue specific attributes */ + IFLA_VF_VLAN, /* VLAN ID and QoS */ + IFLA_VF_TX_RATE, /* Max TX Bandwidth Allocation */ + IFLA_VF_SPOOFCHK, /* Spoof Checking on/off switch */ + IFLA_VF_LINK_STATE, /* link state enable/disable/auto switch */ + IFLA_VF_RATE, /* Min and Max TX Bandwidth Allocation */ + IFLA_VF_RSS_QUERY_EN, /* RSS Redirection Table and Hash Key query + * on/off switch + */ + IFLA_VF_STATS, /* network device statistics */ + IFLA_VF_TRUST, /* Trust VF */ + IFLA_VF_IB_NODE_GUID, /* VF Infiniband node GUID */ + IFLA_VF_IB_PORT_GUID, /* VF Infiniband port GUID */ + IFLA_VF_VLAN_LIST, /* nested list of vlans, option for QinQ */ + IFLA_VF_BROADCAST, /* VF broadcast */ + __IFLA_VF_MAX, +}; + +#define IFLA_VF_MAX (__IFLA_VF_MAX - 1) + +struct ifla_vf_mac { + __u32 vf; + __u8 mac[32]; /* MAX_ADDR_LEN */ +}; + +struct ifla_vf_broadcast { + __u8 broadcast[32]; +}; + +struct ifla_vf_vlan { + __u32 vf; + __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */ + __u32 qos; +}; + +enum { + IFLA_VF_VLAN_INFO_UNSPEC, + IFLA_VF_VLAN_INFO, /* VLAN ID, QoS and VLAN protocol */ + __IFLA_VF_VLAN_INFO_MAX, +}; + +#define IFLA_VF_VLAN_INFO_MAX (__IFLA_VF_VLAN_INFO_MAX - 1) +#define MAX_VLAN_LIST_LEN 1 + +struct ifla_vf_vlan_info { + __u32 vf; + __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */ + __u32 qos; + __be16 vlan_proto; /* VLAN protocol either 802.1Q or 802.1ad */ +}; + +struct ifla_vf_tx_rate { + __u32 vf; + __u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */ +}; + +struct ifla_vf_rate { + __u32 vf; + __u32 min_tx_rate; /* Min Bandwidth in Mbps */ + __u32 max_tx_rate; /* Max Bandwidth in Mbps */ +}; + +struct ifla_vf_spoofchk { + __u32 vf; + __u32 setting; +}; + +struct ifla_vf_guid { + __u32 vf; + __u64 guid; +}; + +enum { + IFLA_VF_LINK_STATE_AUTO, /* link state of the uplink */ + IFLA_VF_LINK_STATE_ENABLE, /* link always up */ + IFLA_VF_LINK_STATE_DISABLE, /* link always down */ + __IFLA_VF_LINK_STATE_MAX, +}; + +struct ifla_vf_link_state { + __u32 vf; + __u32 link_state; +}; + +struct ifla_vf_rss_query_en { + __u32 vf; + __u32 setting; +}; + +enum { + IFLA_VF_STATS_RX_PACKETS, + IFLA_VF_STATS_TX_PACKETS, + IFLA_VF_STATS_RX_BYTES, + IFLA_VF_STATS_TX_BYTES, + IFLA_VF_STATS_BROADCAST, + IFLA_VF_STATS_MULTICAST, + IFLA_VF_STATS_PAD, + IFLA_VF_STATS_RX_DROPPED, + IFLA_VF_STATS_TX_DROPPED, + __IFLA_VF_STATS_MAX, +}; + +#define IFLA_VF_STATS_MAX (__IFLA_VF_STATS_MAX - 1) + +struct ifla_vf_trust { + __u32 vf; + __u32 setting; +}; + +/* VF ports management section + * + * Nested layout of set/get msg is: + * + * [IFLA_NUM_VF] + * [IFLA_VF_PORTS] + * [IFLA_VF_PORT] + * [IFLA_PORT_*], ... + * [IFLA_VF_PORT] + * [IFLA_PORT_*], ... + * ... + * [IFLA_PORT_SELF] + * [IFLA_PORT_*], ... + */ + +enum { + IFLA_VF_PORT_UNSPEC, + IFLA_VF_PORT, /* nest */ + __IFLA_VF_PORT_MAX, +}; + +#define IFLA_VF_PORT_MAX (__IFLA_VF_PORT_MAX - 1) + +enum { + IFLA_PORT_UNSPEC, + IFLA_PORT_VF, /* __u32 */ + IFLA_PORT_PROFILE, /* string */ + IFLA_PORT_VSI_TYPE, /* 802.1Qbg (pre-)standard VDP */ + IFLA_PORT_INSTANCE_UUID, /* binary UUID */ + IFLA_PORT_HOST_UUID, /* binary UUID */ + IFLA_PORT_REQUEST, /* __u8 */ + IFLA_PORT_RESPONSE, /* __u16, output only */ + __IFLA_PORT_MAX, +}; + +#define IFLA_PORT_MAX (__IFLA_PORT_MAX - 1) + +#define PORT_PROFILE_MAX 40 +#define PORT_UUID_MAX 16 +#define PORT_SELF_VF -1 + +enum { + PORT_REQUEST_PREASSOCIATE = 0, + PORT_REQUEST_PREASSOCIATE_RR, + PORT_REQUEST_ASSOCIATE, + PORT_REQUEST_DISASSOCIATE, +}; + +enum { + PORT_VDP_RESPONSE_SUCCESS = 0, + PORT_VDP_RESPONSE_INVALID_FORMAT, + PORT_VDP_RESPONSE_INSUFFICIENT_RESOURCES, + PORT_VDP_RESPONSE_UNUSED_VTID, + PORT_VDP_RESPONSE_VTID_VIOLATION, + PORT_VDP_RESPONSE_VTID_VERSION_VIOALTION, + PORT_VDP_RESPONSE_OUT_OF_SYNC, + /* 0x08-0xFF reserved for future VDP use */ + PORT_PROFILE_RESPONSE_SUCCESS = 0x100, + PORT_PROFILE_RESPONSE_INPROGRESS, + PORT_PROFILE_RESPONSE_INVALID, + PORT_PROFILE_RESPONSE_BADSTATE, + PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES, + PORT_PROFILE_RESPONSE_ERROR, +}; + +struct ifla_port_vsi { + __u8 vsi_mgr_id; + __u8 vsi_type_id[3]; + __u8 vsi_type_version; + __u8 pad[3]; +}; + + +/* IPoIB section */ + +enum { + IFLA_IPOIB_UNSPEC, + IFLA_IPOIB_PKEY, + IFLA_IPOIB_MODE, + IFLA_IPOIB_UMCAST, + __IFLA_IPOIB_MAX +}; + +enum { + IPOIB_MODE_DATAGRAM = 0, /* using unreliable datagram QPs */ + IPOIB_MODE_CONNECTED = 1, /* using connected QPs */ +}; + +#define IFLA_IPOIB_MAX (__IFLA_IPOIB_MAX - 1) + + +/* HSR/PRP section, both uses same interface */ + +/* Different redundancy protocols for hsr device */ +enum { + HSR_PROTOCOL_HSR, + HSR_PROTOCOL_PRP, + HSR_PROTOCOL_MAX, +}; + +enum { + IFLA_HSR_UNSPEC, + IFLA_HSR_SLAVE1, + IFLA_HSR_SLAVE2, + IFLA_HSR_MULTICAST_SPEC, /* Last byte of supervision addr */ + IFLA_HSR_SUPERVISION_ADDR, /* Supervision frame multicast addr */ + IFLA_HSR_SEQ_NR, + IFLA_HSR_VERSION, /* HSR version */ + IFLA_HSR_PROTOCOL, /* Indicate different protocol than + * HSR. For example PRP. + */ + __IFLA_HSR_MAX, +}; + +#define IFLA_HSR_MAX (__IFLA_HSR_MAX - 1) + +/* STATS section */ + +struct if_stats_msg { + __u8 family; + __u8 pad1; + __u16 pad2; + __u32 ifindex; + __u32 filter_mask; +}; + +/* A stats attribute can be netdev specific or a global stat. + * For netdev stats, lets use the prefix IFLA_STATS_LINK_* + */ +enum { + IFLA_STATS_UNSPEC, /* also used as 64bit pad attribute */ + IFLA_STATS_LINK_64, + IFLA_STATS_LINK_XSTATS, + IFLA_STATS_LINK_XSTATS_SLAVE, + IFLA_STATS_LINK_OFFLOAD_XSTATS, + IFLA_STATS_AF_SPEC, + __IFLA_STATS_MAX, +}; + +#define IFLA_STATS_MAX (__IFLA_STATS_MAX - 1) + +#define IFLA_STATS_FILTER_BIT(ATTR) (1 << (ATTR - 1)) + +enum { + IFLA_STATS_GETSET_UNSPEC, + IFLA_STATS_GET_FILTERS, /* Nest of IFLA_STATS_LINK_xxx, each a u32 with + * a filter mask for the corresponding group. + */ + IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS, /* 0 or 1 as u8 */ + __IFLA_STATS_GETSET_MAX, +}; + +#define IFLA_STATS_GETSET_MAX (__IFLA_STATS_GETSET_MAX - 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_HW_S_INFO, /* HW stats info. A nest */ + IFLA_OFFLOAD_XSTATS_L3_STATS, /* struct rtnl_hw_stats64 */ + __IFLA_OFFLOAD_XSTATS_MAX +}; +#define IFLA_OFFLOAD_XSTATS_MAX (__IFLA_OFFLOAD_XSTATS_MAX - 1) + +enum { + IFLA_OFFLOAD_XSTATS_HW_S_INFO_UNSPEC, + IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST, /* u8 */ + IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED, /* u8 */ + __IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX, +}; +#define IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX \ + (__IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX - 1) + +/* XDP section */ + +#define XDP_FLAGS_UPDATE_IF_NOEXIST (1U << 0) +#define XDP_FLAGS_SKB_MODE (1U << 1) +#define XDP_FLAGS_DRV_MODE (1U << 2) +#define XDP_FLAGS_HW_MODE (1U << 3) +#define XDP_FLAGS_REPLACE (1U << 4) +#define XDP_FLAGS_MODES (XDP_FLAGS_SKB_MODE | \ + XDP_FLAGS_DRV_MODE | \ + XDP_FLAGS_HW_MODE) +#define XDP_FLAGS_MASK (XDP_FLAGS_UPDATE_IF_NOEXIST | \ + XDP_FLAGS_MODES | XDP_FLAGS_REPLACE) + +/* These are stored into IFLA_XDP_ATTACHED on dump. */ +enum { + XDP_ATTACHED_NONE = 0, + XDP_ATTACHED_DRV, + XDP_ATTACHED_SKB, + XDP_ATTACHED_HW, + XDP_ATTACHED_MULTI, +}; + +enum { + IFLA_XDP_UNSPEC, + IFLA_XDP_FD, + IFLA_XDP_ATTACHED, + IFLA_XDP_FLAGS, + IFLA_XDP_PROG_ID, + IFLA_XDP_DRV_PROG_ID, + IFLA_XDP_SKB_PROG_ID, + IFLA_XDP_HW_PROG_ID, + IFLA_XDP_EXPECTED_FD, + __IFLA_XDP_MAX, +}; + +#define IFLA_XDP_MAX (__IFLA_XDP_MAX - 1) + +enum { + IFLA_EVENT_NONE, + IFLA_EVENT_REBOOT, /* internal reset / reboot */ + IFLA_EVENT_FEATURES, /* change in offload features */ + IFLA_EVENT_BONDING_FAILOVER, /* change in active slave */ + IFLA_EVENT_NOTIFY_PEERS, /* re-sent grat. arp/ndisc */ + IFLA_EVENT_IGMP_RESEND, /* re-sent IGMP JOIN */ + IFLA_EVENT_BONDING_OPTIONS, /* change in bonding options */ +}; + +/* tun section */ + +enum { + IFLA_TUN_UNSPEC, + IFLA_TUN_OWNER, + IFLA_TUN_GROUP, + IFLA_TUN_TYPE, + IFLA_TUN_PI, + IFLA_TUN_VNET_HDR, + IFLA_TUN_PERSIST, + IFLA_TUN_MULTI_QUEUE, + IFLA_TUN_NUM_QUEUES, + IFLA_TUN_NUM_DISABLED_QUEUES, + __IFLA_TUN_MAX, +}; + +#define IFLA_TUN_MAX (__IFLA_TUN_MAX - 1) + +/* rmnet section */ + +#define RMNET_FLAGS_INGRESS_DEAGGREGATION (1U << 0) +#define RMNET_FLAGS_INGRESS_MAP_COMMANDS (1U << 1) +#define RMNET_FLAGS_INGRESS_MAP_CKSUMV4 (1U << 2) +#define RMNET_FLAGS_EGRESS_MAP_CKSUMV4 (1U << 3) +#define RMNET_FLAGS_INGRESS_MAP_CKSUMV5 (1U << 4) +#define RMNET_FLAGS_EGRESS_MAP_CKSUMV5 (1U << 5) + +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; +}; + +/* MCTP section */ + +enum { + IFLA_MCTP_UNSPEC, + IFLA_MCTP_NET, + __IFLA_MCTP_MAX, +}; + +#define IFLA_MCTP_MAX (__IFLA_MCTP_MAX - 1) + +/* DSA section */ + +enum { + IFLA_DSA_UNSPEC, + IFLA_DSA_MASTER, + __IFLA_DSA_MAX, +}; + +#define IFLA_DSA_MAX (__IFLA_DSA_MAX - 1) + +#endif /* _LINUX_IF_LINK_H */ diff --git a/libnl/include/linux-private/linux/if_macsec.h b/libnl/include/linux-private/linux/if_macsec.h new file mode 100644 index 0000000..6edfea0 --- /dev/null +++ b/libnl/include/linux-private/linux/if_macsec.h @@ -0,0 +1,194 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * include/uapi/linux/if_macsec.h - MACsec device + * + * Copyright (c) 2015 Sabrina Dubroca + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _MACSEC_H +#define _MACSEC_H + +#include + +#define MACSEC_GENL_NAME "macsec" +#define MACSEC_GENL_VERSION 1 + +#define MACSEC_MAX_KEY_LEN 128 + +#define MACSEC_KEYID_LEN 16 + +#define MACSEC_SALT_LEN 12 + +/* cipher IDs as per IEEE802.1AE-2018 (Table 14-1) */ +#define MACSEC_CIPHER_ID_GCM_AES_128 0x0080C20001000001ULL +#define MACSEC_CIPHER_ID_GCM_AES_256 0x0080C20001000002ULL +#define MACSEC_CIPHER_ID_GCM_AES_XPN_128 0x0080C20001000003ULL +#define MACSEC_CIPHER_ID_GCM_AES_XPN_256 0x0080C20001000004ULL + +/* deprecated cipher ID for GCM-AES-128 */ +#define MACSEC_DEFAULT_CIPHER_ID 0x0080020001000001ULL +#define MACSEC_DEFAULT_CIPHER_ALT MACSEC_CIPHER_ID_GCM_AES_128 + +#define MACSEC_MIN_ICV_LEN 8 +#define MACSEC_MAX_ICV_LEN 32 +/* upper limit for ICV length as recommended by IEEE802.1AE-2006 */ +#define MACSEC_STD_ICV_LEN 16 + +enum macsec_attrs { + MACSEC_ATTR_UNSPEC, + MACSEC_ATTR_IFINDEX, /* u32, ifindex of the MACsec netdevice */ + MACSEC_ATTR_RXSC_CONFIG, /* config, nested macsec_rxsc_attrs */ + MACSEC_ATTR_SA_CONFIG, /* config, nested macsec_sa_attrs */ + MACSEC_ATTR_SECY, /* dump, nested macsec_secy_attrs */ + MACSEC_ATTR_TXSA_LIST, /* dump, nested, macsec_sa_attrs for each TXSA */ + MACSEC_ATTR_RXSC_LIST, /* dump, nested, macsec_rxsc_attrs for each RXSC */ + MACSEC_ATTR_TXSC_STATS, /* dump, nested, macsec_txsc_stats_attr */ + MACSEC_ATTR_SECY_STATS, /* dump, nested, macsec_secy_stats_attr */ + MACSEC_ATTR_OFFLOAD, /* config, nested, macsec_offload_attrs */ + __MACSEC_ATTR_END, + NUM_MACSEC_ATTR = __MACSEC_ATTR_END, + MACSEC_ATTR_MAX = __MACSEC_ATTR_END - 1, +}; + +enum macsec_secy_attrs { + MACSEC_SECY_ATTR_UNSPEC, + MACSEC_SECY_ATTR_SCI, + MACSEC_SECY_ATTR_ENCODING_SA, + MACSEC_SECY_ATTR_WINDOW, + MACSEC_SECY_ATTR_CIPHER_SUITE, + MACSEC_SECY_ATTR_ICV_LEN, + MACSEC_SECY_ATTR_PROTECT, + MACSEC_SECY_ATTR_REPLAY, + MACSEC_SECY_ATTR_OPER, + MACSEC_SECY_ATTR_VALIDATE, + MACSEC_SECY_ATTR_ENCRYPT, + MACSEC_SECY_ATTR_INC_SCI, + MACSEC_SECY_ATTR_ES, + MACSEC_SECY_ATTR_SCB, + MACSEC_SECY_ATTR_PAD, + __MACSEC_SECY_ATTR_END, + NUM_MACSEC_SECY_ATTR = __MACSEC_SECY_ATTR_END, + MACSEC_SECY_ATTR_MAX = __MACSEC_SECY_ATTR_END - 1, +}; + +enum macsec_rxsc_attrs { + MACSEC_RXSC_ATTR_UNSPEC, + MACSEC_RXSC_ATTR_SCI, /* config/dump, u64 */ + MACSEC_RXSC_ATTR_ACTIVE, /* config/dump, u8 0..1 */ + MACSEC_RXSC_ATTR_SA_LIST, /* dump, nested */ + MACSEC_RXSC_ATTR_STATS, /* dump, nested, macsec_rxsc_stats_attr */ + MACSEC_RXSC_ATTR_PAD, + __MACSEC_RXSC_ATTR_END, + NUM_MACSEC_RXSC_ATTR = __MACSEC_RXSC_ATTR_END, + MACSEC_RXSC_ATTR_MAX = __MACSEC_RXSC_ATTR_END - 1, +}; + +enum macsec_sa_attrs { + MACSEC_SA_ATTR_UNSPEC, + MACSEC_SA_ATTR_AN, /* config/dump, u8 0..3 */ + MACSEC_SA_ATTR_ACTIVE, /* config/dump, u8 0..1 */ + MACSEC_SA_ATTR_PN, /* config/dump, u32/u64 (u64 if XPN) */ + MACSEC_SA_ATTR_KEY, /* config, data */ + MACSEC_SA_ATTR_KEYID, /* config/dump, 128-bit */ + MACSEC_SA_ATTR_STATS, /* dump, nested, macsec_sa_stats_attr */ + MACSEC_SA_ATTR_PAD, + MACSEC_SA_ATTR_SSCI, /* config/dump, u32 - XPN only */ + MACSEC_SA_ATTR_SALT, /* config, 96-bit - XPN only */ + __MACSEC_SA_ATTR_END, + NUM_MACSEC_SA_ATTR = __MACSEC_SA_ATTR_END, + MACSEC_SA_ATTR_MAX = __MACSEC_SA_ATTR_END - 1, +}; + +enum macsec_offload_attrs { + MACSEC_OFFLOAD_ATTR_UNSPEC, + MACSEC_OFFLOAD_ATTR_TYPE, /* config/dump, u8 0..2 */ + MACSEC_OFFLOAD_ATTR_PAD, + __MACSEC_OFFLOAD_ATTR_END, + NUM_MACSEC_OFFLOAD_ATTR = __MACSEC_OFFLOAD_ATTR_END, + MACSEC_OFFLOAD_ATTR_MAX = __MACSEC_OFFLOAD_ATTR_END - 1, +}; + +enum macsec_nl_commands { + MACSEC_CMD_GET_TXSC, + MACSEC_CMD_ADD_RXSC, + MACSEC_CMD_DEL_RXSC, + MACSEC_CMD_UPD_RXSC, + MACSEC_CMD_ADD_TXSA, + MACSEC_CMD_DEL_TXSA, + MACSEC_CMD_UPD_TXSA, + MACSEC_CMD_ADD_RXSA, + MACSEC_CMD_DEL_RXSA, + MACSEC_CMD_UPD_RXSA, + MACSEC_CMD_UPD_OFFLOAD, +}; + +/* u64 per-RXSC stats */ +enum macsec_rxsc_stats_attr { + MACSEC_RXSC_STATS_ATTR_UNSPEC, + MACSEC_RXSC_STATS_ATTR_IN_OCTETS_VALIDATED, + MACSEC_RXSC_STATS_ATTR_IN_OCTETS_DECRYPTED, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNCHECKED, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_DELAYED, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_OK, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_INVALID, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_LATE, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_VALID, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_USING_SA, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNUSED_SA, + MACSEC_RXSC_STATS_ATTR_PAD, + __MACSEC_RXSC_STATS_ATTR_END, + NUM_MACSEC_RXSC_STATS_ATTR = __MACSEC_RXSC_STATS_ATTR_END, + MACSEC_RXSC_STATS_ATTR_MAX = __MACSEC_RXSC_STATS_ATTR_END - 1, +}; + +/* u32 per-{RX,TX}SA stats */ +enum macsec_sa_stats_attr { + MACSEC_SA_STATS_ATTR_UNSPEC, + MACSEC_SA_STATS_ATTR_IN_PKTS_OK, + MACSEC_SA_STATS_ATTR_IN_PKTS_INVALID, + MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_VALID, + MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_USING_SA, + MACSEC_SA_STATS_ATTR_IN_PKTS_UNUSED_SA, + MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED, + MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED, + __MACSEC_SA_STATS_ATTR_END, + NUM_MACSEC_SA_STATS_ATTR = __MACSEC_SA_STATS_ATTR_END, + MACSEC_SA_STATS_ATTR_MAX = __MACSEC_SA_STATS_ATTR_END - 1, +}; + +/* u64 per-TXSC stats */ +enum macsec_txsc_stats_attr { + MACSEC_TXSC_STATS_ATTR_UNSPEC, + MACSEC_TXSC_STATS_ATTR_OUT_PKTS_PROTECTED, + MACSEC_TXSC_STATS_ATTR_OUT_PKTS_ENCRYPTED, + MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_PROTECTED, + MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_ENCRYPTED, + MACSEC_TXSC_STATS_ATTR_PAD, + __MACSEC_TXSC_STATS_ATTR_END, + NUM_MACSEC_TXSC_STATS_ATTR = __MACSEC_TXSC_STATS_ATTR_END, + MACSEC_TXSC_STATS_ATTR_MAX = __MACSEC_TXSC_STATS_ATTR_END - 1, +}; + +/* u64 per-SecY stats */ +enum macsec_secy_stats_attr { + MACSEC_SECY_STATS_ATTR_UNSPEC, + MACSEC_SECY_STATS_ATTR_OUT_PKTS_UNTAGGED, + MACSEC_SECY_STATS_ATTR_IN_PKTS_UNTAGGED, + MACSEC_SECY_STATS_ATTR_OUT_PKTS_TOO_LONG, + MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_TAG, + MACSEC_SECY_STATS_ATTR_IN_PKTS_BAD_TAG, + MACSEC_SECY_STATS_ATTR_IN_PKTS_UNKNOWN_SCI, + MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_SCI, + MACSEC_SECY_STATS_ATTR_IN_PKTS_OVERRUN, + MACSEC_SECY_STATS_ATTR_PAD, + __MACSEC_SECY_STATS_ATTR_END, + NUM_MACSEC_SECY_STATS_ATTR = __MACSEC_SECY_STATS_ATTR_END, + MACSEC_SECY_STATS_ATTR_MAX = __MACSEC_SECY_STATS_ATTR_END - 1, +}; + +#endif /* _MACSEC_H */ diff --git a/libnl/include/linux-private/linux/if_packet.h b/libnl/include/linux-private/linux/if_packet.h new file mode 100644 index 0000000..9efc423 --- /dev/null +++ b/libnl/include/linux-private/linux/if_packet.h @@ -0,0 +1,319 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_IF_PACKET_H +#define __LINUX_IF_PACKET_H + +#include +#include + +struct sockaddr_pkt { + unsigned short spkt_family; + unsigned char spkt_device[14]; + __be16 spkt_protocol; +}; + +struct sockaddr_ll { + unsigned short sll_family; + __be16 sll_protocol; + int sll_ifindex; + unsigned short sll_hatype; + unsigned char sll_pkttype; + unsigned char sll_halen; + unsigned char sll_addr[8]; +}; + +/* Packet types */ + +#define PACKET_HOST 0 /* To us */ +#define PACKET_BROADCAST 1 /* To all */ +#define PACKET_MULTICAST 2 /* To group */ +#define PACKET_OTHERHOST 3 /* To someone else */ +#define PACKET_OUTGOING 4 /* Outgoing of any type */ +#define PACKET_LOOPBACK 5 /* MC/BRD frame looped back */ +#define PACKET_USER 6 /* To user space */ +#define PACKET_KERNEL 7 /* To kernel space */ +/* Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space */ +#define PACKET_FASTROUTE 6 /* Fastrouted frame */ + +/* Packet socket options */ + +#define PACKET_ADD_MEMBERSHIP 1 +#define PACKET_DROP_MEMBERSHIP 2 +#define PACKET_RECV_OUTPUT 3 +/* Value 4 is still used by obsolete turbo-packet. */ +#define PACKET_RX_RING 5 +#define PACKET_STATISTICS 6 +#define PACKET_COPY_THRESH 7 +#define PACKET_AUXDATA 8 +#define PACKET_ORIGDEV 9 +#define PACKET_VERSION 10 +#define PACKET_HDRLEN 11 +#define PACKET_RESERVE 12 +#define PACKET_TX_RING 13 +#define PACKET_LOSS 14 +#define PACKET_VNET_HDR 15 +#define PACKET_TX_TIMESTAMP 16 +#define PACKET_TIMESTAMP 17 +#define PACKET_FANOUT 18 +#define PACKET_TX_HAS_OFF 19 +#define PACKET_QDISC_BYPASS 20 +#define PACKET_ROLLOVER_STATS 21 +#define PACKET_FANOUT_DATA 22 +#define PACKET_IGNORE_OUTGOING 23 +#define PACKET_VNET_HDR_SZ 24 + +#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_IGNORE_OUTGOING 0x4000 +#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) +#define TP_STATUS_GSO_TCP (1 << 8) + +/* 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]; +}; + +struct fanout_args { +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u16 id; + __u16 type_flags; +#else + __u16 type_flags; + __u16 id; +#endif + __u32 max_num_members; +}; + +#define PACKET_MR_MULTICAST 0 +#define PACKET_MR_PROMISC 1 +#define PACKET_MR_ALLMULTI 2 +#define PACKET_MR_UNICAST 3 + +#endif diff --git a/libnl/include/linux-private/linux/if_tunnel.h b/libnl/include/linux-private/linux/if_tunnel.h new file mode 100644 index 0000000..edaea41 --- /dev/null +++ b/libnl/include/linux-private/linux/if_tunnel.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _IF_TUNNEL_H_ +#define _IF_TUNNEL_H_ + +#include +#include +#include +#include +#include + + +#define SIOCGETTUNNEL (SIOCDEVPRIVATE + 0) +#define SIOCADDTUNNEL (SIOCDEVPRIVATE + 1) +#define SIOCDELTUNNEL (SIOCDEVPRIVATE + 2) +#define SIOCCHGTUNNEL (SIOCDEVPRIVATE + 3) +#define SIOCGETPRL (SIOCDEVPRIVATE + 4) +#define SIOCADDPRL (SIOCDEVPRIVATE + 5) +#define SIOCDELPRL (SIOCDEVPRIVATE + 6) +#define SIOCCHGPRL (SIOCDEVPRIVATE + 7) +#define SIOCGET6RD (SIOCDEVPRIVATE + 8) +#define SIOCADD6RD (SIOCDEVPRIVATE + 9) +#define SIOCDEL6RD (SIOCDEVPRIVATE + 10) +#define SIOCCHG6RD (SIOCDEVPRIVATE + 11) + +#define GRE_CSUM __cpu_to_be16(0x8000) +#define GRE_ROUTING __cpu_to_be16(0x4000) +#define GRE_KEY __cpu_to_be16(0x2000) +#define GRE_SEQ __cpu_to_be16(0x1000) +#define GRE_STRICT __cpu_to_be16(0x0800) +#define GRE_REC __cpu_to_be16(0x0700) +#define GRE_ACK __cpu_to_be16(0x0080) +#define GRE_FLAGS __cpu_to_be16(0x0078) +#define GRE_VERSION __cpu_to_be16(0x0007) + +#define GRE_IS_CSUM(f) ((f) & GRE_CSUM) +#define GRE_IS_ROUTING(f) ((f) & GRE_ROUTING) +#define GRE_IS_KEY(f) ((f) & GRE_KEY) +#define GRE_IS_SEQ(f) ((f) & GRE_SEQ) +#define GRE_IS_STRICT(f) ((f) & GRE_STRICT) +#define GRE_IS_REC(f) ((f) & GRE_REC) +#define GRE_IS_ACK(f) ((f) & GRE_ACK) + +#define GRE_VERSION_0 __cpu_to_be16(0x0000) +#define GRE_VERSION_1 __cpu_to_be16(0x0001) +#define GRE_PROTO_PPP __cpu_to_be16(0x880b) +#define GRE_PPTP_KEY_MASK __cpu_to_be32(0xffff) + +struct ip_tunnel_parm { + char name[IFNAMSIZ]; + int link; + __be16 i_flags; + __be16 o_flags; + __be32 i_key; + __be32 o_key; + struct iphdr iph; +}; + +enum { + IFLA_IPTUN_UNSPEC, + IFLA_IPTUN_LINK, + IFLA_IPTUN_LOCAL, + IFLA_IPTUN_REMOTE, + IFLA_IPTUN_TTL, + IFLA_IPTUN_TOS, + IFLA_IPTUN_ENCAP_LIMIT, + IFLA_IPTUN_FLOWINFO, + IFLA_IPTUN_FLAGS, + IFLA_IPTUN_PROTO, + IFLA_IPTUN_PMTUDISC, + IFLA_IPTUN_6RD_PREFIX, + IFLA_IPTUN_6RD_RELAY_PREFIX, + IFLA_IPTUN_6RD_PREFIXLEN, + IFLA_IPTUN_6RD_RELAY_PREFIXLEN, + IFLA_IPTUN_ENCAP_TYPE, + IFLA_IPTUN_ENCAP_FLAGS, + IFLA_IPTUN_ENCAP_SPORT, + IFLA_IPTUN_ENCAP_DPORT, + IFLA_IPTUN_COLLECT_METADATA, + IFLA_IPTUN_FWMARK, + __IFLA_IPTUN_MAX, +}; +#define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) + +enum tunnel_encap_types { + TUNNEL_ENCAP_NONE, + TUNNEL_ENCAP_FOU, + TUNNEL_ENCAP_GUE, + TUNNEL_ENCAP_MPLS, +}; + +#define TUNNEL_ENCAP_FLAG_CSUM (1<<0) +#define TUNNEL_ENCAP_FLAG_CSUM6 (1<<1) +#define TUNNEL_ENCAP_FLAG_REMCSUM (1<<2) + +/* SIT-mode i_flags */ +#define SIT_ISATAP 0x0001 + +struct ip_tunnel_prl { + __be32 addr; + __u16 flags; + __u16 __reserved; + __u32 datalen; + __u32 __reserved2; + /* data follows */ +}; + +/* PRL flags */ +#define PRL_DEFAULT 0x0001 + +struct ip_tunnel_6rd { + struct in6_addr prefix; + __be32 relay_prefix; + __u16 prefixlen; + __u16 relay_prefixlen; +}; + +enum { + IFLA_GRE_UNSPEC, + IFLA_GRE_LINK, + IFLA_GRE_IFLAGS, + IFLA_GRE_OFLAGS, + IFLA_GRE_IKEY, + IFLA_GRE_OKEY, + IFLA_GRE_LOCAL, + IFLA_GRE_REMOTE, + IFLA_GRE_TTL, + IFLA_GRE_TOS, + IFLA_GRE_PMTUDISC, + IFLA_GRE_ENCAP_LIMIT, + IFLA_GRE_FLOWINFO, + IFLA_GRE_FLAGS, + IFLA_GRE_ENCAP_TYPE, + IFLA_GRE_ENCAP_FLAGS, + IFLA_GRE_ENCAP_SPORT, + IFLA_GRE_ENCAP_DPORT, + IFLA_GRE_COLLECT_METADATA, + IFLA_GRE_IGNORE_DF, + IFLA_GRE_FWMARK, + IFLA_GRE_ERSPAN_INDEX, + IFLA_GRE_ERSPAN_VER, + IFLA_GRE_ERSPAN_DIR, + IFLA_GRE_ERSPAN_HWID, + __IFLA_GRE_MAX, +}; + +#define IFLA_GRE_MAX (__IFLA_GRE_MAX - 1) + +/* VTI-mode i_flags */ +#define VTI_ISVTI ((__be16)0x0001) + +enum { + IFLA_VTI_UNSPEC, + IFLA_VTI_LINK, + IFLA_VTI_IKEY, + IFLA_VTI_OKEY, + IFLA_VTI_LOCAL, + IFLA_VTI_REMOTE, + IFLA_VTI_FWMARK, + __IFLA_VTI_MAX, +}; + +#define IFLA_VTI_MAX (__IFLA_VTI_MAX - 1) + +#define TUNNEL_CSUM __cpu_to_be16(0x01) +#define TUNNEL_ROUTING __cpu_to_be16(0x02) +#define TUNNEL_KEY __cpu_to_be16(0x04) +#define TUNNEL_SEQ __cpu_to_be16(0x08) +#define TUNNEL_STRICT __cpu_to_be16(0x10) +#define TUNNEL_REC __cpu_to_be16(0x20) +#define TUNNEL_VERSION __cpu_to_be16(0x40) +#define TUNNEL_NO_KEY __cpu_to_be16(0x80) +#define TUNNEL_DONT_FRAGMENT __cpu_to_be16(0x0100) +#define TUNNEL_OAM __cpu_to_be16(0x0200) +#define TUNNEL_CRIT_OPT __cpu_to_be16(0x0400) +#define TUNNEL_GENEVE_OPT __cpu_to_be16(0x0800) +#define TUNNEL_VXLAN_OPT __cpu_to_be16(0x1000) +#define TUNNEL_NOCACHE __cpu_to_be16(0x2000) +#define TUNNEL_ERSPAN_OPT __cpu_to_be16(0x4000) +#define TUNNEL_GTP_OPT __cpu_to_be16(0x8000) + +#define TUNNEL_OPTIONS_PRESENT \ + (TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT | TUNNEL_ERSPAN_OPT | \ + TUNNEL_GTP_OPT) + +#endif /* _IF_TUNNEL_H_ */ diff --git a/libnl/include/linux-private/linux/if_vlan.h b/libnl/include/linux-private/linux/if_vlan.h new file mode 100644 index 0000000..04bca79 --- /dev/null +++ b/libnl/include/linux-private/linux/if_vlan.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * VLAN An implementation of 802.1Q VLAN tagging. + * + * Authors: Ben Greear + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#ifndef _LINUX_IF_VLAN_H_ +#define _LINUX_IF_VLAN_H_ + + +/* VLAN IOCTLs are found in sockios.h */ + +/* Passed in vlan_ioctl_args structure to determine behaviour. */ +enum vlan_ioctl_cmds { + ADD_VLAN_CMD, + DEL_VLAN_CMD, + SET_VLAN_INGRESS_PRIORITY_CMD, + SET_VLAN_EGRESS_PRIORITY_CMD, + GET_VLAN_INGRESS_PRIORITY_CMD, + GET_VLAN_EGRESS_PRIORITY_CMD, + SET_VLAN_NAME_TYPE_CMD, + SET_VLAN_FLAG_CMD, + GET_VLAN_REALDEV_NAME_CMD, /* If this works, you know it's a VLAN device, btw */ + GET_VLAN_VID_CMD /* Get the VID of this VLAN (specified by name) */ +}; + +enum vlan_flags { + VLAN_FLAG_REORDER_HDR = 0x1, + VLAN_FLAG_GVRP = 0x2, + VLAN_FLAG_LOOSE_BINDING = 0x4, + VLAN_FLAG_MVRP = 0x8, + VLAN_FLAG_BRIDGE_BINDING = 0x10, +}; + +enum vlan_name_types { + VLAN_NAME_TYPE_PLUS_VID, /* Name will look like: vlan0005 */ + VLAN_NAME_TYPE_RAW_PLUS_VID, /* name will look like: eth1.0005 */ + VLAN_NAME_TYPE_PLUS_VID_NO_PAD, /* Name will look like: vlan5 */ + VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, /* Name will look like: eth0.5 */ + VLAN_NAME_TYPE_HIGHEST +}; + +struct vlan_ioctl_args { + int cmd; /* Should be one of the vlan_ioctl_cmds enum above. */ + char device1[24]; + + union { + char device2[24]; + int VID; + unsigned int skb_priority; + unsigned int name_type; + unsigned int bind_type; + unsigned int flag; /* Matches vlan_dev_priv flags */ + } u; + + short vlan_qos; +}; + +#endif /* _LINUX_IF_VLAN_H_ */ diff --git a/libnl/include/linux-private/linux/in.h b/libnl/include/linux-private/linux/in.h new file mode 100644 index 0000000..7bdd684 --- /dev/null +++ b/libnl/include/linux-private/linux/in.h @@ -0,0 +1,333 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Definitions of the Internet Protocol. + * + * Version: @(#)in.h 1.0.1 04/21/93 + * + * Authors: Original taken from the GNU Project file. + * Fred N. van Kempen, + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _LINUX_IN_H +#define _LINUX_IN_H + +#include +#include +#include +#include + +#if __UAPI_DEF_IN_IPPROTO +/* Standard well-defined IP protocols. */ +enum { + IPPROTO_IP = 0, /* Dummy protocol for TCP */ +#define IPPROTO_IP IPPROTO_IP + IPPROTO_ICMP = 1, /* Internet Control Message Protocol */ +#define IPPROTO_ICMP IPPROTO_ICMP + IPPROTO_IGMP = 2, /* Internet Group Management Protocol */ +#define IPPROTO_IGMP IPPROTO_IGMP + IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94) */ +#define IPPROTO_IPIP IPPROTO_IPIP + IPPROTO_TCP = 6, /* Transmission Control Protocol */ +#define IPPROTO_TCP IPPROTO_TCP + IPPROTO_EGP = 8, /* Exterior Gateway Protocol */ +#define IPPROTO_EGP IPPROTO_EGP + IPPROTO_PUP = 12, /* PUP protocol */ +#define IPPROTO_PUP IPPROTO_PUP + IPPROTO_UDP = 17, /* User Datagram Protocol */ +#define IPPROTO_UDP IPPROTO_UDP + IPPROTO_IDP = 22, /* XNS IDP protocol */ +#define IPPROTO_IDP IPPROTO_IDP + IPPROTO_TP = 29, /* SO Transport Protocol Class 4 */ +#define IPPROTO_TP IPPROTO_TP + IPPROTO_DCCP = 33, /* Datagram Congestion Control Protocol */ +#define IPPROTO_DCCP IPPROTO_DCCP + IPPROTO_IPV6 = 41, /* IPv6-in-IPv4 tunnelling */ +#define IPPROTO_IPV6 IPPROTO_IPV6 + IPPROTO_RSVP = 46, /* RSVP Protocol */ +#define IPPROTO_RSVP IPPROTO_RSVP + IPPROTO_GRE = 47, /* Cisco GRE tunnels (rfc 1701,1702) */ +#define IPPROTO_GRE IPPROTO_GRE + IPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */ +#define IPPROTO_ESP IPPROTO_ESP + IPPROTO_AH = 51, /* Authentication Header protocol */ +#define IPPROTO_AH IPPROTO_AH + IPPROTO_MTP = 92, /* Multicast Transport Protocol */ +#define IPPROTO_MTP IPPROTO_MTP + IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */ +#define IPPROTO_BEETPH IPPROTO_BEETPH + IPPROTO_ENCAP = 98, /* Encapsulation Header */ +#define IPPROTO_ENCAP IPPROTO_ENCAP + IPPROTO_PIM = 103, /* Protocol Independent Multicast */ +#define IPPROTO_PIM IPPROTO_PIM + IPPROTO_COMP = 108, /* Compression Header Protocol */ +#define IPPROTO_COMP IPPROTO_COMP + IPPROTO_L2TP = 115, /* Layer 2 Tunnelling Protocol */ +#define IPPROTO_L2TP IPPROTO_L2TP + IPPROTO_SCTP = 132, /* Stream Control Transport Protocol */ +#define IPPROTO_SCTP IPPROTO_SCTP + IPPROTO_UDPLITE = 136, /* UDP-Lite (RFC 3828) */ +#define IPPROTO_UDPLITE IPPROTO_UDPLITE + IPPROTO_MPLS = 137, /* MPLS in IP (RFC 4023) */ +#define IPPROTO_MPLS IPPROTO_MPLS + IPPROTO_ETHERNET = 143, /* Ethernet-within-IPv6 Encapsulation */ +#define IPPROTO_ETHERNET IPPROTO_ETHERNET + IPPROTO_RAW = 255, /* Raw IP packets */ +#define IPPROTO_RAW IPPROTO_RAW + IPPROTO_MPTCP = 262, /* Multipath TCP connection */ +#define IPPROTO_MPTCP IPPROTO_MPTCP + IPPROTO_MAX +}; +#endif + +#if __UAPI_DEF_IN_ADDR +/* Internet address. */ +struct in_addr { + __be32 s_addr; +}; +#endif + +#define IP_TOS 1 +#define IP_TTL 2 +#define IP_HDRINCL 3 +#define IP_OPTIONS 4 +#define IP_ROUTER_ALERT 5 +#define IP_RECVOPTS 6 +#define IP_RETOPTS 7 +#define IP_PKTINFO 8 +#define IP_PKTOPTIONS 9 +#define IP_MTU_DISCOVER 10 +#define IP_RECVERR 11 +#define IP_RECVTTL 12 +#define IP_RECVTOS 13 +#define IP_MTU 14 +#define IP_FREEBIND 15 +#define IP_IPSEC_POLICY 16 +#define IP_XFRM_POLICY 17 +#define IP_PASSSEC 18 +#define IP_TRANSPARENT 19 + +/* BSD compatibility */ +#define IP_RECVRETOPTS IP_RETOPTS + +/* TProxy original addresses */ +#define IP_ORIGDSTADDR 20 +#define IP_RECVORIGDSTADDR IP_ORIGDSTADDR + +#define IP_MINTTL 21 +#define IP_NODEFRAG 22 +#define IP_CHECKSUM 23 +#define IP_BIND_ADDRESS_NO_PORT 24 +#define IP_RECVFRAGSIZE 25 +#define IP_RECVERR_RFC4884 26 + +/* IP_MTU_DISCOVER values */ +#define IP_PMTUDISC_DONT 0 /* Never send DF frames */ +#define IP_PMTUDISC_WANT 1 /* Use per route hints */ +#define IP_PMTUDISC_DO 2 /* Always DF */ +#define IP_PMTUDISC_PROBE 3 /* Ignore dst pmtu */ +/* Always use interface mtu (ignores dst pmtu) but don't set DF flag. + * Also incoming ICMP frag_needed notifications will be ignored on + * this socket to prevent accepting spoofed ones. + */ +#define IP_PMTUDISC_INTERFACE 4 +/* weaker version of IP_PMTUDISC_INTERFACE, which allows packets to get + * fragmented if they exeed the interface mtu + */ +#define IP_PMTUDISC_OMIT 5 + +#define IP_MULTICAST_IF 32 +#define IP_MULTICAST_TTL 33 +#define IP_MULTICAST_LOOP 34 +#define IP_ADD_MEMBERSHIP 35 +#define IP_DROP_MEMBERSHIP 36 +#define IP_UNBLOCK_SOURCE 37 +#define IP_BLOCK_SOURCE 38 +#define IP_ADD_SOURCE_MEMBERSHIP 39 +#define IP_DROP_SOURCE_MEMBERSHIP 40 +#define IP_MSFILTER 41 +#define MCAST_JOIN_GROUP 42 +#define MCAST_BLOCK_SOURCE 43 +#define MCAST_UNBLOCK_SOURCE 44 +#define MCAST_LEAVE_GROUP 45 +#define MCAST_JOIN_SOURCE_GROUP 46 +#define MCAST_LEAVE_SOURCE_GROUP 47 +#define MCAST_MSFILTER 48 +#define IP_MULTICAST_ALL 49 +#define IP_UNICAST_IF 50 +#define IP_LOCAL_PORT_RANGE 51 +#define IP_PROTOCOL 52 + +#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; + union { + __be32 imsf_slist[1]; + __DECLARE_FLEX_ARRAY(__be32, imsf_slist_flex); + }; +}; + +#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 { + union { + struct { + __u32 gf_interface_aux; /* interface index */ + struct __kernel_sockaddr_storage gf_group_aux; /* multicast address */ + __u32 gf_fmode_aux; /* filter mode */ + __u32 gf_numsrc_aux; /* number of sources */ + struct __kernel_sockaddr_storage gf_slist[1]; /* interface index */ + }; + struct { + __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_flex[]; /* 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) + +/* Dummy address for src of ICMP replies if no real address is set (RFC7600). */ +#define INADDR_DUMMY ((unsigned long int) 0xc0000008) + +/* Network number for local host loopback. */ +#define IN_LOOPBACKNET 127 + +/* Address to loopback in software to local host. */ +#define INADDR_LOOPBACK 0x7f000001 /* 127.0.0.1 */ +#define IN_LOOPBACK(a) ((((long int) (a)) & 0xff000000) == 0x7f000000) + +/* Defines for Multicast INADDR */ +#define INADDR_UNSPEC_GROUP 0xe0000000U /* 224.0.0.0 */ +#define INADDR_ALLHOSTS_GROUP 0xe0000001U /* 224.0.0.1 */ +#define INADDR_ALLRTRS_GROUP 0xe0000002U /* 224.0.0.2 */ +#define INADDR_ALLSNOOPERS_GROUP 0xe000006aU /* 224.0.0.106 */ +#define INADDR_MAX_LOCAL_GROUP 0xe00000ffU /* 224.0.0.255 */ +#endif + +/* contains the htonl type stuff.. */ +#include + + +#endif /* _LINUX_IN_H */ diff --git a/libnl/include/linux-private/linux/in6.h b/libnl/include/linux-private/linux/in6.h new file mode 100644 index 0000000..a17363e --- /dev/null +++ b/libnl/include/linux-private/linux/in6.h @@ -0,0 +1,302 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Types and definitions for AF_INET6 + * Linux INET6 implementation + * + * Authors: + * Pedro Roque + * + * Sources: + * IPv6 Program Interfaces for BSD Systems + * + * + * Advanced Sockets API for IPv6 + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_IN6_H +#define _LINUX_IN6_H + +#include +#include + +/* + * IPv6 address structure + */ + +#if __UAPI_DEF_IN6_ADDR +struct in6_addr { + union { + __u8 u6_addr8[16]; +#if __UAPI_DEF_IN6_ADDR_ALT + __be16 u6_addr16[8]; + __be32 u6_addr32[4]; +#endif + } in6_u; +#define s6_addr in6_u.u6_addr8 +#if __UAPI_DEF_IN6_ADDR_ALT +#define s6_addr16 in6_u.u6_addr16 +#define s6_addr32 in6_u.u6_addr32 +#endif +}; +#endif /* __UAPI_DEF_IN6_ADDR */ + +#if __UAPI_DEF_SOCKADDR_IN6 +struct sockaddr_in6 { + unsigned short int sin6_family; /* AF_INET6 */ + __be16 sin6_port; /* Transport layer port # */ + __be32 sin6_flowinfo; /* IPv6 flow information */ + struct in6_addr sin6_addr; /* IPv6 address */ + __u32 sin6_scope_id; /* scope id (new in RFC2553) */ +}; +#endif /* __UAPI_DEF_SOCKADDR_IN6 */ + +#if __UAPI_DEF_IPV6_MREQ +struct ipv6_mreq { + /* IPv6 multicast address of group */ + struct in6_addr ipv6mr_multiaddr; + + /* local IPv6 address of interface */ + int ipv6mr_ifindex; +}; +#endif /* __UAPI_DEF_IVP6_MREQ */ + +#define ipv6mr_acaddr ipv6mr_multiaddr + +struct in6_flowlabel_req { + struct in6_addr flr_dst; + __be32 flr_label; + __u8 flr_action; + __u8 flr_share; + __u16 flr_flags; + __u16 flr_expires; + __u16 flr_linger; + __u32 __flr_pad; + /* Options in format of IPV6_PKTOPTIONS */ +}; + +#define IPV6_FL_A_GET 0 +#define IPV6_FL_A_PUT 1 +#define IPV6_FL_A_RENEW 2 + +#define IPV6_FL_F_CREATE 1 +#define IPV6_FL_F_EXCL 2 +#define IPV6_FL_F_REFLECT 4 +#define IPV6_FL_F_REMOTE 8 + +#define IPV6_FL_S_NONE 0 +#define IPV6_FL_S_EXCL 1 +#define IPV6_FL_S_PROCESS 2 +#define IPV6_FL_S_USER 3 +#define IPV6_FL_S_ANY 255 + + +/* + * Bitmask constant declarations to help applications select out the + * flow label and priority fields. + * + * Note that this are in host byte order while the flowinfo field of + * sockaddr_in6 is in network byte order. + */ + +#define IPV6_FLOWINFO_FLOWLABEL 0x000fffff +#define IPV6_FLOWINFO_PRIORITY 0x0ff00000 + +/* These definitions are obsolete */ +#define IPV6_PRIORITY_UNCHARACTERIZED 0x0000 +#define IPV6_PRIORITY_FILLER 0x0100 +#define IPV6_PRIORITY_UNATTENDED 0x0200 +#define IPV6_PRIORITY_RESERVED1 0x0300 +#define IPV6_PRIORITY_BULK 0x0400 +#define IPV6_PRIORITY_RESERVED2 0x0500 +#define IPV6_PRIORITY_INTERACTIVE 0x0600 +#define IPV6_PRIORITY_CONTROL 0x0700 +#define IPV6_PRIORITY_8 0x0800 +#define IPV6_PRIORITY_9 0x0900 +#define IPV6_PRIORITY_10 0x0a00 +#define IPV6_PRIORITY_11 0x0b00 +#define IPV6_PRIORITY_12 0x0c00 +#define IPV6_PRIORITY_13 0x0d00 +#define IPV6_PRIORITY_14 0x0e00 +#define IPV6_PRIORITY_15 0x0f00 + +/* + * IPV6 extension headers + */ +#if __UAPI_DEF_IPPROTO_V6 +#define IPPROTO_HOPOPTS 0 /* IPv6 hop-by-hop options */ +#define IPPROTO_ROUTING 43 /* IPv6 routing header */ +#define IPPROTO_FRAGMENT 44 /* IPv6 fragmentation header */ +#define IPPROTO_ICMPV6 58 /* ICMPv6 */ +#define IPPROTO_NONE 59 /* IPv6 no next header */ +#define IPPROTO_DSTOPTS 60 /* IPv6 destination options */ +#define IPPROTO_MH 135 /* IPv6 mobility header */ +#endif /* __UAPI_DEF_IPPROTO_V6 */ + +/* + * IPv6 TLV options. + */ +#define IPV6_TLV_PAD1 0 +#define IPV6_TLV_PADN 1 +#define IPV6_TLV_ROUTERALERT 5 +#define IPV6_TLV_CALIPSO 7 /* RFC 5570 */ +#define IPV6_TLV_IOAM 49 /* TEMPORARY IANA allocation for IOAM */ +#define IPV6_TLV_JUMBO 194 +#define IPV6_TLV_HAO 201 /* home address option */ + +/* + * IPV6 socket options + */ +#if __UAPI_DEF_IPV6_OPTIONS +#define IPV6_ADDRFORM 1 +#define IPV6_2292PKTINFO 2 +#define IPV6_2292HOPOPTS 3 +#define IPV6_2292DSTOPTS 4 +#define IPV6_2292RTHDR 5 +#define IPV6_2292PKTOPTIONS 6 +#define IPV6_CHECKSUM 7 +#define IPV6_2292HOPLIMIT 8 +#define IPV6_NEXTHOP 9 +#define IPV6_AUTHHDR 10 /* obsolete */ +#define IPV6_FLOWINFO 11 + +#define IPV6_UNICAST_HOPS 16 +#define IPV6_MULTICAST_IF 17 +#define IPV6_MULTICAST_HOPS 18 +#define IPV6_MULTICAST_LOOP 19 +#define IPV6_ADD_MEMBERSHIP 20 +#define IPV6_DROP_MEMBERSHIP 21 +#define IPV6_ROUTER_ALERT 22 +#define IPV6_MTU_DISCOVER 23 +#define IPV6_MTU 24 +#define IPV6_RECVERR 25 +#define IPV6_V6ONLY 26 +#define IPV6_JOIN_ANYCAST 27 +#define IPV6_LEAVE_ANYCAST 28 +#define IPV6_MULTICAST_ALL 29 +#define IPV6_ROUTER_ALERT_ISOLATE 30 +#define IPV6_RECVERR_RFC4884 31 + +/* IPV6_MTU_DISCOVER values */ +#define IPV6_PMTUDISC_DONT 0 +#define IPV6_PMTUDISC_WANT 1 +#define IPV6_PMTUDISC_DO 2 +#define IPV6_PMTUDISC_PROBE 3 +/* same as IPV6_PMTUDISC_PROBE, provided for symetry with IPv4 + * also see comments on IP_PMTUDISC_INTERFACE + */ +#define IPV6_PMTUDISC_INTERFACE 4 +/* weaker version of IPV6_PMTUDISC_INTERFACE, which allows packets to + * get fragmented if they exceed the interface mtu + */ +#define IPV6_PMTUDISC_OMIT 5 + +/* Flowlabel */ +#define IPV6_FLOWLABEL_MGR 32 +#define IPV6_FLOWINFO_SEND 33 + +#define IPV6_IPSEC_POLICY 34 +#define IPV6_XFRM_POLICY 35 +#define IPV6_HDRINCL 36 +#endif + +/* + * Multicast: + * Following socket options are shared between IPv4 and IPv6. + * + * MCAST_JOIN_GROUP 42 + * MCAST_BLOCK_SOURCE 43 + * MCAST_UNBLOCK_SOURCE 44 + * MCAST_LEAVE_GROUP 45 + * MCAST_JOIN_SOURCE_GROUP 46 + * MCAST_LEAVE_SOURCE_GROUP 47 + * MCAST_MSFILTER 48 + */ + +/* + * Advanced API (RFC3542) (1) + * + * Note: IPV6_RECVRTHDRDSTOPTS does not exist. see net/ipv6/datagram.c. + */ + +#define IPV6_RECVPKTINFO 49 +#define IPV6_PKTINFO 50 +#define IPV6_RECVHOPLIMIT 51 +#define IPV6_HOPLIMIT 52 +#define IPV6_RECVHOPOPTS 53 +#define IPV6_HOPOPTS 54 +#define IPV6_RTHDRDSTOPTS 55 +#define IPV6_RECVRTHDR 56 +#define IPV6_RTHDR 57 +#define IPV6_RECVDSTOPTS 58 +#define IPV6_DSTOPTS 59 +#define IPV6_RECVPATHMTU 60 +#define IPV6_PATHMTU 61 +#define IPV6_DONTFRAG 62 +#if 0 /* not yet */ +#define IPV6_USE_MIN_MTU 63 +#endif + +/* + * Netfilter (1) + * + * Following socket options are used in ip6_tables; + * see include/linux/netfilter_ipv6/ip6_tables.h. + * + * IP6T_SO_SET_REPLACE / IP6T_SO_GET_INFO 64 + * IP6T_SO_SET_ADD_COUNTERS / IP6T_SO_GET_ENTRIES 65 + */ + +/* + * Advanced API (RFC3542) (2) + */ +#define IPV6_RECVTCLASS 66 +#define IPV6_TCLASS 67 + +/* + * Netfilter (2) + * + * Following socket options are used in ip6_tables; + * see include/linux/netfilter_ipv6/ip6_tables.h. + * + * IP6T_SO_GET_REVISION_MATCH 68 + * IP6T_SO_GET_REVISION_TARGET 69 + * IP6T_SO_ORIGINAL_DST 80 + */ + +#define IPV6_AUTOFLOWLABEL 70 +/* RFC5014: Source address selection */ +#define IPV6_ADDR_PREFERENCES 72 + +#define IPV6_PREFER_SRC_TMP 0x0001 +#define IPV6_PREFER_SRC_PUBLIC 0x0002 +#define IPV6_PREFER_SRC_PUBTMP_DEFAULT 0x0100 +#define IPV6_PREFER_SRC_COA 0x0004 +#define IPV6_PREFER_SRC_HOME 0x0400 +#define IPV6_PREFER_SRC_CGA 0x0008 +#define IPV6_PREFER_SRC_NONCGA 0x0800 + +/* RFC5082: Generalized Ttl Security Mechanism */ +#define IPV6_MINHOPCOUNT 73 + +#define IPV6_ORIGDSTADDR 74 +#define IPV6_RECVORIGDSTADDR IPV6_ORIGDSTADDR +#define IPV6_TRANSPARENT 75 +#define IPV6_UNICAST_IF 76 +#define IPV6_RECVFRAGSIZE 77 +#define IPV6_FREEBIND 78 + +/* + * Multicast Routing: + * see include/uapi/linux/mroute6.h. + * + * MRT6_BASE 200 + * ... + * MRT6_MAX + */ +#endif /* _LINUX_IN6_H */ diff --git a/libnl/include/linux-private/linux/in_route.h b/libnl/include/linux-private/linux/in_route.h new file mode 100644 index 0000000..0cc2c23 --- /dev/null +++ b/libnl/include/linux-private/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/libnl/include/linux-private/linux/inet_diag.h b/libnl/include/linux-private/linux/inet_diag.h new file mode 100644 index 0000000..d81cb69 --- /dev/null +++ b/libnl/include/linux-private/linux/inet_diag.h @@ -0,0 +1,239 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _INET_DIAG_H_ +#define _INET_DIAG_H_ + +#include + +/* Just some random number */ +#define TCPDIAG_GETSOCK 18 +#define DCCPDIAG_GETSOCK 19 + +#define INET_DIAG_GETSOCK_MAX 24 + +/* Socket identity */ +struct inet_diag_sockid { + __be16 idiag_sport; + __be16 idiag_dport; + __be32 idiag_src[4]; + __be32 idiag_dst[4]; + __u32 idiag_if; + __u32 idiag_cookie[2]; +#define INET_DIAG_NOCOOKIE (~0U) +}; + +/* Request structure */ + +struct inet_diag_req { + __u8 idiag_family; /* Family of addresses. */ + __u8 idiag_src_len; + __u8 idiag_dst_len; + __u8 idiag_ext; /* Query extended information */ + + struct inet_diag_sockid id; + + __u32 idiag_states; /* States to dump */ + __u32 idiag_dbs; /* Tables to dump (NI) */ +}; + +struct inet_diag_req_v2 { + __u8 sdiag_family; + __u8 sdiag_protocol; + __u8 idiag_ext; + __u8 pad; + __u32 idiag_states; + struct inet_diag_sockid id; +}; + +/* + * SOCK_RAW sockets require the underlied protocol to be + * additionally specified so we can use @pad member for + * this, but we can't rename it because userspace programs + * still may depend on this name. Instead lets use another + * structure definition as an alias for struct + * @inet_diag_req_v2. + */ +struct inet_diag_req_raw { + __u8 sdiag_family; + __u8 sdiag_protocol; + __u8 idiag_ext; + __u8 sdiag_raw_protocol; + __u32 idiag_states; + struct inet_diag_sockid id; +}; + +enum { + INET_DIAG_REQ_NONE, + INET_DIAG_REQ_BYTECODE, + INET_DIAG_REQ_SK_BPF_STORAGES, + INET_DIAG_REQ_PROTOCOL, + __INET_DIAG_REQ_MAX, +}; + +#define INET_DIAG_REQ_MAX (__INET_DIAG_REQ_MAX - 1) + +/* Bytecode is sequence of 4 byte commands followed by variable arguments. + * All the commands identified by "code" are conditional jumps forward: + * to offset cc+"yes" or to offset cc+"no". "yes" is supposed to be + * length of the command and its arguments. + */ + +struct inet_diag_bc_op { + unsigned char code; + unsigned char yes; + unsigned short no; +}; + +enum { + INET_DIAG_BC_NOP, + INET_DIAG_BC_JMP, + INET_DIAG_BC_S_GE, + INET_DIAG_BC_S_LE, + INET_DIAG_BC_D_GE, + INET_DIAG_BC_D_LE, + INET_DIAG_BC_AUTO, + INET_DIAG_BC_S_COND, + INET_DIAG_BC_D_COND, + INET_DIAG_BC_DEV_COND, /* u32 ifindex */ + INET_DIAG_BC_MARK_COND, + INET_DIAG_BC_S_EQ, + INET_DIAG_BC_D_EQ, + INET_DIAG_BC_CGROUP_COND, /* u64 cgroup v2 ID */ +}; + +struct inet_diag_hostcond { + __u8 family; + __u8 prefix_len; + int port; + __be32 addr[]; +}; + +struct inet_diag_markcond { + __u32 mark; + __u32 mask; +}; + +/* Base info structure. It contains socket identity (addrs/ports/cookie) + * and, alas, the information shown by netstat. */ +struct inet_diag_msg { + __u8 idiag_family; + __u8 idiag_state; + __u8 idiag_timer; + __u8 idiag_retrans; + + struct inet_diag_sockid id; + + __u32 idiag_expires; + __u32 idiag_rqueue; + __u32 idiag_wqueue; + __u32 idiag_uid; + __u32 idiag_inode; +}; + +/* Extensions */ + +enum { + INET_DIAG_NONE, + INET_DIAG_MEMINFO, + INET_DIAG_INFO, + INET_DIAG_VEGASINFO, + INET_DIAG_CONG, + INET_DIAG_TOS, + INET_DIAG_TCLASS, + INET_DIAG_SKMEMINFO, + INET_DIAG_SHUTDOWN, + + /* + * Next extenstions cannot be requested in struct inet_diag_req_v2: + * its field idiag_ext has only 8 bits. + */ + + INET_DIAG_DCTCPINFO, /* request as INET_DIAG_VEGASINFO */ + INET_DIAG_PROTOCOL, /* response attribute only */ + INET_DIAG_SKV6ONLY, + INET_DIAG_LOCALS, + INET_DIAG_PEERS, + INET_DIAG_PAD, + INET_DIAG_MARK, /* only with CAP_NET_ADMIN */ + INET_DIAG_BBRINFO, /* request as INET_DIAG_VEGASINFO */ + INET_DIAG_CLASS_ID, /* request as INET_DIAG_TCLASS */ + INET_DIAG_MD5SIG, + INET_DIAG_ULP_INFO, + INET_DIAG_SK_BPF_STORAGES, + INET_DIAG_CGROUP_ID, + INET_DIAG_SOCKOPT, + __INET_DIAG_MAX, +}; + +#define INET_DIAG_MAX (__INET_DIAG_MAX - 1) + +enum { + INET_ULP_INFO_UNSPEC, + INET_ULP_INFO_NAME, + INET_ULP_INFO_TLS, + INET_ULP_INFO_MPTCP, + __INET_ULP_INFO_MAX, +}; +#define INET_ULP_INFO_MAX (__INET_ULP_INFO_MAX - 1) + +/* INET_DIAG_MEM */ + +struct inet_diag_meminfo { + __u32 idiag_rmem; + __u32 idiag_wmem; + __u32 idiag_fmem; + __u32 idiag_tmem; +}; + +/* INET_DIAG_SOCKOPT */ + +struct inet_diag_sockopt { + __u8 recverr:1, + is_icsk:1, + freebind:1, + hdrincl:1, + mc_loop:1, + transparent:1, + mc_all:1, + nodefrag:1; + __u8 bind_address_no_port:1, + recverr_rfc4884:1, + defer_connect:1, + unused:5; +}; + +/* 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/libnl/include/linux-private/linux/ioctl.h b/libnl/include/linux-private/linux/ioctl.h new file mode 100644 index 0000000..b292e80 --- /dev/null +++ b/libnl/include/linux-private/linux/ioctl.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_IOCTL_H +#define _LINUX_IOCTL_H + +#include + +#endif /* _LINUX_IOCTL_H */ + diff --git a/libnl/include/linux-private/linux/ip.h b/libnl/include/linux-private/linux/ip.h new file mode 100644 index 0000000..9b4dd89 --- /dev/null +++ b/libnl/include/linux-private/linux/ip.h @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Definitions for the IP protocol. + * + * Version: @(#)ip.h 1.0.2 04/28/93 + * + * Authors: Fred N. van Kempen, + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _LINUX_IP_H +#define _LINUX_IP_H +#include +#include +#include + +#define IPTOS_TOS_MASK 0x1E +#define IPTOS_TOS(tos) ((tos)&IPTOS_TOS_MASK) +#define IPTOS_LOWDELAY 0x10 +#define IPTOS_THROUGHPUT 0x08 +#define IPTOS_RELIABILITY 0x04 +#define IPTOS_MINCOST 0x02 + +#define IPTOS_PREC_MASK 0xE0 +#define IPTOS_PREC(tos) ((tos)&IPTOS_PREC_MASK) +#define IPTOS_PREC_NETCONTROL 0xe0 +#define IPTOS_PREC_INTERNETCONTROL 0xc0 +#define IPTOS_PREC_CRITIC_ECP 0xa0 +#define IPTOS_PREC_FLASHOVERRIDE 0x80 +#define IPTOS_PREC_FLASH 0x60 +#define IPTOS_PREC_IMMEDIATE 0x40 +#define IPTOS_PREC_PRIORITY 0x20 +#define IPTOS_PREC_ROUTINE 0x00 + + +/* IP options */ +#define IPOPT_COPY 0x80 +#define IPOPT_CLASS_MASK 0x60 +#define IPOPT_NUMBER_MASK 0x1f + +#define IPOPT_COPIED(o) ((o)&IPOPT_COPY) +#define IPOPT_CLASS(o) ((o)&IPOPT_CLASS_MASK) +#define IPOPT_NUMBER(o) ((o)&IPOPT_NUMBER_MASK) + +#define IPOPT_CONTROL 0x00 +#define IPOPT_RESERVED1 0x20 +#define IPOPT_MEASUREMENT 0x40 +#define IPOPT_RESERVED2 0x60 + +#define IPOPT_END (0 |IPOPT_CONTROL) +#define IPOPT_NOOP (1 |IPOPT_CONTROL) +#define IPOPT_SEC (2 |IPOPT_CONTROL|IPOPT_COPY) +#define IPOPT_LSRR (3 |IPOPT_CONTROL|IPOPT_COPY) +#define IPOPT_TIMESTAMP (4 |IPOPT_MEASUREMENT) +#define IPOPT_CIPSO (6 |IPOPT_CONTROL|IPOPT_COPY) +#define IPOPT_RR (7 |IPOPT_CONTROL) +#define IPOPT_SID (8 |IPOPT_CONTROL|IPOPT_COPY) +#define IPOPT_SSRR (9 |IPOPT_CONTROL|IPOPT_COPY) +#define IPOPT_RA (20|IPOPT_CONTROL|IPOPT_COPY) + +#define IPVERSION 4 +#define MAXTTL 255 +#define IPDEFTTL 64 + +#define IPOPT_OPTVAL 0 +#define IPOPT_OLEN 1 +#define IPOPT_OFFSET 2 +#define IPOPT_MINOFF 4 +#define MAX_IPOPTLEN 40 +#define IPOPT_NOP IPOPT_NOOP +#define IPOPT_EOL IPOPT_END +#define IPOPT_TS IPOPT_TIMESTAMP + +#define IPOPT_TS_TSONLY 0 /* timestamps only */ +#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */ +#define IPOPT_TS_PRESPEC 3 /* specified modules only */ + +#define IPV4_BEET_PHMAXLEN 8 + +struct iphdr { +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 ihl:4, + version:4; +#elif defined (__BIG_ENDIAN_BITFIELD) + __u8 version:4, + ihl:4; +#else +#error "Please fix " +#endif + __u8 tos; + __be16 tot_len; + __be16 id; + __be16 frag_off; + __u8 ttl; + __u8 protocol; + __sum16 check; + __struct_group(/* no tag */, addrs, /* no attrs */, + __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[]; /* Variable len but >=4. Mind the 64 bit alignment! */ +}; + +struct ip_esp_hdr { + __be32 spi; + __be32 seq_no; /* Sequence number */ + __u8 enc_data[]; /* 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_ARP_EVICT_NOCARRIER, + __IPV4_DEVCONF_MAX +}; + +#define IPV4_DEVCONF_MAX (__IPV4_DEVCONF_MAX - 1) + +#endif /* _LINUX_IP_H */ diff --git a/libnl/include/linux-private/linux/ipv6.h b/libnl/include/linux-private/linux/ipv6.h new file mode 100644 index 0000000..fcf05a6 --- /dev/null +++ b/libnl/include/linux-private/linux/ipv6.h @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _IPV6_H +#define _IPV6_H + +#include +#include +#include +#include +#include + +/* The latest drafts declared increase in minimal mtu up to 1280. */ + +#define IPV6_MIN_MTU 1280 + +/* + * Advanced API + * source interface/address selection, source routing, etc... + * *under construction* + */ + +#if __UAPI_DEF_IN6_PKTINFO +struct in6_pktinfo { + struct in6_addr ipi6_addr; + int ipi6_ifindex; +}; +#endif + +#if __UAPI_DEF_IP6_MTUINFO +struct ip6_mtuinfo { + struct sockaddr_in6 ip6m_addr; + __u32 ip6m_mtu; +}; +#endif + +struct in6_ifreq { + struct in6_addr ifr6_addr; + __u32 ifr6_prefixlen; + int ifr6_ifindex; +}; + +#define IPV6_SRCRT_STRICT 0x01 /* Deprecated; will be removed */ +#define IPV6_SRCRT_TYPE_0 0 /* Deprecated; will be removed */ +#define IPV6_SRCRT_TYPE_2 2 /* IPv6 type 2 Routing Header */ +#define IPV6_SRCRT_TYPE_3 3 /* RPL Segment Routing with IPv6 */ +#define IPV6_SRCRT_TYPE_4 4 /* Segment Routing with IPv6 */ + +/* + * routing header + */ +struct ipv6_rt_hdr { + __u8 nexthdr; + __u8 hdrlen; + __u8 type; + __u8 segments_left; + + /* + * type specific data + * variable length field + */ +}; + + +struct ipv6_opt_hdr { + __u8 nexthdr; + __u8 hdrlen; + /* + * TLV encoded option data follows. + */ +} __attribute__((packed)); /* required for some archs */ + +#define ipv6_destopt_hdr ipv6_opt_hdr +#define ipv6_hopopt_hdr ipv6_opt_hdr + +/* Router Alert option values (RFC2711) */ +#define IPV6_OPT_ROUTERALERT_MLD 0x0000 /* MLD(RFC2710) */ + +/* + * routing header type 0 (used in cmsghdr struct) + */ + +struct rt0_hdr { + struct ipv6_rt_hdr rt_hdr; + __u32 reserved; + struct in6_addr addr[]; + +#define rt0_type rt_hdr.type +}; + +/* + * routing header type 2 + */ + +struct rt2_hdr { + struct ipv6_rt_hdr rt_hdr; + __u32 reserved; + struct in6_addr addr; + +#define rt2_type rt_hdr.type +}; + +/* + * home address option in destination options header + */ + +struct ipv6_destopt_hao { + __u8 type; + __u8 length; + struct in6_addr addr; +} __attribute__((packed)); + +/* + * IPv6 fixed header + * + * BEWARE, it is incorrect. The first 4 bits of flow_lbl + * are glued to priority now, forming "class". + */ + +struct ipv6hdr { +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 priority:4, + version:4; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u8 version:4, + priority:4; +#else +#error "Please fix " +#endif + __u8 flow_lbl[3]; + + __be16 payload_len; + __u8 nexthdr; + __u8 hop_limit; + + __struct_group(/* no tag */, addrs, /* no attrs */, + struct in6_addr saddr; + struct in6_addr daddr; + ); +}; + + +/* index values for the variables in ipv6_devconf */ +enum { + DEVCONF_FORWARDING = 0, + DEVCONF_HOPLIMIT, + DEVCONF_MTU6, + DEVCONF_ACCEPT_RA, + DEVCONF_ACCEPT_REDIRECTS, + DEVCONF_AUTOCONF, + DEVCONF_DAD_TRANSMITS, + DEVCONF_RTR_SOLICITS, + DEVCONF_RTR_SOLICIT_INTERVAL, + DEVCONF_RTR_SOLICIT_DELAY, + DEVCONF_USE_TEMPADDR, + DEVCONF_TEMP_VALID_LFT, + DEVCONF_TEMP_PREFERED_LFT, + DEVCONF_REGEN_MAX_RETRY, + DEVCONF_MAX_DESYNC_FACTOR, + DEVCONF_MAX_ADDRESSES, + DEVCONF_FORCE_MLD_VERSION, + DEVCONF_ACCEPT_RA_DEFRTR, + DEVCONF_ACCEPT_RA_PINFO, + DEVCONF_ACCEPT_RA_RTR_PREF, + DEVCONF_RTR_PROBE_INTERVAL, + DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN, + DEVCONF_PROXY_NDP, + DEVCONF_OPTIMISTIC_DAD, + DEVCONF_ACCEPT_SOURCE_ROUTE, + DEVCONF_MC_FORWARDING, + DEVCONF_DISABLE_IPV6, + DEVCONF_ACCEPT_DAD, + DEVCONF_FORCE_TLLAO, + DEVCONF_NDISC_NOTIFY, + DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL, + DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL, + DEVCONF_SUPPRESS_FRAG_NDISC, + DEVCONF_ACCEPT_RA_FROM_LOCAL, + DEVCONF_USE_OPTIMISTIC, + DEVCONF_ACCEPT_RA_MTU, + DEVCONF_STABLE_SECRET, + DEVCONF_USE_OIF_ADDRS_ONLY, + DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT, + DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN, + DEVCONF_DROP_UNICAST_IN_L2_MULTICAST, + DEVCONF_DROP_UNSOLICITED_NA, + DEVCONF_KEEP_ADDR_ON_DOWN, + DEVCONF_RTR_SOLICIT_MAX_INTERVAL, + DEVCONF_SEG6_ENABLED, + DEVCONF_SEG6_REQUIRE_HMAC, + DEVCONF_ENHANCED_DAD, + DEVCONF_ADDR_GEN_MODE, + DEVCONF_DISABLE_POLICY, + DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN, + DEVCONF_NDISC_TCLASS, + DEVCONF_RPL_SEG_ENABLED, + DEVCONF_RA_DEFRTR_METRIC, + DEVCONF_IOAM6_ENABLED, + DEVCONF_IOAM6_ID, + DEVCONF_IOAM6_ID_WIDE, + DEVCONF_NDISC_EVICT_NOCARRIER, + DEVCONF_ACCEPT_UNTRACKED_NA, + DEVCONF_MAX +}; + + +#endif /* _IPV6_H */ diff --git a/libnl/include/linux-private/linux/libc-compat.h b/libnl/include/linux-private/linux/libc-compat.h new file mode 100644 index 0000000..a159991 --- /dev/null +++ b/libnl/include/linux-private/linux/libc-compat.h @@ -0,0 +1,267 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Compatibility interface for userspace libc header coordination: + * + * Define compatibility macros that are used to control the inclusion or + * exclusion of UAPI structures and definitions in coordination with another + * userspace C library. + * + * This header is intended to solve the problem of UAPI definitions that + * conflict with userspace definitions. If a UAPI header has such conflicting + * definitions then the solution is as follows: + * + * * Synchronize the UAPI header and the libc headers so either one can be + * used and such that the ABI is preserved. If this is not possible then + * no simple compatibility interface exists (you need to write translating + * wrappers and rename things) and you can't use this interface. + * + * Then follow this process: + * + * (a) Include libc-compat.h in the UAPI header. + * e.g. #include + * This include must be as early as possible. + * + * (b) In libc-compat.h add enough code to detect that the comflicting + * userspace libc header has been included first. + * + * (c) If the userspace libc header has been included first define a set of + * guard macros of the form __UAPI_DEF_FOO and set their values to 1, else + * set their values to 0. + * + * (d) Back in the UAPI header with the conflicting definitions, guard the + * definitions with: + * #if __UAPI_DEF_FOO + * ... + * #endif + * + * This fixes the situation where the linux headers are included *after* the + * libc headers. To fix the problem with the inclusion in the other order the + * userspace libc headers must be fixed like this: + * + * * For all definitions that conflict with kernel definitions wrap those + * defines in the following: + * #if !__UAPI_DEF_FOO + * ... + * #endif + * + * This prevents the redefinition of a construct already defined by the kernel. + */ +#ifndef _LIBC_COMPAT_H +#define _LIBC_COMPAT_H + +/* We have included glibc headers... */ +#if defined(__GLIBC__) + +/* Coordinate with glibc net/if.h header. */ +#if defined(_NET_IF_H) && defined(__USE_MISC) + +/* GLIBC headers included first so don't define anything + * that would already be defined. */ + +#define __UAPI_DEF_IF_IFCONF 0 +#define __UAPI_DEF_IF_IFMAP 0 +#define __UAPI_DEF_IF_IFNAMSIZ 0 +#define __UAPI_DEF_IF_IFREQ 0 +/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */ +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 0 +/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */ +#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ + +#else /* _NET_IF_H */ + +/* Linux headers included first, and we must define everything + * we need. The expectation is that glibc will check the + * __UAPI_DEF_* defines and adjust appropriately. */ + +#define __UAPI_DEF_IF_IFCONF 1 +#define __UAPI_DEF_IF_IFMAP 1 +#define __UAPI_DEF_IF_IFNAMSIZ 1 +#define __UAPI_DEF_IF_IFREQ 1 +/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */ +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1 +/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */ +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 + +#endif /* _NET_IF_H */ + +/* Coordinate with glibc netinet/in.h header. */ +#if defined(_NETINET_IN_H) + +/* GLIBC headers included first so don't define anything + * that would already be defined. */ +#define __UAPI_DEF_IN_ADDR 0 +#define __UAPI_DEF_IN_IPPROTO 0 +#define __UAPI_DEF_IN_PKTINFO 0 +#define __UAPI_DEF_IP_MREQ 0 +#define __UAPI_DEF_SOCKADDR_IN 0 +#define __UAPI_DEF_IN_CLASS 0 + +#define __UAPI_DEF_IN6_ADDR 0 +/* The exception is the in6_addr macros which must be defined + * if the glibc code didn't define them. This guard matches + * the guard in glibc/inet/netinet/in.h which defines the + * additional in6_addr macros e.g. s6_addr16, and s6_addr32. */ +#if defined(__USE_MISC) || defined (__USE_GNU) +#define __UAPI_DEF_IN6_ADDR_ALT 0 +#else +#define __UAPI_DEF_IN6_ADDR_ALT 1 +#endif +#define __UAPI_DEF_SOCKADDR_IN6 0 +#define __UAPI_DEF_IPV6_MREQ 0 +#define __UAPI_DEF_IPPROTO_V6 0 +#define __UAPI_DEF_IPV6_OPTIONS 0 +#define __UAPI_DEF_IN6_PKTINFO 0 +#define __UAPI_DEF_IP6_MTUINFO 0 + +#else + +/* Linux headers included first, and we must define everything + * we need. The expectation is that glibc will check the + * __UAPI_DEF_* defines and adjust appropriately. */ +#define __UAPI_DEF_IN_ADDR 1 +#define __UAPI_DEF_IN_IPPROTO 1 +#define __UAPI_DEF_IN_PKTINFO 1 +#define __UAPI_DEF_IP_MREQ 1 +#define __UAPI_DEF_SOCKADDR_IN 1 +#define __UAPI_DEF_IN_CLASS 1 + +#define __UAPI_DEF_IN6_ADDR 1 +/* We unconditionally define the in6_addr macros and glibc must + * coordinate. */ +#define __UAPI_DEF_IN6_ADDR_ALT 1 +#define __UAPI_DEF_SOCKADDR_IN6 1 +#define __UAPI_DEF_IPV6_MREQ 1 +#define __UAPI_DEF_IPPROTO_V6 1 +#define __UAPI_DEF_IPV6_OPTIONS 1 +#define __UAPI_DEF_IN6_PKTINFO 1 +#define __UAPI_DEF_IP6_MTUINFO 1 + +#endif /* _NETINET_IN_H */ + +/* Coordinate with glibc netipx/ipx.h header. */ +#if defined(__NETIPX_IPX_H) + +#define __UAPI_DEF_SOCKADDR_IPX 0 +#define __UAPI_DEF_IPX_ROUTE_DEFINITION 0 +#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 0 +#define __UAPI_DEF_IPX_CONFIG_DATA 0 +#define __UAPI_DEF_IPX_ROUTE_DEF 0 + +#else /* defined(__NETIPX_IPX_H) */ + +#define __UAPI_DEF_SOCKADDR_IPX 1 +#define __UAPI_DEF_IPX_ROUTE_DEFINITION 1 +#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 1 +#define __UAPI_DEF_IPX_CONFIG_DATA 1 +#define __UAPI_DEF_IPX_ROUTE_DEF 1 + +#endif /* defined(__NETIPX_IPX_H) */ + +/* Definitions for xattr.h */ +#if defined(_SYS_XATTR_H) +#define __UAPI_DEF_XATTR 0 +#else +#define __UAPI_DEF_XATTR 1 +#endif + +/* If we did not see any headers from any supported C libraries, + * or we are being included in the kernel, then define everything + * that we need. Check for previous __UAPI_* definitions to give + * unsupported C libraries a way to opt out of any kernel definition. */ +#else /* !defined(__GLIBC__) */ + +/* Definitions for if.h */ +#ifndef __UAPI_DEF_IF_IFCONF +#define __UAPI_DEF_IF_IFCONF 1 +#endif +#ifndef __UAPI_DEF_IF_IFMAP +#define __UAPI_DEF_IF_IFMAP 1 +#endif +#ifndef __UAPI_DEF_IF_IFNAMSIZ +#define __UAPI_DEF_IF_IFNAMSIZ 1 +#endif +#ifndef __UAPI_DEF_IF_IFREQ +#define __UAPI_DEF_IF_IFREQ 1 +#endif +/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */ +#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1 +#endif +/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */ +#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 +#endif + +/* Definitions for in.h */ +#ifndef __UAPI_DEF_IN_ADDR +#define __UAPI_DEF_IN_ADDR 1 +#endif +#ifndef __UAPI_DEF_IN_IPPROTO +#define __UAPI_DEF_IN_IPPROTO 1 +#endif +#ifndef __UAPI_DEF_IN_PKTINFO +#define __UAPI_DEF_IN_PKTINFO 1 +#endif +#ifndef __UAPI_DEF_IP_MREQ +#define __UAPI_DEF_IP_MREQ 1 +#endif +#ifndef __UAPI_DEF_SOCKADDR_IN +#define __UAPI_DEF_SOCKADDR_IN 1 +#endif +#ifndef __UAPI_DEF_IN_CLASS +#define __UAPI_DEF_IN_CLASS 1 +#endif + +/* Definitions for in6.h */ +#ifndef __UAPI_DEF_IN6_ADDR +#define __UAPI_DEF_IN6_ADDR 1 +#endif +#ifndef __UAPI_DEF_IN6_ADDR_ALT +#define __UAPI_DEF_IN6_ADDR_ALT 1 +#endif +#ifndef __UAPI_DEF_SOCKADDR_IN6 +#define __UAPI_DEF_SOCKADDR_IN6 1 +#endif +#ifndef __UAPI_DEF_IPV6_MREQ +#define __UAPI_DEF_IPV6_MREQ 1 +#endif +#ifndef __UAPI_DEF_IPPROTO_V6 +#define __UAPI_DEF_IPPROTO_V6 1 +#endif +#ifndef __UAPI_DEF_IPV6_OPTIONS +#define __UAPI_DEF_IPV6_OPTIONS 1 +#endif +#ifndef __UAPI_DEF_IN6_PKTINFO +#define __UAPI_DEF_IN6_PKTINFO 1 +#endif +#ifndef __UAPI_DEF_IP6_MTUINFO +#define __UAPI_DEF_IP6_MTUINFO 1 +#endif + +/* Definitions for ipx.h */ +#ifndef __UAPI_DEF_SOCKADDR_IPX +#define __UAPI_DEF_SOCKADDR_IPX 1 +#endif +#ifndef __UAPI_DEF_IPX_ROUTE_DEFINITION +#define __UAPI_DEF_IPX_ROUTE_DEFINITION 1 +#endif +#ifndef __UAPI_DEF_IPX_INTERFACE_DEFINITION +#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 1 +#endif +#ifndef __UAPI_DEF_IPX_CONFIG_DATA +#define __UAPI_DEF_IPX_CONFIG_DATA 1 +#endif +#ifndef __UAPI_DEF_IPX_ROUTE_DEF +#define __UAPI_DEF_IPX_ROUTE_DEF 1 +#endif + +/* Definitions for xattr.h */ +#ifndef __UAPI_DEF_XATTR +#define __UAPI_DEF_XATTR 1 +#endif + +#endif /* __GLIBC__ */ + +#endif /* _LIBC_COMPAT_H */ diff --git a/libnl/include/linux-private/linux/limits.h b/libnl/include/linux-private/linux/limits.h new file mode 100644 index 0000000..c3547f0 --- /dev/null +++ b/libnl/include/linux-private/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/libnl/include/linux-private/linux/lwtunnel.h b/libnl/include/linux-private/linux/lwtunnel.h new file mode 100644 index 0000000..9d22961 --- /dev/null +++ b/libnl/include/linux-private/linux/lwtunnel.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LWTUNNEL_H_ +#define _LWTUNNEL_H_ + +#include + +enum lwtunnel_encap_types { + LWTUNNEL_ENCAP_NONE, + LWTUNNEL_ENCAP_MPLS, + LWTUNNEL_ENCAP_IP, + LWTUNNEL_ENCAP_ILA, + LWTUNNEL_ENCAP_IP6, + LWTUNNEL_ENCAP_SEG6, + LWTUNNEL_ENCAP_BPF, + LWTUNNEL_ENCAP_SEG6_LOCAL, + LWTUNNEL_ENCAP_RPL, + LWTUNNEL_ENCAP_IOAM6, + LWTUNNEL_ENCAP_XFRM, + __LWTUNNEL_ENCAP_MAX, +}; + +#define LWTUNNEL_ENCAP_MAX (__LWTUNNEL_ENCAP_MAX - 1) + +enum lwtunnel_ip_t { + LWTUNNEL_IP_UNSPEC, + LWTUNNEL_IP_ID, + LWTUNNEL_IP_DST, + LWTUNNEL_IP_SRC, + LWTUNNEL_IP_TTL, + LWTUNNEL_IP_TOS, + LWTUNNEL_IP_FLAGS, + LWTUNNEL_IP_PAD, + LWTUNNEL_IP_OPTS, + __LWTUNNEL_IP_MAX, +}; + +#define LWTUNNEL_IP_MAX (__LWTUNNEL_IP_MAX - 1) + +enum lwtunnel_ip6_t { + LWTUNNEL_IP6_UNSPEC, + LWTUNNEL_IP6_ID, + LWTUNNEL_IP6_DST, + LWTUNNEL_IP6_SRC, + LWTUNNEL_IP6_HOPLIMIT, + LWTUNNEL_IP6_TC, + LWTUNNEL_IP6_FLAGS, + LWTUNNEL_IP6_PAD, + LWTUNNEL_IP6_OPTS, + __LWTUNNEL_IP6_MAX, +}; + +#define LWTUNNEL_IP6_MAX (__LWTUNNEL_IP6_MAX - 1) + +enum { + LWTUNNEL_IP_OPTS_UNSPEC, + LWTUNNEL_IP_OPTS_GENEVE, + LWTUNNEL_IP_OPTS_VXLAN, + LWTUNNEL_IP_OPTS_ERSPAN, + __LWTUNNEL_IP_OPTS_MAX, +}; + +#define LWTUNNEL_IP_OPTS_MAX (__LWTUNNEL_IP_OPTS_MAX - 1) + +enum { + LWTUNNEL_IP_OPT_GENEVE_UNSPEC, + LWTUNNEL_IP_OPT_GENEVE_CLASS, + LWTUNNEL_IP_OPT_GENEVE_TYPE, + LWTUNNEL_IP_OPT_GENEVE_DATA, + __LWTUNNEL_IP_OPT_GENEVE_MAX, +}; + +#define LWTUNNEL_IP_OPT_GENEVE_MAX (__LWTUNNEL_IP_OPT_GENEVE_MAX - 1) + +enum { + LWTUNNEL_IP_OPT_VXLAN_UNSPEC, + LWTUNNEL_IP_OPT_VXLAN_GBP, + __LWTUNNEL_IP_OPT_VXLAN_MAX, +}; + +#define LWTUNNEL_IP_OPT_VXLAN_MAX (__LWTUNNEL_IP_OPT_VXLAN_MAX - 1) + +enum { + LWTUNNEL_IP_OPT_ERSPAN_UNSPEC, + LWTUNNEL_IP_OPT_ERSPAN_VER, + LWTUNNEL_IP_OPT_ERSPAN_INDEX, + LWTUNNEL_IP_OPT_ERSPAN_DIR, + LWTUNNEL_IP_OPT_ERSPAN_HWID, + __LWTUNNEL_IP_OPT_ERSPAN_MAX, +}; + +#define LWTUNNEL_IP_OPT_ERSPAN_MAX (__LWTUNNEL_IP_OPT_ERSPAN_MAX - 1) + +enum { + LWT_BPF_PROG_UNSPEC, + LWT_BPF_PROG_FD, + LWT_BPF_PROG_NAME, + __LWT_BPF_PROG_MAX, +}; + +#define LWT_BPF_PROG_MAX (__LWT_BPF_PROG_MAX - 1) + +enum { + LWT_BPF_UNSPEC, + LWT_BPF_IN, + LWT_BPF_OUT, + LWT_BPF_XMIT, + LWT_BPF_XMIT_HEADROOM, + __LWT_BPF_MAX, +}; + +#define LWT_BPF_MAX (__LWT_BPF_MAX - 1) + +#define LWT_BPF_MAX_HEADROOM 256 + +enum { + LWT_XFRM_UNSPEC, + LWT_XFRM_IF_ID, + LWT_XFRM_LINK, + __LWT_XFRM_MAX, +}; + +#define LWT_XFRM_MAX (__LWT_XFRM_MAX - 1) + +#endif /* _LWTUNNEL_H_ */ diff --git a/libnl/include/linux-private/linux/mpls.h b/libnl/include/linux-private/linux/mpls.h new file mode 100644 index 0000000..9effbf9 --- /dev/null +++ b/libnl/include/linux-private/linux/mpls.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _MPLS_H +#define _MPLS_H + +#include +#include + +/* Reference: RFC 5462, RFC 3032 + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Label | TC |S| TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Label: Label Value, 20 bits + * TC: Traffic Class field, 3 bits + * S: Bottom of Stack, 1 bit + * TTL: Time to Live, 8 bits + */ + +struct mpls_label { + __be32 entry; +}; + +#define MPLS_LS_LABEL_MASK 0xFFFFF000 +#define MPLS_LS_LABEL_SHIFT 12 +#define MPLS_LS_TC_MASK 0x00000E00 +#define MPLS_LS_TC_SHIFT 9 +#define MPLS_LS_S_MASK 0x00000100 +#define MPLS_LS_S_SHIFT 8 +#define MPLS_LS_TTL_MASK 0x000000FF +#define MPLS_LS_TTL_SHIFT 0 + +/* Reserved labels */ +#define MPLS_LABEL_IPV4NULL 0 /* RFC3032 */ +#define MPLS_LABEL_RTALERT 1 /* RFC3032 */ +#define MPLS_LABEL_IPV6NULL 2 /* RFC3032 */ +#define MPLS_LABEL_IMPLNULL 3 /* RFC3032 */ +#define MPLS_LABEL_ENTROPY 7 /* RFC6790 */ +#define MPLS_LABEL_GAL 13 /* RFC5586 */ +#define MPLS_LABEL_OAMALERT 14 /* RFC3429 */ +#define MPLS_LABEL_EXTENSION 15 /* RFC7274 */ + +#define MPLS_LABEL_FIRST_UNRESERVED 16 /* RFC3032 */ + +/* These are embedded into IFLA_STATS_AF_SPEC: + * [IFLA_STATS_AF_SPEC] + * -> [AF_MPLS] + * -> [MPLS_STATS_xxx] + * + * Attributes: + * [MPLS_STATS_LINK] = { + * struct mpls_link_stats + * } + */ +enum { + MPLS_STATS_UNSPEC, /* also used as 64bit pad attribute */ + MPLS_STATS_LINK, + __MPLS_STATS_MAX, +}; + +#define MPLS_STATS_MAX (__MPLS_STATS_MAX - 1) + +struct mpls_link_stats { + __u64 rx_packets; /* total packets received */ + __u64 tx_packets; /* total packets transmitted */ + __u64 rx_bytes; /* total bytes received */ + __u64 tx_bytes; /* total bytes transmitted */ + __u64 rx_errors; /* bad packets received */ + __u64 tx_errors; /* packet transmit problems */ + __u64 rx_dropped; /* packet dropped on receive */ + __u64 tx_dropped; /* packet dropped on transmit */ + __u64 rx_noroute; /* no route for packet dest */ +}; + +#endif /* _MPLS_H */ diff --git a/libnl/include/linux-private/linux/mpls_iptunnel.h b/libnl/include/linux-private/linux/mpls_iptunnel.h new file mode 100644 index 0000000..2c69b7d --- /dev/null +++ b/libnl/include/linux-private/linux/mpls_iptunnel.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * mpls tunnel api + * + * Authors: + * Roopa Prabhu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_MPLS_IPTUNNEL_H +#define _LINUX_MPLS_IPTUNNEL_H + +/* MPLS tunnel attributes + * [RTA_ENCAP] = { + * [MPLS_IPTUNNEL_DST] + * [MPLS_IPTUNNEL_TTL] + * } + */ +enum { + MPLS_IPTUNNEL_UNSPEC, + MPLS_IPTUNNEL_DST, + MPLS_IPTUNNEL_TTL, + __MPLS_IPTUNNEL_MAX, +}; +#define MPLS_IPTUNNEL_MAX (__MPLS_IPTUNNEL_MAX - 1) + +#endif /* _LINUX_MPLS_IPTUNNEL_H */ diff --git a/libnl/include/linux-private/linux/neighbour.h b/libnl/include/linux-private/linux/neighbour.h new file mode 100644 index 0000000..5e67a7e --- /dev/null +++ b/libnl/include/linux-private/linux/neighbour.h @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_NEIGHBOUR_H +#define __LINUX_NEIGHBOUR_H + +#include +#include + +struct ndmsg { + __u8 ndm_family; + __u8 ndm_pad1; + __u16 ndm_pad2; + __s32 ndm_ifindex; + __u16 ndm_state; + __u8 ndm_flags; + __u8 ndm_type; +}; + +enum { + NDA_UNSPEC, + NDA_DST, + NDA_LLADDR, + NDA_CACHEINFO, + NDA_PROBES, + NDA_VLAN, + NDA_PORT, + NDA_VNI, + NDA_IFINDEX, + NDA_MASTER, + NDA_LINK_NETNSID, + NDA_SRC_VNI, + NDA_PROTOCOL, /* Originator of entry */ + NDA_NH_ID, + NDA_FDB_EXT_ATTRS, + NDA_FLAGS_EXT, + NDA_NDM_STATE_MASK, + NDA_NDM_FLAGS_MASK, + __NDA_MAX +}; + +#define NDA_MAX (__NDA_MAX - 1) + +/* + * Neighbor Cache Entry Flags + */ + +#define NTF_USE (1 << 0) +#define NTF_SELF (1 << 1) +#define NTF_MASTER (1 << 2) +#define NTF_PROXY (1 << 3) /* == ATF_PUBL */ +#define NTF_EXT_LEARNED (1 << 4) +#define NTF_OFFLOADED (1 << 5) +#define NTF_STICKY (1 << 6) +#define NTF_ROUTER (1 << 7) +/* Extended flags under NDA_FLAGS_EXT: */ +#define NTF_EXT_MANAGED (1 << 0) +#define NTF_EXT_LOCKED (1 << 1) + +/* + * 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. This holds true + * for dynamic entries with NTF_EXT_LEARNED flag as well. However, upon carrier + * down event, NUD_PERMANENT entries are not flushed whereas NTF_EXT_LEARNED + * flagged entries explicitly are (which is also consistent with the routing + * subsystem). + * + * When NTF_EXT_LEARNED is set for a bridge fdb entry the different cache entry + * states don't make sense and thus are ignored. Such entries don't age and + * can roam. + * + * NTF_EXT_MANAGED flagged neigbor entries are managed by the kernel on behalf + * of a user space control plane, and automatically refreshed so that (if + * possible) they remain in NUD_REACHABLE state. + * + * NTF_EXT_LOCKED flagged bridge FDB entries are entries generated by the + * bridge in response to a host trying to communicate via a locked bridge port + * with MAB enabled. Their purpose is to notify user space that a host requires + * authentication. + */ + +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_INTERVAL_PROBE_TIME_MS, /* u64, msecs */ + __NDTPA_MAX +}; +#define NDTPA_MAX (__NDTPA_MAX - 1) + +struct ndtmsg { + __u8 ndtm_family; + __u8 ndtm_pad1; + __u16 ndtm_pad2; +}; + +struct ndt_config { + __u16 ndtc_key_len; + __u16 ndtc_entry_size; + __u32 ndtc_entries; + __u32 ndtc_last_flush; /* delta to now in msecs */ + __u32 ndtc_last_rand; /* delta to now in msecs */ + __u32 ndtc_hash_rnd; + __u32 ndtc_hash_mask; + __u32 ndtc_hash_chain_gc; + __u32 ndtc_proxy_qlen; +}; + +enum { + NDTA_UNSPEC, + NDTA_NAME, /* char *, unchangeable */ + NDTA_THRESH1, /* u32 */ + NDTA_THRESH2, /* u32 */ + NDTA_THRESH3, /* u32 */ + NDTA_CONFIG, /* struct ndt_config, read-only */ + NDTA_PARMS, /* nested TLV NDTPA_* */ + NDTA_STATS, /* struct ndt_stats, read-only */ + NDTA_GC_INTERVAL, /* u64, msecs */ + NDTA_PAD, + __NDTA_MAX +}; +#define NDTA_MAX (__NDTA_MAX - 1) + + /* FDB activity notification bits used in NFEA_ACTIVITY_NOTIFY: + * - FDB_NOTIFY_BIT - notify on activity/expire for any entry + * - FDB_NOTIFY_INACTIVE_BIT - mark as inactive to avoid multiple notifications + */ +enum { + FDB_NOTIFY_BIT = (1 << 0), + FDB_NOTIFY_INACTIVE_BIT = (1 << 1) +}; + +/* embedded into NDA_FDB_EXT_ATTRS: + * [NDA_FDB_EXT_ATTRS] = { + * [NFEA_ACTIVITY_NOTIFY] + * ... + * } + */ +enum { + NFEA_UNSPEC, + NFEA_ACTIVITY_NOTIFY, + NFEA_DONT_REFRESH, + __NFEA_MAX +}; +#define NFEA_MAX (__NFEA_MAX - 1) + +#endif diff --git a/libnl/include/linux-private/linux/netconf.h b/libnl/include/linux-private/linux/netconf.h new file mode 100644 index 0000000..229e885 --- /dev/null +++ b/libnl/include/linux-private/linux/netconf.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_NETCONF_H_ +#define _LINUX_NETCONF_H_ + +#include +#include + +struct netconfmsg { + __u8 ncm_family; +}; + +enum { + NETCONFA_UNSPEC, + NETCONFA_IFINDEX, + NETCONFA_FORWARDING, + NETCONFA_RP_FILTER, + NETCONFA_MC_FORWARDING, + NETCONFA_PROXY_NEIGH, + NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, + NETCONFA_INPUT, + NETCONFA_BC_FORWARDING, + __NETCONFA_MAX +}; +#define NETCONFA_MAX (__NETCONFA_MAX - 1) +#define NETCONFA_ALL -1 + +#define NETCONFA_IFINDEX_ALL -1 +#define NETCONFA_IFINDEX_DEFAULT -2 + +#endif /* _LINUX_NETCONF_H_ */ diff --git a/libnl/include/linux-private/linux/netdevice.h b/libnl/include/linux-private/linux/netdevice.h new file mode 100644 index 0000000..86d961c --- /dev/null +++ b/libnl/include/linux-private/linux/netdevice.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Definitions for the Interfaces handler. + * + * Version: @(#)dev.h 1.0.10 08/12/93 + * + * Authors: Ross Biro + * Fred N. van Kempen, + * Corey Minyard + * Donald J. Becker, + * Alan Cox, + * Bjorn Ekwall. + * Pekka Riikonen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Moved to /usr/include/linux for NET3 + */ +#ifndef _LINUX_NETDEVICE_H +#define _LINUX_NETDEVICE_H + +#include +#include +#include +#include + + +#define MAX_ADDR_LEN 32 /* Largest hardware address length */ + +/* Initial net device group. All devices belong to group 0 by default. */ +#define INIT_NETDEV_GROUP 0 + + +/* interface name assignment types (sysfs name_assign_type attribute) */ +#define NET_NAME_UNKNOWN 0 /* unknown origin (not exposed to userspace) */ +#define NET_NAME_ENUM 1 /* enumerated by kernel */ +#define NET_NAME_PREDICTABLE 2 /* predictably named by the kernel */ +#define NET_NAME_USER 3 /* provided by user-space */ +#define NET_NAME_RENAMED 4 /* renamed by user-space */ + +/* Media selection options. */ +enum { + IF_PORT_UNKNOWN = 0, + IF_PORT_10BASE2, + IF_PORT_10BASET, + IF_PORT_AUI, + IF_PORT_100BASET, + IF_PORT_100BASETX, + IF_PORT_100BASEFX +}; + +/* hardware address assignment types */ +#define NET_ADDR_PERM 0 /* address is permanent (default) */ +#define NET_ADDR_RANDOM 1 /* address is generated randomly */ +#define NET_ADDR_STOLEN 2 /* address is stolen from other device */ +#define NET_ADDR_SET 3 /* address is set using + * dev_set_mac_address() */ + +#endif /* _LINUX_NETDEVICE_H */ diff --git a/libnl/include/linux-private/linux/netfilter.h b/libnl/include/linux-private/linux/netfilter.h new file mode 100644 index 0000000..30c045b --- /dev/null +++ b/libnl/include/linux-private/linux/netfilter.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_NETFILTER_H +#define __LINUX_NETFILTER_H + +#include + +#include +#include + +/* Responses from hook functions. */ +#define NF_DROP 0 +#define NF_ACCEPT 1 +#define NF_STOLEN 2 +#define NF_QUEUE 3 +#define NF_REPEAT 4 +#define NF_STOP 5 /* Deprecated, for userspace nf_queue compatibility. */ +#define NF_MAX_VERDICT NF_STOP + +/* we overload the higher bits for encoding auxiliary data such as the queue + * number or errno values. Not nice, but better than additional function + * arguments. */ +#define NF_VERDICT_MASK 0x000000ff + +/* extra verdict flags have mask 0x0000ff00 */ +#define NF_VERDICT_FLAG_QUEUE_BYPASS 0x00008000 + +/* queue number (NF_QUEUE) or errno (NF_DROP) */ +#define NF_VERDICT_QMASK 0xffff0000 +#define NF_VERDICT_QBITS 16 + +#define NF_QUEUE_NR(x) ((((x) << 16) & NF_VERDICT_QMASK) | NF_QUEUE) + +#define NF_DROP_ERR(x) (((-x) << 16) | NF_DROP) + +/* only for userspace compatibility */ + +/* NF_VERDICT_BITS should be 8 now, but userspace might break if this changes */ +#define NF_VERDICT_BITS 16 + +enum nf_inet_hooks { + NF_INET_PRE_ROUTING, + NF_INET_LOCAL_IN, + NF_INET_FORWARD, + NF_INET_LOCAL_OUT, + NF_INET_POST_ROUTING, + NF_INET_NUMHOOKS, + NF_INET_INGRESS = NF_INET_NUMHOOKS, +}; + +enum nf_dev_hooks { + NF_NETDEV_INGRESS, + NF_NETDEV_EGRESS, + 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/libnl/include/linux-private/linux/netfilter/nf_conntrack_common.h b/libnl/include/linux-private/linux/netfilter/nf_conntrack_common.h new file mode 100644 index 0000000..3c99056 --- /dev/null +++ b/libnl/include/linux-private/linux/netfilter/nf_conntrack_common.h @@ -0,0 +1,148 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _NF_CONNTRACK_COMMON_H +#define _NF_CONNTRACK_COMMON_H +/* Connection state tracking for netfilter. This is separated from, + but required by, the NAT layer; it can also be used by an iptables + extension. */ +enum ip_conntrack_info { + /* Part of an established connection (either direction). */ + IP_CT_ESTABLISHED, + + /* Like NEW, but related to an existing connection, or ICMP error + (in either direction). */ + IP_CT_RELATED, + + /* Started a new connection to track (only + IP_CT_DIR_ORIGINAL); may be a retransmission. */ + IP_CT_NEW, + + /* >= this indicates reply direction */ + IP_CT_IS_REPLY, + + IP_CT_ESTABLISHED_REPLY = IP_CT_ESTABLISHED + IP_CT_IS_REPLY, + IP_CT_RELATED_REPLY = IP_CT_RELATED + IP_CT_IS_REPLY, + /* No NEW in reply direction. */ + + /* Number of distinct IP_CT types. */ + IP_CT_NUMBER, + + /* only for userspace compatibility */ + IP_CT_NEW_REPLY = IP_CT_NUMBER, +}; + +#define NF_CT_STATE_INVALID_BIT (1 << 0) +#define NF_CT_STATE_BIT(ctinfo) (1 << ((ctinfo) % IP_CT_IS_REPLY + 1)) +#define NF_CT_STATE_UNTRACKED_BIT (1 << 6) + +/* Bitset representing status of connection. */ +enum ip_conntrack_status { + /* It's an expected connection: bit 0 set. This bit never changed */ + IPS_EXPECTED_BIT = 0, + IPS_EXPECTED = (1 << IPS_EXPECTED_BIT), + + /* We've seen packets both ways: bit 1 set. Can be set, not unset. */ + IPS_SEEN_REPLY_BIT = 1, + IPS_SEEN_REPLY = (1 << IPS_SEEN_REPLY_BIT), + + /* Conntrack should never be early-expired. */ + IPS_ASSURED_BIT = 2, + IPS_ASSURED = (1 << IPS_ASSURED_BIT), + + /* Connection is confirmed: originating packet has left box */ + IPS_CONFIRMED_BIT = 3, + IPS_CONFIRMED = (1 << IPS_CONFIRMED_BIT), + + /* Connection needs src nat in orig dir. This bit never changed. */ + IPS_SRC_NAT_BIT = 4, + IPS_SRC_NAT = (1 << IPS_SRC_NAT_BIT), + + /* Connection needs dst nat in orig dir. This bit never changed. */ + IPS_DST_NAT_BIT = 5, + IPS_DST_NAT = (1 << IPS_DST_NAT_BIT), + + /* Both together. */ + IPS_NAT_MASK = (IPS_DST_NAT | IPS_SRC_NAT), + + /* Connection needs TCP sequence adjusted. */ + IPS_SEQ_ADJUST_BIT = 6, + IPS_SEQ_ADJUST = (1 << IPS_SEQ_ADJUST_BIT), + + /* NAT initialization bits. */ + IPS_SRC_NAT_DONE_BIT = 7, + IPS_SRC_NAT_DONE = (1 << IPS_SRC_NAT_DONE_BIT), + + IPS_DST_NAT_DONE_BIT = 8, + IPS_DST_NAT_DONE = (1 << IPS_DST_NAT_DONE_BIT), + + /* Both together */ + IPS_NAT_DONE_MASK = (IPS_DST_NAT_DONE | IPS_SRC_NAT_DONE), + + /* Connection is dying (removed from lists), can not be unset. */ + IPS_DYING_BIT = 9, + IPS_DYING = (1 << IPS_DYING_BIT), + + /* Connection has fixed timeout. */ + IPS_FIXED_TIMEOUT_BIT = 10, + IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT), + + /* Conntrack is a template */ + IPS_TEMPLATE_BIT = 11, + IPS_TEMPLATE = (1 << IPS_TEMPLATE_BIT), + + /* Conntrack is a fake untracked entry. Obsolete and not used anymore */ + IPS_UNTRACKED_BIT = 12, + IPS_UNTRACKED = (1 << IPS_UNTRACKED_BIT), + + + /* Conntrack got a helper explicitly attached (ruleset, ctnetlink). */ + IPS_HELPER_BIT = 13, + IPS_HELPER = (1 << IPS_HELPER_BIT), + + /* Conntrack has been offloaded to flow table. */ + IPS_OFFLOAD_BIT = 14, + IPS_OFFLOAD = (1 << IPS_OFFLOAD_BIT), + + /* Conntrack has been offloaded to hardware. */ + IPS_HW_OFFLOAD_BIT = 15, + IPS_HW_OFFLOAD = (1 << IPS_HW_OFFLOAD_BIT), + + /* Be careful here, modifying these bits can make things messy, + * so don't let users modify them directly. + */ + IPS_UNCHANGEABLE_MASK = (IPS_NAT_DONE_MASK | IPS_NAT_MASK | + IPS_EXPECTED | IPS_CONFIRMED | IPS_DYING | + IPS_SEQ_ADJUST | IPS_TEMPLATE | IPS_UNTRACKED | + IPS_OFFLOAD | IPS_HW_OFFLOAD), + + __IPS_MAX_BIT = 16, +}; + +/* Connection tracking event types */ +enum ip_conntrack_events { + IPCT_NEW, /* new conntrack */ + IPCT_RELATED, /* related conntrack */ + IPCT_DESTROY, /* destroyed conntrack */ + IPCT_REPLY, /* connection has seen two-way traffic */ + IPCT_ASSURED, /* connection status has changed to assured */ + IPCT_PROTOINFO, /* protocol information has changed */ + IPCT_HELPER, /* new helper has been set */ + IPCT_MARK, /* new mark has been set */ + IPCT_SEQADJ, /* sequence adjustment has changed */ + IPCT_NATSEQADJ = IPCT_SEQADJ, + IPCT_SECMARK, /* new security mark has been set */ + IPCT_LABEL, /* new connlabel has been set */ + IPCT_SYNPROXY, /* synproxy has been set */ +}; + +enum ip_conntrack_expect_events { + IPEXP_NEW, /* new expectation */ + IPEXP_DESTROY, /* destroyed expectation */ +}; + +/* expectation flags */ +#define NF_CT_EXPECT_PERMANENT 0x1 +#define NF_CT_EXPECT_INACTIVE 0x2 +#define NF_CT_EXPECT_USERSPACE 0x4 + + +#endif /* _NF_CONNTRACK_COMMON_H */ diff --git a/libnl/include/linux-private/linux/netfilter/nf_conntrack_tcp.h b/libnl/include/linux-private/linux/netfilter/nf_conntrack_tcp.h new file mode 100644 index 0000000..a589dbc --- /dev/null +++ b/libnl/include/linux-private/linux/netfilter/nf_conntrack_tcp.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _NF_CONNTRACK_TCP_H +#define _NF_CONNTRACK_TCP_H +/* TCP tracking. */ + +#include + +/* This is exposed to userspace (ctnetlink) */ +enum tcp_conntrack { + TCP_CONNTRACK_NONE, + TCP_CONNTRACK_SYN_SENT, + TCP_CONNTRACK_SYN_RECV, + TCP_CONNTRACK_ESTABLISHED, + TCP_CONNTRACK_FIN_WAIT, + TCP_CONNTRACK_CLOSE_WAIT, + TCP_CONNTRACK_LAST_ACK, + TCP_CONNTRACK_TIME_WAIT, + TCP_CONNTRACK_CLOSE, + TCP_CONNTRACK_LISTEN, /* obsolete */ +#define TCP_CONNTRACK_SYN_SENT2 TCP_CONNTRACK_LISTEN + TCP_CONNTRACK_MAX, + TCP_CONNTRACK_IGNORE, + TCP_CONNTRACK_RETRANS, + TCP_CONNTRACK_UNACK, + TCP_CONNTRACK_TIMEOUT_MAX +}; + +/* Window scaling is advertised by the sender */ +#define IP_CT_TCP_FLAG_WINDOW_SCALE 0x01 + +/* SACK is permitted by the sender */ +#define IP_CT_TCP_FLAG_SACK_PERM 0x02 + +/* This sender sent FIN first */ +#define IP_CT_TCP_FLAG_CLOSE_INIT 0x04 + +/* Be liberal in window checking */ +#define IP_CT_TCP_FLAG_BE_LIBERAL 0x08 + +/* Has unacknowledged data */ +#define IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED 0x10 + +/* The field td_maxack has been set */ +#define IP_CT_TCP_FLAG_MAXACK_SET 0x20 + +/* Marks possibility for expected RFC5961 challenge ACK */ +#define IP_CT_EXP_CHALLENGE_ACK 0x40 + +/* Simultaneous open initialized */ +#define IP_CT_TCP_SIMULTANEOUS_OPEN 0x80 + +struct nf_ct_tcp_flags { + __u8 flags; + __u8 mask; +}; + + +#endif /* _NF_CONNTRACK_TCP_H */ diff --git a/libnl/include/linux-private/linux/netfilter/nfnetlink.h b/libnl/include/linux-private/linux/netfilter/nfnetlink.h new file mode 100644 index 0000000..b89a3e6 --- /dev/null +++ b/libnl/include/linux-private/linux/netfilter/nfnetlink.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _NFNETLINK_H +#define _NFNETLINK_H +#include +#include + +enum nfnetlink_groups { + NFNLGRP_NONE, +#define NFNLGRP_NONE NFNLGRP_NONE + NFNLGRP_CONNTRACK_NEW, +#define NFNLGRP_CONNTRACK_NEW NFNLGRP_CONNTRACK_NEW + NFNLGRP_CONNTRACK_UPDATE, +#define NFNLGRP_CONNTRACK_UPDATE NFNLGRP_CONNTRACK_UPDATE + NFNLGRP_CONNTRACK_DESTROY, +#define NFNLGRP_CONNTRACK_DESTROY NFNLGRP_CONNTRACK_DESTROY + NFNLGRP_CONNTRACK_EXP_NEW, +#define NFNLGRP_CONNTRACK_EXP_NEW NFNLGRP_CONNTRACK_EXP_NEW + NFNLGRP_CONNTRACK_EXP_UPDATE, +#define NFNLGRP_CONNTRACK_EXP_UPDATE NFNLGRP_CONNTRACK_EXP_UPDATE + NFNLGRP_CONNTRACK_EXP_DESTROY, +#define NFNLGRP_CONNTRACK_EXP_DESTROY NFNLGRP_CONNTRACK_EXP_DESTROY + NFNLGRP_NFTABLES, +#define NFNLGRP_NFTABLES NFNLGRP_NFTABLES + NFNLGRP_ACCT_QUOTA, +#define NFNLGRP_ACCT_QUOTA NFNLGRP_ACCT_QUOTA + NFNLGRP_NFTRACE, +#define NFNLGRP_NFTRACE NFNLGRP_NFTRACE + __NFNLGRP_MAX, +}; +#define NFNLGRP_MAX (__NFNLGRP_MAX - 1) + +/* General form of address family dependent message. + */ +struct nfgenmsg { + __u8 nfgen_family; /* AF_xxx */ + __u8 version; /* nfnetlink version */ + __be16 res_id; /* resource id */ +}; + +#define NFNETLINK_V0 0 + +/* netfilter netlink message types are split in two pieces: + * 8 bit subsystem, 8bit operation. + */ + +#define NFNL_SUBSYS_ID(x) ((x & 0xff00) >> 8) +#define NFNL_MSG_TYPE(x) (x & 0x00ff) + +/* No enum here, otherwise __stringify() trick of MODULE_ALIAS_NFNL_SUBSYS() + * won't work anymore */ +#define NFNL_SUBSYS_NONE 0 +#define NFNL_SUBSYS_CTNETLINK 1 +#define NFNL_SUBSYS_CTNETLINK_EXP 2 +#define NFNL_SUBSYS_QUEUE 3 +#define NFNL_SUBSYS_ULOG 4 +#define NFNL_SUBSYS_OSF 5 +#define NFNL_SUBSYS_IPSET 6 +#define NFNL_SUBSYS_ACCT 7 +#define NFNL_SUBSYS_CTNETLINK_TIMEOUT 8 +#define NFNL_SUBSYS_CTHELPER 9 +#define NFNL_SUBSYS_NFTABLES 10 +#define NFNL_SUBSYS_NFT_COMPAT 11 +#define NFNL_SUBSYS_HOOK 12 +#define NFNL_SUBSYS_COUNT 13 + +/* Reserved control nfnetlink messages */ +#define NFNL_MSG_BATCH_BEGIN NLMSG_MIN_TYPE +#define NFNL_MSG_BATCH_END NLMSG_MIN_TYPE+1 + +/** + * enum nfnl_batch_attributes - nfnetlink batch netlink attributes + * + * @NFNL_BATCH_GENID: generation ID for this changeset (NLA_U32) + */ +enum nfnl_batch_attributes { + NFNL_BATCH_UNSPEC, + NFNL_BATCH_GENID, + __NFNL_BATCH_MAX +}; +#define NFNL_BATCH_MAX (__NFNL_BATCH_MAX - 1) + +#endif /* _NFNETLINK_H */ diff --git a/libnl/include/linux-private/linux/netfilter/nfnetlink_compat.h b/libnl/include/linux-private/linux/netfilter/nfnetlink_compat.h new file mode 100644 index 0000000..ead7161 --- /dev/null +++ b/libnl/include/linux-private/linux/netfilter/nfnetlink_compat.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _NFNETLINK_COMPAT_H +#define _NFNETLINK_COMPAT_H + +#include + +/* Old nfnetlink macros for userspace */ + +/* nfnetlink groups: Up to 32 maximum */ +#define NF_NETLINK_CONNTRACK_NEW 0x00000001 +#define NF_NETLINK_CONNTRACK_UPDATE 0x00000002 +#define NF_NETLINK_CONNTRACK_DESTROY 0x00000004 +#define NF_NETLINK_CONNTRACK_EXP_NEW 0x00000008 +#define NF_NETLINK_CONNTRACK_EXP_UPDATE 0x00000010 +#define NF_NETLINK_CONNTRACK_EXP_DESTROY 0x00000020 + +/* Generic structure for encapsulation optional netfilter information. + * It is reminiscent of sockaddr, but with sa_family replaced + * with attribute type. + * ! This should someday be put somewhere generic as now rtnetlink and + * ! nfnetlink use the same attributes methods. - J. Schulist. + */ + +struct nfattr { + __u16 nfa_len; + __u16 nfa_type; /* we use 15 bits for the type, and the highest + * bit to indicate whether the payload is nested */ +}; + +/* FIXME: Apart from NFNL_NFA_NESTED shamelessly copy and pasted from + * rtnetlink.h, it's time to put this in a generic file */ + +#define NFNL_NFA_NEST 0x8000 +#define NFA_TYPE(attr) ((attr)->nfa_type & 0x7fff) + +#define NFA_ALIGNTO 4 +#define NFA_ALIGN(len) (((len) + NFA_ALIGNTO - 1) & ~(NFA_ALIGNTO - 1)) +#define NFA_OK(nfa,len) ((len) > 0 && (nfa)->nfa_len >= sizeof(struct nfattr) \ + && (nfa)->nfa_len <= (len)) +#define NFA_NEXT(nfa,attrlen) ((attrlen) -= NFA_ALIGN((nfa)->nfa_len), \ + (struct nfattr *)(((char *)(nfa)) + NFA_ALIGN((nfa)->nfa_len))) +#define NFA_LENGTH(len) (NFA_ALIGN(sizeof(struct nfattr)) + (len)) +#define NFA_SPACE(len) NFA_ALIGN(NFA_LENGTH(len)) +#define NFA_DATA(nfa) ((void *)(((char *)(nfa)) + NFA_LENGTH(0))) +#define NFA_PAYLOAD(nfa) ((int)((nfa)->nfa_len) - NFA_LENGTH(0)) +#define NFA_NEST(skb, type) \ +({ struct nfattr *__start = (struct nfattr *)skb_tail_pointer(skb); \ + NFA_PUT(skb, (NFNL_NFA_NEST | type), 0, NULL); \ + __start; }) +#define NFA_NEST_END(skb, start) \ +({ (start)->nfa_len = skb_tail_pointer(skb) - (unsigned char *)(start); \ + (skb)->len; }) +#define NFA_NEST_CANCEL(skb, start) \ +({ if (start) \ + skb_trim(skb, (unsigned char *) (start) - (skb)->data); \ + -1; }) + +#define NFM_NFA(n) ((struct nfattr *)(((char *)(n)) \ + + NLMSG_ALIGN(sizeof(struct nfgenmsg)))) +#define NFM_PAYLOAD(n) NLMSG_PAYLOAD(n, sizeof(struct nfgenmsg)) + +#endif /* _NFNETLINK_COMPAT_H */ diff --git a/libnl/include/linux-private/linux/netfilter/nfnetlink_conntrack.h b/libnl/include/linux-private/linux/netfilter/nfnetlink_conntrack.h new file mode 100644 index 0000000..c2ac726 --- /dev/null +++ b/libnl/include/linux-private/linux/netfilter/nfnetlink_conntrack.h @@ -0,0 +1,291 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _IPCONNTRACK_NETLINK_H +#define _IPCONNTRACK_NETLINK_H +#include + +enum cntl_msg_types { + IPCTNL_MSG_CT_NEW, + IPCTNL_MSG_CT_GET, + IPCTNL_MSG_CT_DELETE, + IPCTNL_MSG_CT_GET_CTRZERO, + IPCTNL_MSG_CT_GET_STATS_CPU, + IPCTNL_MSG_CT_GET_STATS, + IPCTNL_MSG_CT_GET_DYING, + IPCTNL_MSG_CT_GET_UNCONFIRMED, + + IPCTNL_MSG_MAX +}; + +enum ctnl_exp_msg_types { + IPCTNL_MSG_EXP_NEW, + IPCTNL_MSG_EXP_GET, + IPCTNL_MSG_EXP_DELETE, + IPCTNL_MSG_EXP_GET_STATS_CPU, + + IPCTNL_MSG_EXP_MAX +}; + + +enum ctattr_type { + CTA_UNSPEC, + CTA_TUPLE_ORIG, + CTA_TUPLE_REPLY, + CTA_STATUS, + CTA_PROTOINFO, + CTA_HELP, + CTA_NAT_SRC, +#define CTA_NAT CTA_NAT_SRC /* backwards compatibility */ + CTA_TIMEOUT, + CTA_MARK, + CTA_COUNTERS_ORIG, + CTA_COUNTERS_REPLY, + CTA_USE, + CTA_ID, + CTA_NAT_DST, + CTA_TUPLE_MASTER, + CTA_SEQ_ADJ_ORIG, + CTA_NAT_SEQ_ADJ_ORIG = CTA_SEQ_ADJ_ORIG, + CTA_SEQ_ADJ_REPLY, + CTA_NAT_SEQ_ADJ_REPLY = CTA_SEQ_ADJ_REPLY, + CTA_SECMARK, /* obsolete */ + CTA_ZONE, + CTA_SECCTX, + CTA_TIMESTAMP, + CTA_MARK_MASK, + CTA_LABELS, + CTA_LABELS_MASK, + CTA_SYNPROXY, + CTA_FILTER, + CTA_STATUS_MASK, + __CTA_MAX +}; +#define CTA_MAX (__CTA_MAX - 1) + +enum ctattr_tuple { + CTA_TUPLE_UNSPEC, + CTA_TUPLE_IP, + CTA_TUPLE_PROTO, + CTA_TUPLE_ZONE, + __CTA_TUPLE_MAX +}; +#define CTA_TUPLE_MAX (__CTA_TUPLE_MAX - 1) + +enum ctattr_ip { + CTA_IP_UNSPEC, + CTA_IP_V4_SRC, + CTA_IP_V4_DST, + CTA_IP_V6_SRC, + CTA_IP_V6_DST, + __CTA_IP_MAX +}; +#define CTA_IP_MAX (__CTA_IP_MAX - 1) + +enum ctattr_l4proto { + CTA_PROTO_UNSPEC, + CTA_PROTO_NUM, + CTA_PROTO_SRC_PORT, + CTA_PROTO_DST_PORT, + CTA_PROTO_ICMP_ID, + CTA_PROTO_ICMP_TYPE, + CTA_PROTO_ICMP_CODE, + CTA_PROTO_ICMPV6_ID, + CTA_PROTO_ICMPV6_TYPE, + CTA_PROTO_ICMPV6_CODE, + __CTA_PROTO_MAX +}; +#define CTA_PROTO_MAX (__CTA_PROTO_MAX - 1) + +enum ctattr_protoinfo { + CTA_PROTOINFO_UNSPEC, + CTA_PROTOINFO_TCP, + CTA_PROTOINFO_DCCP, + CTA_PROTOINFO_SCTP, + __CTA_PROTOINFO_MAX +}; +#define CTA_PROTOINFO_MAX (__CTA_PROTOINFO_MAX - 1) + +enum ctattr_protoinfo_tcp { + CTA_PROTOINFO_TCP_UNSPEC, + CTA_PROTOINFO_TCP_STATE, + CTA_PROTOINFO_TCP_WSCALE_ORIGINAL, + CTA_PROTOINFO_TCP_WSCALE_REPLY, + CTA_PROTOINFO_TCP_FLAGS_ORIGINAL, + CTA_PROTOINFO_TCP_FLAGS_REPLY, + __CTA_PROTOINFO_TCP_MAX +}; +#define CTA_PROTOINFO_TCP_MAX (__CTA_PROTOINFO_TCP_MAX - 1) + +enum ctattr_protoinfo_dccp { + CTA_PROTOINFO_DCCP_UNSPEC, + CTA_PROTOINFO_DCCP_STATE, + CTA_PROTOINFO_DCCP_ROLE, + CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ, + CTA_PROTOINFO_DCCP_PAD, + __CTA_PROTOINFO_DCCP_MAX, +}; +#define CTA_PROTOINFO_DCCP_MAX (__CTA_PROTOINFO_DCCP_MAX - 1) + +enum ctattr_protoinfo_sctp { + CTA_PROTOINFO_SCTP_UNSPEC, + CTA_PROTOINFO_SCTP_STATE, + CTA_PROTOINFO_SCTP_VTAG_ORIGINAL, + CTA_PROTOINFO_SCTP_VTAG_REPLY, + __CTA_PROTOINFO_SCTP_MAX +}; +#define CTA_PROTOINFO_SCTP_MAX (__CTA_PROTOINFO_SCTP_MAX - 1) + +enum ctattr_counters { + CTA_COUNTERS_UNSPEC, + CTA_COUNTERS_PACKETS, /* 64bit counters */ + CTA_COUNTERS_BYTES, /* 64bit counters */ + CTA_COUNTERS32_PACKETS, /* old 32bit counters, unused */ + CTA_COUNTERS32_BYTES, /* old 32bit counters, unused */ + CTA_COUNTERS_PAD, + __CTA_COUNTERS_MAX +}; +#define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1) + +enum ctattr_tstamp { + CTA_TIMESTAMP_UNSPEC, + CTA_TIMESTAMP_START, + CTA_TIMESTAMP_STOP, + CTA_TIMESTAMP_PAD, + __CTA_TIMESTAMP_MAX +}; +#define CTA_TIMESTAMP_MAX (__CTA_TIMESTAMP_MAX - 1) + +enum ctattr_nat { + CTA_NAT_UNSPEC, + CTA_NAT_V4_MINIP, +#define CTA_NAT_MINIP CTA_NAT_V4_MINIP + CTA_NAT_V4_MAXIP, +#define CTA_NAT_MAXIP CTA_NAT_V4_MAXIP + CTA_NAT_PROTO, + CTA_NAT_V6_MINIP, + CTA_NAT_V6_MAXIP, + __CTA_NAT_MAX +}; +#define CTA_NAT_MAX (__CTA_NAT_MAX - 1) + +enum ctattr_protonat { + CTA_PROTONAT_UNSPEC, + CTA_PROTONAT_PORT_MIN, + CTA_PROTONAT_PORT_MAX, + __CTA_PROTONAT_MAX +}; +#define CTA_PROTONAT_MAX (__CTA_PROTONAT_MAX - 1) + +enum ctattr_seqadj { + CTA_SEQADJ_UNSPEC, + CTA_SEQADJ_CORRECTION_POS, + CTA_SEQADJ_OFFSET_BEFORE, + CTA_SEQADJ_OFFSET_AFTER, + __CTA_SEQADJ_MAX +}; +#define CTA_SEQADJ_MAX (__CTA_SEQADJ_MAX - 1) + +enum ctattr_natseq { + CTA_NAT_SEQ_UNSPEC, + CTA_NAT_SEQ_CORRECTION_POS, + CTA_NAT_SEQ_OFFSET_BEFORE, + CTA_NAT_SEQ_OFFSET_AFTER, + __CTA_NAT_SEQ_MAX +}; +#define CTA_NAT_SEQ_MAX (__CTA_NAT_SEQ_MAX - 1) + +enum ctattr_synproxy { + CTA_SYNPROXY_UNSPEC, + CTA_SYNPROXY_ISN, + CTA_SYNPROXY_ITS, + CTA_SYNPROXY_TSOFF, + __CTA_SYNPROXY_MAX, +}; +#define CTA_SYNPROXY_MAX (__CTA_SYNPROXY_MAX - 1) + +enum ctattr_expect { + CTA_EXPECT_UNSPEC, + CTA_EXPECT_MASTER, + CTA_EXPECT_TUPLE, + CTA_EXPECT_MASK, + CTA_EXPECT_TIMEOUT, + CTA_EXPECT_ID, + CTA_EXPECT_HELP_NAME, + CTA_EXPECT_ZONE, + CTA_EXPECT_FLAGS, + CTA_EXPECT_CLASS, + CTA_EXPECT_NAT, + CTA_EXPECT_FN, + __CTA_EXPECT_MAX +}; +#define CTA_EXPECT_MAX (__CTA_EXPECT_MAX - 1) + +enum ctattr_expect_nat { + CTA_EXPECT_NAT_UNSPEC, + CTA_EXPECT_NAT_DIR, + CTA_EXPECT_NAT_TUPLE, + __CTA_EXPECT_NAT_MAX +}; +#define CTA_EXPECT_NAT_MAX (__CTA_EXPECT_NAT_MAX - 1) + +enum ctattr_help { + CTA_HELP_UNSPEC, + CTA_HELP_NAME, + CTA_HELP_INFO, + __CTA_HELP_MAX +}; +#define CTA_HELP_MAX (__CTA_HELP_MAX - 1) + +enum ctattr_secctx { + CTA_SECCTX_UNSPEC, + CTA_SECCTX_NAME, + __CTA_SECCTX_MAX +}; +#define CTA_SECCTX_MAX (__CTA_SECCTX_MAX - 1) + +enum ctattr_stats_cpu { + CTA_STATS_UNSPEC, + CTA_STATS_SEARCHED, /* no longer used */ + CTA_STATS_FOUND, + CTA_STATS_NEW, /* no longer used */ + CTA_STATS_INVALID, + CTA_STATS_IGNORE, /* no longer used */ + CTA_STATS_DELETE, /* no longer used */ + CTA_STATS_DELETE_LIST, /* no longer used */ + CTA_STATS_INSERT, + CTA_STATS_INSERT_FAILED, + CTA_STATS_DROP, + CTA_STATS_EARLY_DROP, + CTA_STATS_ERROR, + CTA_STATS_SEARCH_RESTART, + CTA_STATS_CLASH_RESOLVE, + CTA_STATS_CHAIN_TOOLONG, + __CTA_STATS_MAX, +}; +#define CTA_STATS_MAX (__CTA_STATS_MAX - 1) + +enum ctattr_stats_global { + CTA_STATS_GLOBAL_UNSPEC, + CTA_STATS_GLOBAL_ENTRIES, + CTA_STATS_GLOBAL_MAX_ENTRIES, + __CTA_STATS_GLOBAL_MAX, +}; +#define CTA_STATS_GLOBAL_MAX (__CTA_STATS_GLOBAL_MAX - 1) + +enum ctattr_expect_stats { + CTA_STATS_EXP_UNSPEC, + CTA_STATS_EXP_NEW, + CTA_STATS_EXP_CREATE, + CTA_STATS_EXP_DELETE, + __CTA_STATS_EXP_MAX, +}; +#define CTA_STATS_EXP_MAX (__CTA_STATS_EXP_MAX - 1) + +enum ctattr_filter { + CTA_FILTER_UNSPEC, + CTA_FILTER_ORIG_FLAGS, + CTA_FILTER_REPLY_FLAGS, + __CTA_FILTER_MAX +}; +#define CTA_FILTER_MAX (__CTA_FILTER_MAX - 1) + +#endif /* _IPCONNTRACK_NETLINK_H */ diff --git a/libnl/include/linux-private/linux/netfilter/nfnetlink_log.h b/libnl/include/linux-private/linux/netfilter/nfnetlink_log.h new file mode 100644 index 0000000..0af9c11 --- /dev/null +++ b/libnl/include/linux-private/linux/netfilter/nfnetlink_log.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _NFNETLINK_LOG_H +#define _NFNETLINK_LOG_H + +/* This file describes the netlink messages (i.e. 'protocol packets'), + * and not any kind of function definitions. It is shared between kernel and + * userspace. Don't put kernel specific stuff in here */ + +#include +#include + +enum nfulnl_msg_types { + NFULNL_MSG_PACKET, /* packet from kernel to userspace */ + NFULNL_MSG_CONFIG, /* connect to a particular queue */ + + NFULNL_MSG_MAX +}; + +struct nfulnl_msg_packet_hdr { + __be16 hw_protocol; /* hw protocol (network order) */ + __u8 hook; /* netfilter hook */ + __u8 _pad; +}; + +struct nfulnl_msg_packet_hw { + __be16 hw_addrlen; + __u16 _pad; + __u8 hw_addr[8]; +}; + +struct nfulnl_msg_packet_timestamp { + __aligned_be64 sec; + __aligned_be64 usec; +}; + +enum nfulnl_vlan_attr { + NFULA_VLAN_UNSPEC, + NFULA_VLAN_PROTO, /* __be16 skb vlan_proto */ + NFULA_VLAN_TCI, /* __be16 skb htons(vlan_tci) */ + __NFULA_VLAN_MAX, +}; + +#define NFULA_VLAN_MAX (__NFULA_VLAN_MAX + 1) + +enum nfulnl_attr_type { + NFULA_UNSPEC, + NFULA_PACKET_HDR, + NFULA_MARK, /* __u32 nfmark */ + NFULA_TIMESTAMP, /* nfulnl_msg_packet_timestamp */ + NFULA_IFINDEX_INDEV, /* __u32 ifindex */ + NFULA_IFINDEX_OUTDEV, /* __u32 ifindex */ + NFULA_IFINDEX_PHYSINDEV, /* __u32 ifindex */ + NFULA_IFINDEX_PHYSOUTDEV, /* __u32 ifindex */ + NFULA_HWADDR, /* nfulnl_msg_packet_hw */ + NFULA_PAYLOAD, /* opaque data payload */ + NFULA_PREFIX, /* string prefix */ + NFULA_UID, /* user id of socket */ + NFULA_SEQ, /* instance-local sequence number */ + NFULA_SEQ_GLOBAL, /* global sequence number */ + NFULA_GID, /* group id of socket */ + NFULA_HWTYPE, /* hardware type */ + NFULA_HWHEADER, /* hardware header */ + NFULA_HWLEN, /* hardware header length */ + NFULA_CT, /* nfnetlink_conntrack.h */ + NFULA_CT_INFO, /* enum ip_conntrack_info */ + NFULA_VLAN, /* nested attribute: packet vlan info */ + NFULA_L2HDR, /* full L2 header */ + + __NFULA_MAX +}; +#define NFULA_MAX (__NFULA_MAX - 1) + +enum nfulnl_msg_config_cmds { + NFULNL_CFG_CMD_NONE, + NFULNL_CFG_CMD_BIND, + NFULNL_CFG_CMD_UNBIND, + NFULNL_CFG_CMD_PF_BIND, + NFULNL_CFG_CMD_PF_UNBIND, +}; + +struct nfulnl_msg_config_cmd { + __u8 command; /* nfulnl_msg_config_cmds */ +} __attribute__ ((packed)); + +struct nfulnl_msg_config_mode { + __be32 copy_range; + __u8 copy_mode; + __u8 _pad; +} __attribute__ ((packed)); + +enum nfulnl_attr_config { + NFULA_CFG_UNSPEC, + NFULA_CFG_CMD, /* nfulnl_msg_config_cmd */ + NFULA_CFG_MODE, /* nfulnl_msg_config_mode */ + NFULA_CFG_NLBUFSIZ, /* __u32 buffer size */ + NFULA_CFG_TIMEOUT, /* __u32 in 1/100 s */ + NFULA_CFG_QTHRESH, /* __u32 */ + NFULA_CFG_FLAGS, /* __u16 */ + __NFULA_CFG_MAX +}; +#define NFULA_CFG_MAX (__NFULA_CFG_MAX -1) + +#define NFULNL_COPY_NONE 0x00 +#define NFULNL_COPY_META 0x01 +#define NFULNL_COPY_PACKET 0x02 +/* 0xff is reserved, don't use it for new copy modes. */ + +#define NFULNL_CFG_F_SEQ 0x0001 +#define NFULNL_CFG_F_SEQ_GLOBAL 0x0002 +#define NFULNL_CFG_F_CONNTRACK 0x0004 + +#endif /* _NFNETLINK_LOG_H */ diff --git a/libnl/include/linux-private/linux/netfilter/nfnetlink_queue.h b/libnl/include/linux-private/linux/netfilter/nfnetlink_queue.h new file mode 100644 index 0000000..efcb7c0 --- /dev/null +++ b/libnl/include/linux-private/linux/netfilter/nfnetlink_queue.h @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _NFNETLINK_QUEUE_H +#define _NFNETLINK_QUEUE_H + +#include +#include + +enum nfqnl_msg_types { + NFQNL_MSG_PACKET, /* packet from kernel to userspace */ + NFQNL_MSG_VERDICT, /* verdict from userspace to kernel */ + NFQNL_MSG_CONFIG, /* connect to a particular queue */ + NFQNL_MSG_VERDICT_BATCH, /* batchv from userspace to kernel */ + + NFQNL_MSG_MAX +}; + +struct nfqnl_msg_packet_hdr { + __be32 packet_id; /* unique ID of packet in queue */ + __be16 hw_protocol; /* hw protocol (network order) */ + __u8 hook; /* netfilter hook */ +} __attribute__ ((packed)); + +struct nfqnl_msg_packet_hw { + __be16 hw_addrlen; + __u16 _pad; + __u8 hw_addr[8]; +}; + +struct nfqnl_msg_packet_timestamp { + __aligned_be64 sec; + __aligned_be64 usec; +}; + +enum nfqnl_vlan_attr { + NFQA_VLAN_UNSPEC, + NFQA_VLAN_PROTO, /* __be16 skb vlan_proto */ + NFQA_VLAN_TCI, /* __be16 skb htons(vlan_tci) */ + __NFQA_VLAN_MAX, +}; +#define NFQA_VLAN_MAX (__NFQA_VLAN_MAX - 1) + +enum nfqnl_attr_type { + NFQA_UNSPEC, + NFQA_PACKET_HDR, + NFQA_VERDICT_HDR, /* nfqnl_msg_verdict_hrd */ + NFQA_MARK, /* __u32 nfmark */ + NFQA_TIMESTAMP, /* nfqnl_msg_packet_timestamp */ + NFQA_IFINDEX_INDEV, /* __u32 ifindex */ + NFQA_IFINDEX_OUTDEV, /* __u32 ifindex */ + NFQA_IFINDEX_PHYSINDEV, /* __u32 ifindex */ + NFQA_IFINDEX_PHYSOUTDEV, /* __u32 ifindex */ + NFQA_HWADDR, /* nfqnl_msg_packet_hw */ + NFQA_PAYLOAD, /* opaque data payload */ + NFQA_CT, /* nfnetlink_conntrack.h */ + NFQA_CT_INFO, /* enum ip_conntrack_info */ + NFQA_CAP_LEN, /* __u32 length of captured packet */ + NFQA_SKB_INFO, /* __u32 skb meta information */ + NFQA_EXP, /* nfnetlink_conntrack.h */ + NFQA_UID, /* __u32 sk uid */ + NFQA_GID, /* __u32 sk gid */ + NFQA_SECCTX, /* security context string */ + NFQA_VLAN, /* nested attribute: packet vlan info */ + NFQA_L2HDR, /* full L2 header */ + NFQA_PRIORITY, /* skb->priority */ + NFQA_CGROUP_CLASSID, /* __u32 cgroup classid */ + + __NFQA_MAX +}; +#define NFQA_MAX (__NFQA_MAX - 1) + +struct nfqnl_msg_verdict_hdr { + __be32 verdict; + __be32 id; +}; + + +enum nfqnl_msg_config_cmds { + NFQNL_CFG_CMD_NONE, + NFQNL_CFG_CMD_BIND, + NFQNL_CFG_CMD_UNBIND, + NFQNL_CFG_CMD_PF_BIND, + NFQNL_CFG_CMD_PF_UNBIND, +}; + +struct nfqnl_msg_config_cmd { + __u8 command; /* nfqnl_msg_config_cmds */ + __u8 _pad; + __be16 pf; /* AF_xxx for PF_[UN]BIND */ +}; + +enum nfqnl_config_mode { + NFQNL_COPY_NONE, + NFQNL_COPY_META, + NFQNL_COPY_PACKET, +}; + +struct nfqnl_msg_config_params { + __be32 copy_range; + __u8 copy_mode; /* enum nfqnl_config_mode */ +} __attribute__ ((packed)); + + +enum nfqnl_attr_config { + NFQA_CFG_UNSPEC, + NFQA_CFG_CMD, /* nfqnl_msg_config_cmd */ + NFQA_CFG_PARAMS, /* nfqnl_msg_config_params */ + NFQA_CFG_QUEUE_MAXLEN, /* __u32 */ + NFQA_CFG_MASK, /* identify which flags to change */ + NFQA_CFG_FLAGS, /* value of these flags (__u32) */ + __NFQA_CFG_MAX +}; +#define NFQA_CFG_MAX (__NFQA_CFG_MAX-1) + +/* Flags for NFQA_CFG_FLAGS */ +#define NFQA_CFG_F_FAIL_OPEN (1 << 0) +#define NFQA_CFG_F_CONNTRACK (1 << 1) +#define NFQA_CFG_F_GSO (1 << 2) +#define NFQA_CFG_F_UID_GID (1 << 3) +#define NFQA_CFG_F_SECCTX (1 << 4) +#define NFQA_CFG_F_MAX (1 << 5) + +/* flags for NFQA_SKB_INFO */ +/* packet appears to have wrong checksums, but they are ok */ +#define NFQA_SKB_CSUMNOTREADY (1 << 0) +/* packet is GSO (i.e., exceeds device mtu) */ +#define NFQA_SKB_GSO (1 << 1) +/* csum not validated (incoming device doesn't support hw checksum, etc.) */ +#define NFQA_SKB_CSUM_NOTVERIFIED (1 << 2) + +#endif /* _NFNETLINK_QUEUE_H */ diff --git a/libnl/include/linux-private/linux/netlink.h b/libnl/include/linux-private/linux/netlink.h new file mode 100644 index 0000000..47bac97 --- /dev/null +++ b/libnl/include/linux-private/linux/netlink.h @@ -0,0 +1,374 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_NETLINK_H +#define __LINUX_NETLINK_H + +#include +#include /* for __kernel_sa_family_t */ +#include + +#define NETLINK_ROUTE 0 /* Routing/device hook */ +#define NETLINK_UNUSED 1 /* Unused number */ +#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ +#define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */ +#define NETLINK_SOCK_DIAG 4 /* socket monitoring */ +#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */ +#define NETLINK_XFRM 6 /* ipsec */ +#define NETLINK_SELINUX 7 /* SELinux event notifications */ +#define NETLINK_ISCSI 8 /* Open-iSCSI */ +#define NETLINK_AUDIT 9 /* auditing */ +#define NETLINK_FIB_LOOKUP 10 +#define NETLINK_CONNECTOR 11 +#define NETLINK_NETFILTER 12 /* netfilter subsystem */ +#define NETLINK_IP6_FW 13 +#define NETLINK_DNRTMSG 14 /* DECnet routing messages (obsolete) */ +#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 - fixed format metadata header of Netlink messages + * @nlmsg_len: Length of message including header + * @nlmsg_type: Message content type + * @nlmsg_flags: Additional flags + * @nlmsg_seq: Sequence number + * @nlmsg_pid: Sending process port ID + */ +struct nlmsghdr { + __u32 nlmsg_len; + __u16 nlmsg_type; + __u16 nlmsg_flags; + __u32 nlmsg_seq; + __u32 nlmsg_pid; +}; + +/* 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 /* Receive resulting notifications */ +#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 */ +#define NLM_F_BULK 0x200 /* Delete multiple objects */ + +/* 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_HDRLEN)) +#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_POLICY: policy for a rejected attribute + * @NLMSGERR_ATTR_MISS_TYPE: type of a missing required attribute, + * %NLMSGERR_ATTR_MISS_NEST will not be present if the attribute was + * missing at the message level + * @NLMSGERR_ATTR_MISS_NEST: offset of the nest where attribute was missing + * @__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_POLICY, + NLMSGERR_ATTR_MISS_TYPE, + NLMSGERR_ATTR_MISS_NEST, + + __NLMSGERR_ATTR_MAX, + NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1 +}; + +#define NETLINK_ADD_MEMBERSHIP 1 +#define NETLINK_DROP_MEMBERSHIP 2 +#define NETLINK_PKTINFO 3 +#define NETLINK_BROADCAST_ERROR 4 +#define NETLINK_NO_ENOBUFS 5 +#define NETLINK_RX_RING 6 +#define NETLINK_TX_RING 7 +#define NETLINK_LISTEN_ALL_NSID 8 +#define NETLINK_LIST_MEMBERSHIPS 9 +#define NETLINK_CAP_ACK 10 +#define NETLINK_EXT_ACK 11 +#define NETLINK_GET_STRICT_CHK 12 + +struct nl_pktinfo { + __u32 group; +}; + +struct nl_mmap_req { + unsigned int nm_block_size; + unsigned int nm_block_nr; + unsigned int nm_frame_size; + unsigned int nm_frame_nr; +}; + +struct nl_mmap_hdr { + unsigned int nm_status; + unsigned int nm_len; + __u32 nm_group; + /* credentials */ + __u32 nm_pid; + __u32 nm_uid; + __u32 nm_gid; +}; + +enum nl_mmap_status { + NL_MMAP_STATUS_UNUSED, + NL_MMAP_STATUS_RESERVED, + NL_MMAP_STATUS_VALID, + NL_MMAP_STATUS_COPY, + NL_MMAP_STATUS_SKIP, +}; + +#define NL_MMAP_MSG_ALIGNMENT NLMSG_ALIGNTO +#define NL_MMAP_MSG_ALIGN(sz) __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT) +#define NL_MMAP_HDRLEN NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr)) + +#define NET_MAJOR 36 /* Major 36 is reserved for networking */ + +enum { + NETLINK_UNCONNECTED = 0, + NETLINK_CONNECTED, +}; + +/* + * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)--> + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * | Header | Pad | Payload | Pad | + * | (struct nlattr) | ing | | ing | + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * <-------------- nlattr->nla_len --------------> + */ + +struct nlattr { + __u16 nla_len; + __u16 nla_type; +}; + +/* + * nla_type (16 bits) + * +---+---+-------------------------------+ + * | N | O | Attribute Type | + * +---+---+-------------------------------+ + * N := Carries nested attributes + * O := Payload stored in network byte order + * + * Note: The N and O flag are mutually exclusive. + */ +#define NLA_F_NESTED (1 << 15) +#define NLA_F_NET_BYTEORDER (1 << 14) +#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER) + +#define NLA_ALIGNTO 4 +#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) +#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) + +/* Generic 32 bitflags attribute content sent to the kernel. + * + * The value is a bitmap that defines the values being set + * The selector is a bitmask that defines which value is legit + * + * Examples: + * value = 0x0, and selector = 0x1 + * implies we are selecting bit 1 and we want to set its value to 0. + * + * value = 0x2, and selector = 0x2 + * implies we are selecting bit 2 and we want to set its value to 1. + * + */ +struct nla_bitfield32 { + __u32 value; + __u32 selector; +}; + +/* + * policy descriptions - it's specific to each family how this is used + * Normally, it should be retrieved via a dump inside another attribute + * specifying where it applies. + */ + +/** + * enum netlink_attribute_type - type of an attribute + * @NL_ATTR_TYPE_INVALID: unused + * @NL_ATTR_TYPE_FLAG: flag attribute (present/not present) + * @NL_ATTR_TYPE_U8: 8-bit unsigned attribute + * @NL_ATTR_TYPE_U16: 16-bit unsigned attribute + * @NL_ATTR_TYPE_U32: 32-bit unsigned attribute + * @NL_ATTR_TYPE_U64: 64-bit unsigned attribute + * @NL_ATTR_TYPE_S8: 8-bit signed attribute + * @NL_ATTR_TYPE_S16: 16-bit signed attribute + * @NL_ATTR_TYPE_S32: 32-bit signed attribute + * @NL_ATTR_TYPE_S64: 64-bit signed attribute + * @NL_ATTR_TYPE_BINARY: binary data, min/max length may be specified + * @NL_ATTR_TYPE_STRING: string, min/max length may be specified + * @NL_ATTR_TYPE_NUL_STRING: NUL-terminated string, + * min/max length may be specified + * @NL_ATTR_TYPE_NESTED: nested, i.e. the content of this attribute + * consists of sub-attributes. The nested policy and maxtype + * inside may be specified. + * @NL_ATTR_TYPE_NESTED_ARRAY: nested array, i.e. the content of this + * attribute contains sub-attributes whose type is irrelevant + * (just used to separate the array entries) and each such array + * entry has attributes again, the policy for those inner ones + * and the corresponding maxtype may be specified. + * @NL_ATTR_TYPE_BITFIELD32: &struct nla_bitfield32 attribute + */ +enum netlink_attribute_type { + NL_ATTR_TYPE_INVALID, + + NL_ATTR_TYPE_FLAG, + + NL_ATTR_TYPE_U8, + NL_ATTR_TYPE_U16, + NL_ATTR_TYPE_U32, + NL_ATTR_TYPE_U64, + + NL_ATTR_TYPE_S8, + NL_ATTR_TYPE_S16, + NL_ATTR_TYPE_S32, + NL_ATTR_TYPE_S64, + + NL_ATTR_TYPE_BINARY, + NL_ATTR_TYPE_STRING, + NL_ATTR_TYPE_NUL_STRING, + + NL_ATTR_TYPE_NESTED, + NL_ATTR_TYPE_NESTED_ARRAY, + + NL_ATTR_TYPE_BITFIELD32, +}; + +/** + * enum netlink_policy_type_attr - policy type attributes + * @NL_POLICY_TYPE_ATTR_UNSPEC: unused + * @NL_POLICY_TYPE_ATTR_TYPE: type of the attribute, + * &enum netlink_attribute_type (U32) + * @NL_POLICY_TYPE_ATTR_MIN_VALUE_S: minimum value for signed + * integers (S64) + * @NL_POLICY_TYPE_ATTR_MAX_VALUE_S: maximum value for signed + * integers (S64) + * @NL_POLICY_TYPE_ATTR_MIN_VALUE_U: minimum value for unsigned + * integers (U64) + * @NL_POLICY_TYPE_ATTR_MAX_VALUE_U: maximum value for unsigned + * integers (U64) + * @NL_POLICY_TYPE_ATTR_MIN_LENGTH: minimum length for binary + * attributes, no minimum if not given (U32) + * @NL_POLICY_TYPE_ATTR_MAX_LENGTH: maximum length for binary + * attributes, no maximum if not given (U32) + * @NL_POLICY_TYPE_ATTR_POLICY_IDX: sub policy for nested and + * nested array types (U32) + * @NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE: maximum sub policy + * attribute for nested and nested array types, this can + * in theory be < the size of the policy pointed to by + * the index, if limited inside the nesting (U32) + * @NL_POLICY_TYPE_ATTR_BITFIELD32_MASK: valid mask for the + * bitfield32 type (U32) + * @NL_POLICY_TYPE_ATTR_MASK: mask of valid bits for unsigned integers (U64) + * @NL_POLICY_TYPE_ATTR_PAD: pad attribute for 64-bit alignment + * + * @__NL_POLICY_TYPE_ATTR_MAX: number of attributes + * @NL_POLICY_TYPE_ATTR_MAX: highest attribute number + */ +enum netlink_policy_type_attr { + NL_POLICY_TYPE_ATTR_UNSPEC, + NL_POLICY_TYPE_ATTR_TYPE, + NL_POLICY_TYPE_ATTR_MIN_VALUE_S, + NL_POLICY_TYPE_ATTR_MAX_VALUE_S, + NL_POLICY_TYPE_ATTR_MIN_VALUE_U, + NL_POLICY_TYPE_ATTR_MAX_VALUE_U, + NL_POLICY_TYPE_ATTR_MIN_LENGTH, + NL_POLICY_TYPE_ATTR_MAX_LENGTH, + NL_POLICY_TYPE_ATTR_POLICY_IDX, + NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE, + NL_POLICY_TYPE_ATTR_BITFIELD32_MASK, + NL_POLICY_TYPE_ATTR_PAD, + NL_POLICY_TYPE_ATTR_MASK, + + /* keep last */ + __NL_POLICY_TYPE_ATTR_MAX, + NL_POLICY_TYPE_ATTR_MAX = __NL_POLICY_TYPE_ATTR_MAX - 1 +}; + +#endif /* __LINUX_NETLINK_H */ diff --git a/libnl/include/linux-private/linux/nexthop.h b/libnl/include/linux-private/linux/nexthop.h new file mode 100644 index 0000000..37b14b4 --- /dev/null +++ b/libnl/include/linux-private/linux/nexthop.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_NEXTHOP_H +#define _LINUX_NEXTHOP_H + +#include + +struct nhmsg { + unsigned char nh_family; + unsigned char nh_scope; /* return only */ + unsigned char nh_protocol; /* Routing protocol that installed nh */ + unsigned char resvd; + unsigned int nh_flags; /* RTNH_F flags */ +}; + +/* entry in a nexthop group */ +struct nexthop_grp { + __u32 id; /* nexthop id - must exist */ + __u8 weight; /* weight of this nexthop */ + __u8 resvd1; + __u16 resvd2; +}; + +enum { + NEXTHOP_GRP_TYPE_MPATH, /* hash-threshold nexthop group + * default type if not specified + */ + NEXTHOP_GRP_TYPE_RES, /* resilient nexthop group */ + __NEXTHOP_GRP_TYPE_MAX, +}; + +#define NEXTHOP_GRP_TYPE_MAX (__NEXTHOP_GRP_TYPE_MAX - 1) + +enum { + NHA_UNSPEC, + NHA_ID, /* u32; id for nexthop. id == 0 means auto-assign */ + + NHA_GROUP, /* array of nexthop_grp */ + NHA_GROUP_TYPE, /* u16 one of NEXTHOP_GRP_TYPE */ + /* if NHA_GROUP attribute is added, no other attributes can be set */ + + NHA_BLACKHOLE, /* flag; nexthop used to blackhole packets */ + /* if NHA_BLACKHOLE is added, OIF, GATEWAY, ENCAP can not be set */ + + NHA_OIF, /* u32; nexthop device */ + NHA_GATEWAY, /* be32 (IPv4) or in6_addr (IPv6) gw address */ + NHA_ENCAP_TYPE, /* u16; lwt encap type */ + NHA_ENCAP, /* lwt encap data */ + + /* NHA_OIF can be appended to dump request to return only + * nexthops using given device + */ + NHA_GROUPS, /* flag; only return nexthop groups in dump */ + NHA_MASTER, /* u32; only return nexthops with given master dev */ + + NHA_FDB, /* flag; nexthop belongs to a bridge fdb */ + /* if NHA_FDB is added, OIF, BLACKHOLE, ENCAP cannot be set */ + + /* nested; resilient nexthop group attributes */ + NHA_RES_GROUP, + /* nested; nexthop bucket attributes */ + NHA_RES_BUCKET, + + __NHA_MAX, +}; + +#define NHA_MAX (__NHA_MAX - 1) + +enum { + NHA_RES_GROUP_UNSPEC, + /* Pad attribute for 64-bit alignment. */ + NHA_RES_GROUP_PAD = NHA_RES_GROUP_UNSPEC, + + /* u16; number of nexthop buckets in a resilient nexthop group */ + NHA_RES_GROUP_BUCKETS, + /* clock_t as u32; nexthop bucket idle timer (per-group) */ + NHA_RES_GROUP_IDLE_TIMER, + /* clock_t as u32; nexthop unbalanced timer */ + NHA_RES_GROUP_UNBALANCED_TIMER, + /* clock_t as u64; nexthop unbalanced time */ + NHA_RES_GROUP_UNBALANCED_TIME, + + __NHA_RES_GROUP_MAX, +}; + +#define NHA_RES_GROUP_MAX (__NHA_RES_GROUP_MAX - 1) + +enum { + NHA_RES_BUCKET_UNSPEC, + /* Pad attribute for 64-bit alignment. */ + NHA_RES_BUCKET_PAD = NHA_RES_BUCKET_UNSPEC, + + /* u16; nexthop bucket index */ + NHA_RES_BUCKET_INDEX, + /* clock_t as u64; nexthop bucket idle time */ + NHA_RES_BUCKET_IDLE_TIME, + /* u32; nexthop id assigned to the nexthop bucket */ + NHA_RES_BUCKET_NH_ID, + + __NHA_RES_BUCKET_MAX, +}; + +#define NHA_RES_BUCKET_MAX (__NHA_RES_BUCKET_MAX - 1) + +#endif diff --git a/libnl/include/linux-private/linux/pkt_cls.h b/libnl/include/linux-private/linux/pkt_cls.h new file mode 100644 index 0000000..648a82f --- /dev/null +++ b/libnl/include/linux-private/linux/pkt_cls.h @@ -0,0 +1,804 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_PKT_CLS_H +#define __LINUX_PKT_CLS_H + +#include +#include + +#define TC_COOKIE_MAX_SIZE 16 + +/* Action attributes */ +enum { + TCA_ACT_UNSPEC, + TCA_ACT_KIND, + TCA_ACT_OPTIONS, + TCA_ACT_INDEX, + TCA_ACT_STATS, + TCA_ACT_PAD, + TCA_ACT_COOKIE, + TCA_ACT_FLAGS, + TCA_ACT_HW_STATS, + TCA_ACT_USED_HW_STATS, + TCA_ACT_IN_HW_COUNT, + __TCA_ACT_MAX +}; + +/* See other TCA_ACT_FLAGS_ * flags in include/net/act_api.h. */ +#define TCA_ACT_FLAGS_NO_PERCPU_STATS (1 << 0) /* Don't use percpu allocator for + * actions stats. + */ +#define TCA_ACT_FLAGS_SKIP_HW (1 << 1) /* don't offload action to HW */ +#define TCA_ACT_FLAGS_SKIP_SW (1 << 2) /* don't use action in SW */ + +/* tca HW stats type + * When user does not pass the attribute, he does not care. + * It is the same as if he would pass the attribute with + * all supported bits set. + * In case no bits are set, user is not interested in getting any HW statistics. + */ +#define TCA_ACT_HW_STATS_IMMEDIATE (1 << 0) /* Means that in dump, user + * gets the current HW stats + * state from the device + * queried at the dump time. + */ +#define TCA_ACT_HW_STATS_DELAYED (1 << 1) /* Means that in dump, user gets + * HW stats that might be out of date + * for some time, maybe couple of + * seconds. This is the case when + * driver polls stats updates + * periodically or when it gets async + * stats update from the device. + */ + +#define TCA_ACT_MAX __TCA_ACT_MAX +#define TCA_OLD_COMPAT (TCA_ACT_MAX+1) +#define TCA_ACT_MAX_PRIO 32 +#define TCA_ACT_BIND 1 +#define TCA_ACT_NOBIND 0 +#define TCA_ACT_UNBIND 1 +#define TCA_ACT_NOUNBIND 0 +#define TCA_ACT_REPLACE 1 +#define TCA_ACT_NOREPLACE 0 + +#define TC_ACT_UNSPEC (-1) +#define TC_ACT_OK 0 +#define TC_ACT_RECLASSIFY 1 +#define TC_ACT_SHOT 2 +#define TC_ACT_PIPE 3 +#define TC_ACT_STOLEN 4 +#define TC_ACT_QUEUED 5 +#define TC_ACT_REPEAT 6 +#define TC_ACT_REDIRECT 7 +#define TC_ACT_TRAP 8 /* For hw path, this means "trap to cpu" + * and don't further process the frame + * in hardware. For sw path, this is + * equivalent of TC_ACT_STOLEN - drop + * the skb and act like everything + * is alright. + */ +#define TC_ACT_VALUE_MAX TC_ACT_TRAP + +/* There is a special kind of actions called "extended actions", + * which need a value parameter. These have a local opcode located in + * the highest nibble, starting from 1. The rest of the bits + * are used to carry the value. These two parts together make + * a combined opcode. + */ +#define __TC_ACT_EXT_SHIFT 28 +#define __TC_ACT_EXT(local) ((local) << __TC_ACT_EXT_SHIFT) +#define TC_ACT_EXT_VAL_MASK ((1 << __TC_ACT_EXT_SHIFT) - 1) +#define TC_ACT_EXT_OPCODE(combined) ((combined) & (~TC_ACT_EXT_VAL_MASK)) +#define TC_ACT_EXT_CMP(combined, opcode) (TC_ACT_EXT_OPCODE(combined) == opcode) + +#define TC_ACT_JUMP __TC_ACT_EXT(1) +#define TC_ACT_GOTO_CHAIN __TC_ACT_EXT(2) +#define TC_ACT_EXT_OPCODE_MAX TC_ACT_GOTO_CHAIN + +/* These macros are put here for binary compatibility with userspace apps that + * make use of them. For kernel code and new userspace apps, use the TCA_ID_* + * versions. + */ +#define TCA_ACT_GACT 5 +#define TCA_ACT_IPT 6 +#define TCA_ACT_PEDIT 7 +#define TCA_ACT_MIRRED 8 +#define TCA_ACT_NAT 9 +#define TCA_ACT_XT 10 +#define TCA_ACT_SKBEDIT 11 +#define TCA_ACT_VLAN 12 +#define TCA_ACT_BPF 13 +#define TCA_ACT_CONNMARK 14 +#define TCA_ACT_SKBMOD 15 +#define TCA_ACT_CSUM 16 +#define TCA_ACT_TUNNEL_KEY 17 +#define TCA_ACT_SIMP 22 +#define TCA_ACT_IFE 25 +#define TCA_ACT_SAMPLE 26 + +/* Action type identifiers*/ +enum tca_id { + TCA_ID_UNSPEC = 0, + TCA_ID_POLICE = 1, + TCA_ID_GACT = TCA_ACT_GACT, + TCA_ID_IPT = TCA_ACT_IPT, + TCA_ID_PEDIT = TCA_ACT_PEDIT, + TCA_ID_MIRRED = TCA_ACT_MIRRED, + TCA_ID_NAT = TCA_ACT_NAT, + TCA_ID_XT = TCA_ACT_XT, + TCA_ID_SKBEDIT = TCA_ACT_SKBEDIT, + TCA_ID_VLAN = TCA_ACT_VLAN, + TCA_ID_BPF = TCA_ACT_BPF, + TCA_ID_CONNMARK = TCA_ACT_CONNMARK, + TCA_ID_SKBMOD = TCA_ACT_SKBMOD, + TCA_ID_CSUM = TCA_ACT_CSUM, + TCA_ID_TUNNEL_KEY = TCA_ACT_TUNNEL_KEY, + TCA_ID_SIMP = TCA_ACT_SIMP, + TCA_ID_IFE = TCA_ACT_IFE, + TCA_ID_SAMPLE = TCA_ACT_SAMPLE, + TCA_ID_CTINFO, + TCA_ID_MPLS, + TCA_ID_CT, + TCA_ID_GATE, + /* other actions go here */ + __TCA_ID_MAX = 255 +}; + +#define TCA_ID_MAX __TCA_ID_MAX + +struct tc_police { + __u32 index; + int action; +#define TC_POLICE_UNSPEC TC_ACT_UNSPEC +#define TC_POLICE_OK TC_ACT_OK +#define TC_POLICE_RECLASSIFY TC_ACT_RECLASSIFY +#define TC_POLICE_SHOT TC_ACT_SHOT +#define TC_POLICE_PIPE TC_ACT_PIPE + + __u32 limit; + __u32 burst; + __u32 mtu; + struct tc_ratespec rate; + struct tc_ratespec peakrate; + int refcnt; + int bindcnt; + __u32 capab; +}; + +struct tcf_t { + __u64 install; + __u64 lastuse; + __u64 expires; + __u64 firstuse; +}; + +struct tc_cnt { + int refcnt; + int bindcnt; +}; + +#define tc_gen \ + __u32 index; \ + __u32 capab; \ + int action; \ + int refcnt; \ + int bindcnt + +enum { + TCA_POLICE_UNSPEC, + TCA_POLICE_TBF, + TCA_POLICE_RATE, + TCA_POLICE_PEAKRATE, + TCA_POLICE_AVRATE, + TCA_POLICE_RESULT, + TCA_POLICE_TM, + TCA_POLICE_PAD, + TCA_POLICE_RATE64, + TCA_POLICE_PEAKRATE64, + TCA_POLICE_PKTRATE64, + TCA_POLICE_PKTBURST64, + __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[]; +}; + +struct tc_u32_mark { + __u32 val; + __u32 mask; + __u32 success; +}; + +struct tc_u32_pcnt { + __u64 rcnt; + __u64 rhit; + __u64 kcnts[]; +}; + +/* Flags */ + +#define TC_U32_TERMINAL 1 +#define TC_U32_OFFSET 2 +#define TC_U32_VAROFFSET 4 +#define TC_U32_EAT 8 + +#define TC_U32_MAXDEPTH 8 + + +/* RSVP filter */ + +enum { + TCA_RSVP_UNSPEC, + TCA_RSVP_CLASSID, + TCA_RSVP_DST, + TCA_RSVP_SRC, + TCA_RSVP_PINFO, + TCA_RSVP_POLICE, + TCA_RSVP_ACT, + __TCA_RSVP_MAX +}; + +#define TCA_RSVP_MAX (__TCA_RSVP_MAX - 1 ) + +struct tc_rsvp_gpi { + __u32 key; + __u32 mask; + int offset; +}; + +struct tc_rsvp_pinfo { + struct tc_rsvp_gpi dpi; + struct tc_rsvp_gpi spi; + __u8 protocol; + __u8 tunnelid; + __u8 tunnelhdr; + __u8 pad; +}; + +/* ROUTE filter */ + +enum { + TCA_ROUTE4_UNSPEC, + TCA_ROUTE4_CLASSID, + TCA_ROUTE4_TO, + TCA_ROUTE4_FROM, + TCA_ROUTE4_IIF, + TCA_ROUTE4_POLICE, + TCA_ROUTE4_ACT, + __TCA_ROUTE4_MAX +}; + +#define TCA_ROUTE4_MAX (__TCA_ROUTE4_MAX - 1) + + +/* FW filter */ + +enum { + TCA_FW_UNSPEC, + TCA_FW_CLASSID, + TCA_FW_POLICE, + TCA_FW_INDEV, + TCA_FW_ACT, /* used by CONFIG_NET_CLS_ACT */ + TCA_FW_MASK, + __TCA_FW_MAX +}; + +#define TCA_FW_MAX (__TCA_FW_MAX - 1) + +/* TC index filter */ + +enum { + TCA_TCINDEX_UNSPEC, + TCA_TCINDEX_HASH, + TCA_TCINDEX_MASK, + TCA_TCINDEX_SHIFT, + TCA_TCINDEX_FALL_THROUGH, + TCA_TCINDEX_CLASSID, + TCA_TCINDEX_POLICE, + TCA_TCINDEX_ACT, + __TCA_TCINDEX_MAX +}; + +#define TCA_TCINDEX_MAX (__TCA_TCINDEX_MAX - 1) + +/* Flow filter */ + +enum { + FLOW_KEY_SRC, + FLOW_KEY_DST, + FLOW_KEY_PROTO, + FLOW_KEY_PROTO_SRC, + FLOW_KEY_PROTO_DST, + FLOW_KEY_IIF, + FLOW_KEY_PRIORITY, + FLOW_KEY_MARK, + FLOW_KEY_NFCT, + FLOW_KEY_NFCT_SRC, + FLOW_KEY_NFCT_DST, + FLOW_KEY_NFCT_PROTO_SRC, + FLOW_KEY_NFCT_PROTO_DST, + FLOW_KEY_RTCLASSID, + FLOW_KEY_SKUID, + FLOW_KEY_SKGID, + FLOW_KEY_VLAN_TAG, + FLOW_KEY_RXHASH, + __FLOW_KEY_MAX, +}; + +#define FLOW_KEY_MAX (__FLOW_KEY_MAX - 1) + +enum { + FLOW_MODE_MAP, + FLOW_MODE_HASH, +}; + +enum { + TCA_FLOW_UNSPEC, + TCA_FLOW_KEYS, + TCA_FLOW_MODE, + TCA_FLOW_BASECLASS, + TCA_FLOW_RSHIFT, + TCA_FLOW_ADDEND, + TCA_FLOW_MASK, + TCA_FLOW_XOR, + TCA_FLOW_DIVISOR, + TCA_FLOW_ACT, + TCA_FLOW_POLICE, + TCA_FLOW_EMATCHES, + TCA_FLOW_PERTURB, + __TCA_FLOW_MAX +}; + +#define TCA_FLOW_MAX (__TCA_FLOW_MAX - 1) + +/* Basic filter */ + +struct tc_basic_pcnt { + __u64 rcnt; + __u64 rhit; +}; + +enum { + TCA_BASIC_UNSPEC, + TCA_BASIC_CLASSID, + TCA_BASIC_EMATCHES, + TCA_BASIC_ACT, + TCA_BASIC_POLICE, + TCA_BASIC_PCNT, + TCA_BASIC_PAD, + __TCA_BASIC_MAX +}; + +#define TCA_BASIC_MAX (__TCA_BASIC_MAX - 1) + + +/* Cgroup classifier */ + +enum { + TCA_CGROUP_UNSPEC, + TCA_CGROUP_ACT, + TCA_CGROUP_POLICE, + TCA_CGROUP_EMATCHES, + __TCA_CGROUP_MAX, +}; + +#define TCA_CGROUP_MAX (__TCA_CGROUP_MAX - 1) + +/* BPF classifier */ + +#define TCA_BPF_FLAG_ACT_DIRECT (1 << 0) + +enum { + TCA_BPF_UNSPEC, + TCA_BPF_ACT, + TCA_BPF_POLICE, + TCA_BPF_CLASSID, + TCA_BPF_OPS_LEN, + TCA_BPF_OPS, + TCA_BPF_FD, + TCA_BPF_NAME, + TCA_BPF_FLAGS, + TCA_BPF_FLAGS_GEN, + TCA_BPF_TAG, + TCA_BPF_ID, + __TCA_BPF_MAX, +}; + +#define TCA_BPF_MAX (__TCA_BPF_MAX - 1) + +/* Flower classifier */ + +enum { + TCA_FLOWER_UNSPEC, + TCA_FLOWER_CLASSID, + TCA_FLOWER_INDEV, + TCA_FLOWER_ACT, + TCA_FLOWER_KEY_ETH_DST, /* ETH_ALEN */ + TCA_FLOWER_KEY_ETH_DST_MASK, /* ETH_ALEN */ + TCA_FLOWER_KEY_ETH_SRC, /* ETH_ALEN */ + TCA_FLOWER_KEY_ETH_SRC_MASK, /* ETH_ALEN */ + TCA_FLOWER_KEY_ETH_TYPE, /* be16 */ + TCA_FLOWER_KEY_IP_PROTO, /* u8 */ + TCA_FLOWER_KEY_IPV4_SRC, /* be32 */ + TCA_FLOWER_KEY_IPV4_SRC_MASK, /* be32 */ + TCA_FLOWER_KEY_IPV4_DST, /* be32 */ + TCA_FLOWER_KEY_IPV4_DST_MASK, /* be32 */ + TCA_FLOWER_KEY_IPV6_SRC, /* struct in6_addr */ + TCA_FLOWER_KEY_IPV6_SRC_MASK, /* struct in6_addr */ + TCA_FLOWER_KEY_IPV6_DST, /* struct in6_addr */ + TCA_FLOWER_KEY_IPV6_DST_MASK, /* struct in6_addr */ + TCA_FLOWER_KEY_TCP_SRC, /* be16 */ + TCA_FLOWER_KEY_TCP_DST, /* be16 */ + TCA_FLOWER_KEY_UDP_SRC, /* be16 */ + TCA_FLOWER_KEY_UDP_DST, /* be16 */ + + TCA_FLOWER_FLAGS, + TCA_FLOWER_KEY_VLAN_ID, /* be16 */ + TCA_FLOWER_KEY_VLAN_PRIO, /* u8 */ + TCA_FLOWER_KEY_VLAN_ETH_TYPE, /* be16 */ + + TCA_FLOWER_KEY_ENC_KEY_ID, /* be32 */ + TCA_FLOWER_KEY_ENC_IPV4_SRC, /* be32 */ + TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK,/* be32 */ + TCA_FLOWER_KEY_ENC_IPV4_DST, /* be32 */ + TCA_FLOWER_KEY_ENC_IPV4_DST_MASK,/* be32 */ + TCA_FLOWER_KEY_ENC_IPV6_SRC, /* struct in6_addr */ + TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK,/* struct in6_addr */ + TCA_FLOWER_KEY_ENC_IPV6_DST, /* struct in6_addr */ + TCA_FLOWER_KEY_ENC_IPV6_DST_MASK,/* struct in6_addr */ + + TCA_FLOWER_KEY_TCP_SRC_MASK, /* be16 */ + TCA_FLOWER_KEY_TCP_DST_MASK, /* be16 */ + TCA_FLOWER_KEY_UDP_SRC_MASK, /* be16 */ + TCA_FLOWER_KEY_UDP_DST_MASK, /* be16 */ + TCA_FLOWER_KEY_SCTP_SRC_MASK, /* be16 */ + TCA_FLOWER_KEY_SCTP_DST_MASK, /* be16 */ + + TCA_FLOWER_KEY_SCTP_SRC, /* be16 */ + TCA_FLOWER_KEY_SCTP_DST, /* be16 */ + + TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, /* be16 */ + TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, /* be16 */ + TCA_FLOWER_KEY_ENC_UDP_DST_PORT, /* be16 */ + TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, /* be16 */ + + TCA_FLOWER_KEY_FLAGS, /* be32 */ + TCA_FLOWER_KEY_FLAGS_MASK, /* be32 */ + + TCA_FLOWER_KEY_ICMPV4_CODE, /* u8 */ + TCA_FLOWER_KEY_ICMPV4_CODE_MASK,/* u8 */ + TCA_FLOWER_KEY_ICMPV4_TYPE, /* u8 */ + TCA_FLOWER_KEY_ICMPV4_TYPE_MASK,/* u8 */ + TCA_FLOWER_KEY_ICMPV6_CODE, /* u8 */ + TCA_FLOWER_KEY_ICMPV6_CODE_MASK,/* u8 */ + TCA_FLOWER_KEY_ICMPV6_TYPE, /* u8 */ + TCA_FLOWER_KEY_ICMPV6_TYPE_MASK,/* u8 */ + + TCA_FLOWER_KEY_ARP_SIP, /* be32 */ + TCA_FLOWER_KEY_ARP_SIP_MASK, /* be32 */ + TCA_FLOWER_KEY_ARP_TIP, /* be32 */ + TCA_FLOWER_KEY_ARP_TIP_MASK, /* be32 */ + TCA_FLOWER_KEY_ARP_OP, /* u8 */ + TCA_FLOWER_KEY_ARP_OP_MASK, /* u8 */ + TCA_FLOWER_KEY_ARP_SHA, /* ETH_ALEN */ + TCA_FLOWER_KEY_ARP_SHA_MASK, /* ETH_ALEN */ + TCA_FLOWER_KEY_ARP_THA, /* ETH_ALEN */ + TCA_FLOWER_KEY_ARP_THA_MASK, /* ETH_ALEN */ + + TCA_FLOWER_KEY_MPLS_TTL, /* u8 - 8 bits */ + TCA_FLOWER_KEY_MPLS_BOS, /* u8 - 1 bit */ + TCA_FLOWER_KEY_MPLS_TC, /* u8 - 3 bits */ + TCA_FLOWER_KEY_MPLS_LABEL, /* be32 - 20 bits */ + + TCA_FLOWER_KEY_TCP_FLAGS, /* be16 */ + TCA_FLOWER_KEY_TCP_FLAGS_MASK, /* be16 */ + + TCA_FLOWER_KEY_IP_TOS, /* u8 */ + TCA_FLOWER_KEY_IP_TOS_MASK, /* u8 */ + TCA_FLOWER_KEY_IP_TTL, /* u8 */ + TCA_FLOWER_KEY_IP_TTL_MASK, /* u8 */ + + TCA_FLOWER_KEY_CVLAN_ID, /* be16 */ + TCA_FLOWER_KEY_CVLAN_PRIO, /* u8 */ + TCA_FLOWER_KEY_CVLAN_ETH_TYPE, /* be16 */ + + TCA_FLOWER_KEY_ENC_IP_TOS, /* u8 */ + TCA_FLOWER_KEY_ENC_IP_TOS_MASK, /* u8 */ + TCA_FLOWER_KEY_ENC_IP_TTL, /* u8 */ + TCA_FLOWER_KEY_ENC_IP_TTL_MASK, /* u8 */ + + TCA_FLOWER_KEY_ENC_OPTS, + TCA_FLOWER_KEY_ENC_OPTS_MASK, + + TCA_FLOWER_IN_HW_COUNT, + + TCA_FLOWER_KEY_PORT_SRC_MIN, /* be16 */ + TCA_FLOWER_KEY_PORT_SRC_MAX, /* be16 */ + TCA_FLOWER_KEY_PORT_DST_MIN, /* be16 */ + TCA_FLOWER_KEY_PORT_DST_MAX, /* be16 */ + + TCA_FLOWER_KEY_CT_STATE, /* u16 */ + TCA_FLOWER_KEY_CT_STATE_MASK, /* u16 */ + TCA_FLOWER_KEY_CT_ZONE, /* u16 */ + TCA_FLOWER_KEY_CT_ZONE_MASK, /* u16 */ + TCA_FLOWER_KEY_CT_MARK, /* u32 */ + TCA_FLOWER_KEY_CT_MARK_MASK, /* u32 */ + TCA_FLOWER_KEY_CT_LABELS, /* u128 */ + TCA_FLOWER_KEY_CT_LABELS_MASK, /* u128 */ + + TCA_FLOWER_KEY_MPLS_OPTS, + + TCA_FLOWER_KEY_HASH, /* u32 */ + TCA_FLOWER_KEY_HASH_MASK, /* u32 */ + + TCA_FLOWER_KEY_NUM_OF_VLANS, /* u8 */ + + TCA_FLOWER_KEY_PPPOE_SID, /* be16 */ + TCA_FLOWER_KEY_PPP_PROTO, /* be16 */ + + TCA_FLOWER_KEY_L2TPV3_SID, /* be32 */ + + __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. */ + TCA_FLOWER_KEY_CT_FLAGS_INVALID = 1 << 4, /* Conntrack is invalid. */ + TCA_FLOWER_KEY_CT_FLAGS_REPLY = 1 << 5, /* Packet is in the reply direction. */ + __TCA_FLOWER_KEY_CT_FLAGS_MAX, +}; + +enum { + TCA_FLOWER_KEY_ENC_OPTS_UNSPEC, + TCA_FLOWER_KEY_ENC_OPTS_GENEVE, /* Nested + * TCA_FLOWER_KEY_ENC_OPT_GENEVE_ + * attributes + */ + TCA_FLOWER_KEY_ENC_OPTS_VXLAN, /* Nested + * TCA_FLOWER_KEY_ENC_OPT_VXLAN_ + * attributes + */ + TCA_FLOWER_KEY_ENC_OPTS_ERSPAN, /* Nested + * TCA_FLOWER_KEY_ENC_OPT_ERSPAN_ + * attributes + */ + TCA_FLOWER_KEY_ENC_OPTS_GTP, /* Nested + * TCA_FLOWER_KEY_ENC_OPT_GTP_ + * attributes + */ + __TCA_FLOWER_KEY_ENC_OPTS_MAX, +}; + +#define TCA_FLOWER_KEY_ENC_OPTS_MAX (__TCA_FLOWER_KEY_ENC_OPTS_MAX - 1) + +enum { + TCA_FLOWER_KEY_ENC_OPT_GENEVE_UNSPEC, + TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS, /* u16 */ + TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE, /* u8 */ + TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA, /* 4 to 128 bytes */ + + __TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX, +}; + +#define TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX \ + (__TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX - 1) + +enum { + TCA_FLOWER_KEY_ENC_OPT_VXLAN_UNSPEC, + TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP, /* u32 */ + __TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX, +}; + +#define TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX \ + (__TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX - 1) + +enum { + TCA_FLOWER_KEY_ENC_OPT_ERSPAN_UNSPEC, + TCA_FLOWER_KEY_ENC_OPT_ERSPAN_VER, /* u8 */ + TCA_FLOWER_KEY_ENC_OPT_ERSPAN_INDEX, /* be32 */ + TCA_FLOWER_KEY_ENC_OPT_ERSPAN_DIR, /* u8 */ + TCA_FLOWER_KEY_ENC_OPT_ERSPAN_HWID, /* u8 */ + __TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX, +}; + +#define TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX \ + (__TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX - 1) + +enum { + TCA_FLOWER_KEY_ENC_OPT_GTP_UNSPEC, + TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE, /* u8 */ + TCA_FLOWER_KEY_ENC_OPT_GTP_QFI, /* u8 */ + + __TCA_FLOWER_KEY_ENC_OPT_GTP_MAX, +}; + +#define TCA_FLOWER_KEY_ENC_OPT_GTP_MAX \ + (__TCA_FLOWER_KEY_ENC_OPT_GTP_MAX - 1) + +enum { + TCA_FLOWER_KEY_MPLS_OPTS_UNSPEC, + TCA_FLOWER_KEY_MPLS_OPTS_LSE, + __TCA_FLOWER_KEY_MPLS_OPTS_MAX, +}; + +#define TCA_FLOWER_KEY_MPLS_OPTS_MAX (__TCA_FLOWER_KEY_MPLS_OPTS_MAX - 1) + +enum { + TCA_FLOWER_KEY_MPLS_OPT_LSE_UNSPEC, + TCA_FLOWER_KEY_MPLS_OPT_LSE_DEPTH, + TCA_FLOWER_KEY_MPLS_OPT_LSE_TTL, + TCA_FLOWER_KEY_MPLS_OPT_LSE_BOS, + TCA_FLOWER_KEY_MPLS_OPT_LSE_TC, + TCA_FLOWER_KEY_MPLS_OPT_LSE_LABEL, + __TCA_FLOWER_KEY_MPLS_OPT_LSE_MAX, +}; + +#define TCA_FLOWER_KEY_MPLS_OPT_LSE_MAX \ + (__TCA_FLOWER_KEY_MPLS_OPT_LSE_MAX - 1) + +enum { + TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT = (1 << 0), + TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST = (1 << 1), +}; + +#define TCA_FLOWER_MASK_FLAGS_RANGE (1 << 0) /* Range-based match */ + +/* Match-all classifier */ + +struct tc_matchall_pcnt { + __u64 rhit; +}; + +enum { + TCA_MATCHALL_UNSPEC, + TCA_MATCHALL_CLASSID, + TCA_MATCHALL_ACT, + TCA_MATCHALL_FLAGS, + TCA_MATCHALL_PCNT, + TCA_MATCHALL_PAD, + __TCA_MATCHALL_MAX, +}; + +#define TCA_MATCHALL_MAX (__TCA_MATCHALL_MAX - 1) + +/* Extended Matches */ + +struct tcf_ematch_tree_hdr { + __u16 nmatches; + __u16 progid; +}; + +enum { + TCA_EMATCH_TREE_UNSPEC, + TCA_EMATCH_TREE_HDR, + TCA_EMATCH_TREE_LIST, + __TCA_EMATCH_TREE_MAX +}; +#define TCA_EMATCH_TREE_MAX (__TCA_EMATCH_TREE_MAX - 1) + +struct tcf_ematch_hdr { + __u16 matchid; + __u16 kind; + __u16 flags; + __u16 pad; /* currently unused */ +}; + +/* 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-----------------------+-+-+---+ + * | Unused |S|I| R | + * +-----------------------+-+-+---+ + * + * R(2) ::= relation to next ematch + * where: 0 0 END (last ematch) + * 0 1 AND + * 1 0 OR + * 1 1 Unused (invalid) + * I(1) ::= invert result + * S(1) ::= simple payload + */ +#define TCF_EM_REL_END 0 +#define TCF_EM_REL_AND (1<<0) +#define TCF_EM_REL_OR (1<<1) +#define TCF_EM_INVERT (1<<2) +#define TCF_EM_SIMPLE (1<<3) + +#define TCF_EM_REL_MASK 3 +#define TCF_EM_REL_VALID(v) (((v) & TCF_EM_REL_MASK) != TCF_EM_REL_MASK) + +enum { + TCF_LAYER_LINK, + TCF_LAYER_NETWORK, + TCF_LAYER_TRANSPORT, + __TCF_LAYER_MAX +}; +#define TCF_LAYER_MAX (__TCF_LAYER_MAX - 1) + +/* Ematch type assignments + * 1..32767 Reserved for ematches inside kernel tree + * 32768..65535 Free to use, not reliable + */ +#define TCF_EM_CONTAINER 0 +#define TCF_EM_CMP 1 +#define TCF_EM_NBYTE 2 +#define TCF_EM_U32 3 +#define TCF_EM_META 4 +#define TCF_EM_TEXT 5 +#define TCF_EM_VLAN 6 +#define TCF_EM_CANID 7 +#define TCF_EM_IPSET 8 +#define TCF_EM_IPT 9 +#define TCF_EM_MAX 9 + +enum { + TCF_EM_PROG_TC +}; + +enum { + TCF_EM_OPND_EQ, + TCF_EM_OPND_GT, + TCF_EM_OPND_LT +}; + +#endif diff --git a/libnl/include/linux-private/linux/pkt_sched.h b/libnl/include/linux-private/linux/pkt_sched.h new file mode 100644 index 0000000..51a7add --- /dev/null +++ b/libnl/include/linux-private/linux/pkt_sched.h @@ -0,0 +1,1298 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_PKT_SCHED_H +#define __LINUX_PKT_SCHED_H + +#include +#include + +/* Logical priority bands not depending on specific packet scheduler. + Every scheduler will map them to real traffic classes, if it has + no more precise mechanism to classify packets. + + These numbers have no special meaning, though their coincidence + with obsolete IPv6 values is not occasional :-). New IPv6 drafts + preferred full anarchy inspired by diffserv group. + + Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy + class, actually, as rule it will be handled with more care than + filler or even bulk. + */ + +#define TC_PRIO_BESTEFFORT 0 +#define TC_PRIO_FILLER 1 +#define TC_PRIO_BULK 2 +#define TC_PRIO_INTERACTIVE_BULK 4 +#define TC_PRIO_INTERACTIVE 6 +#define TC_PRIO_CONTROL 7 + +#define TC_PRIO_MAX 15 + +/* Generic queue statistics, available for all the elements. + Particular schedulers may have also their private records. + */ + +struct tc_stats { + __u64 bytes; /* Number of enqueued bytes */ + __u32 packets; /* Number of enqueued packets */ + __u32 drops; /* Packets dropped because of lack of resources */ + __u32 overlimits; /* Number of throttle events when this + * flow goes out of allocated bandwidth */ + __u32 bps; /* Current flow byte rate */ + __u32 pps; /* Current flow packet rate */ + __u32 qlen; + __u32 backlog; +}; + +struct tc_estimator { + signed char interval; + unsigned char ewma_log; +}; + +/* "Handles" + --------- + + All the traffic control objects have 32bit identifiers, or "handles". + + They can be considered as opaque numbers from user API viewpoint, + but actually they always consist of two fields: major and + minor numbers, which are interpreted by kernel specially, + that may be used by applications, though not recommended. + + F.e. qdisc handles always have minor number equal to zero, + classes (or flows) have major equal to parent qdisc major, and + minor uniquely identifying class inside qdisc. + + Macros to manipulate handles: + */ + +#define TC_H_MAJ_MASK (0xFFFF0000U) +#define TC_H_MIN_MASK (0x0000FFFFU) +#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK) +#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK) +#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK)) + +#define TC_H_UNSPEC (0U) +#define TC_H_ROOT (0xFFFFFFFFU) +#define TC_H_INGRESS (0xFFFFFFF1U) +#define TC_H_CLSACT TC_H_INGRESS + +#define TC_H_MIN_PRIORITY 0xFFE0U +#define TC_H_MIN_INGRESS 0xFFF2U +#define TC_H_MIN_EGRESS 0xFFF3U + +/* Need to corrospond to iproute2 tc/tc_core.h "enum link_layer" */ +enum tc_link_layer { + TC_LINKLAYER_UNAWARE, /* Indicate unaware old iproute2 util */ + TC_LINKLAYER_ETHERNET, + TC_LINKLAYER_ATM, +}; +#define TC_LINKLAYER_MASK 0x0F /* limit use to lower 4 bits */ + +struct tc_ratespec { + unsigned char cell_log; + __u8 linklayer; /* lower 4 bits */ + unsigned short overhead; + short cell_align; + unsigned short mpu; + __u32 rate; +}; + +#define TC_RTAB_SIZE 1024 + +struct tc_sizespec { + unsigned char cell_log; + unsigned char size_log; + short cell_align; + int overhead; + unsigned int linklayer; + unsigned int mpu; + unsigned int mtu; + unsigned int tsize; +}; + +enum { + TCA_STAB_UNSPEC, + TCA_STAB_BASE, + TCA_STAB_DATA, + __TCA_STAB_MAX +}; + +#define TCA_STAB_MAX (__TCA_STAB_MAX - 1) + +/* FIFO section */ + +struct tc_fifo_qopt { + __u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */ +}; + +/* SKBPRIO section */ + +/* + * Priorities go from zero to (SKBPRIO_MAX_PRIORITY - 1). + * SKBPRIO_MAX_PRIORITY should be at least 64 in order for skbprio to be able + * to map one to one the DS field of IPV4 and IPV6 headers. + * Memory allocation grows linearly with SKBPRIO_MAX_PRIORITY. + */ + +#define SKBPRIO_MAX_PRIORITY 64 + +struct tc_skbprio_qopt { + __u32 limit; /* Queue length in packets. */ +}; + +/* PRIO section */ + +#define TCQ_PRIO_BANDS 16 +#define TCQ_MIN_PRIO_BANDS 2 + +struct tc_prio_qopt { + int bands; /* Number of bands */ + __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */ +}; + +/* MULTIQ section */ + +struct tc_multiq_qopt { + __u16 bands; /* Number of bands */ + __u16 max_bands; /* Maximum number of queues */ +}; + +/* PLUG section */ + +#define TCQ_PLUG_BUFFER 0 +#define TCQ_PLUG_RELEASE_ONE 1 +#define TCQ_PLUG_RELEASE_INDEFINITE 2 +#define TCQ_PLUG_LIMIT 3 + +struct tc_plug_qopt { + /* TCQ_PLUG_BUFFER: Inset a plug into the queue and + * buffer any incoming packets + * TCQ_PLUG_RELEASE_ONE: Dequeue packets from queue head + * to beginning of the next plug. + * TCQ_PLUG_RELEASE_INDEFINITE: Dequeue all packets from queue. + * Stop buffering packets until the next TCQ_PLUG_BUFFER + * command is received (just act as a pass-thru queue). + * TCQ_PLUG_LIMIT: Increase/decrease queue size + */ + int action; + __u32 limit; +}; + +/* TBF section */ + +struct tc_tbf_qopt { + struct tc_ratespec rate; + struct tc_ratespec peakrate; + __u32 limit; + __u32 buffer; + __u32 mtu; +}; + +enum { + TCA_TBF_UNSPEC, + TCA_TBF_PARMS, + TCA_TBF_RTAB, + TCA_TBF_PTAB, + TCA_TBF_RATE64, + TCA_TBF_PRATE64, + TCA_TBF_BURST, + TCA_TBF_PBURST, + TCA_TBF_PAD, + __TCA_TBF_MAX, +}; + +#define TCA_TBF_MAX (__TCA_TBF_MAX - 1) + + +/* TEQL section */ + +/* TEQL does not require any parameters */ + +/* SFQ section */ + +struct tc_sfq_qopt { + unsigned quantum; /* Bytes per round allocated to flow */ + int perturb_period; /* Period of hash perturbation */ + __u32 limit; /* Maximal packets in queue */ + unsigned divisor; /* Hash divisor */ + unsigned flows; /* Maximal number of flows */ +}; + +struct tc_sfqred_stats { + __u32 prob_drop; /* Early drops, below max threshold */ + __u32 forced_drop; /* Early drops, after max threshold */ + __u32 prob_mark; /* Marked packets, below max threshold */ + __u32 forced_mark; /* Marked packets, after max threshold */ + __u32 prob_mark_head; /* Marked packets, below max threshold */ + __u32 forced_mark_head;/* Marked packets, after max threshold */ +}; + +struct tc_sfq_qopt_v1 { + struct tc_sfq_qopt v0; + unsigned int depth; /* max number of packets per flow */ + unsigned int headdrop; +/* SFQRED parameters */ + __u32 limit; /* HARD maximal flow queue length (bytes) */ + __u32 qth_min; /* Min average length threshold (bytes) */ + __u32 qth_max; /* Max average length threshold (bytes) */ + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + unsigned char flags; + __u32 max_P; /* probability, high resolution */ +/* SFQRED stats */ + struct tc_sfqred_stats stats; +}; + + +struct tc_sfq_xstats { + __s32 allot; +}; + +/* RED section */ + +enum { + TCA_RED_UNSPEC, + TCA_RED_PARMS, + TCA_RED_STAB, + TCA_RED_MAX_P, + TCA_RED_FLAGS, /* bitfield32 */ + TCA_RED_EARLY_DROP_BLOCK, /* u32 */ + TCA_RED_MARK_BLOCK, /* u32 */ + __TCA_RED_MAX, +}; + +#define TCA_RED_MAX (__TCA_RED_MAX - 1) + +struct tc_red_qopt { + __u32 limit; /* HARD maximal queue length (bytes) */ + __u32 qth_min; /* Min average length threshold (bytes) */ + __u32 qth_max; /* Max average length threshold (bytes) */ + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + + /* This field can be used for flags that a RED-like qdisc has + * historically supported. E.g. when configuring RED, it can be used for + * ECN, HARDDROP and ADAPTATIVE. For SFQ it can be used for ECN, + * HARDDROP. Etc. Because this field has not been validated, and is + * copied back on dump, any bits besides those to which a given qdisc + * has assigned a historical meaning need to be considered for free use + * by userspace tools. + * + * Any further flags need to be passed differently, e.g. through an + * attribute (such as TCA_RED_FLAGS above). Such attribute should allow + * passing both recent and historic flags in one value. + */ + unsigned char flags; +#define TC_RED_ECN 1 +#define TC_RED_HARDDROP 2 +#define TC_RED_ADAPTATIVE 4 +#define TC_RED_NODROP 8 +}; + +#define TC_RED_HISTORIC_FLAGS (TC_RED_ECN | TC_RED_HARDDROP | TC_RED_ADAPTATIVE) + +struct tc_red_xstats { + __u32 early; /* Early drops */ + __u32 pdrop; /* Drops due to queue limits */ + __u32 other; /* Drops due to drop() calls */ + __u32 marked; /* Marked packets */ +}; + +/* GRED section */ + +#define MAX_DPs 16 + +enum { + TCA_GRED_UNSPEC, + TCA_GRED_PARMS, + TCA_GRED_STAB, + TCA_GRED_DPS, + TCA_GRED_MAX_P, + TCA_GRED_LIMIT, + TCA_GRED_VQ_LIST, /* nested TCA_GRED_VQ_ENTRY */ + __TCA_GRED_MAX, +}; + +#define TCA_GRED_MAX (__TCA_GRED_MAX - 1) + +enum { + TCA_GRED_VQ_ENTRY_UNSPEC, + TCA_GRED_VQ_ENTRY, /* nested TCA_GRED_VQ_* */ + __TCA_GRED_VQ_ENTRY_MAX, +}; +#define TCA_GRED_VQ_ENTRY_MAX (__TCA_GRED_VQ_ENTRY_MAX - 1) + +enum { + TCA_GRED_VQ_UNSPEC, + TCA_GRED_VQ_PAD, + TCA_GRED_VQ_DP, /* u32 */ + TCA_GRED_VQ_STAT_BYTES, /* u64 */ + TCA_GRED_VQ_STAT_PACKETS, /* u32 */ + TCA_GRED_VQ_STAT_BACKLOG, /* u32 */ + TCA_GRED_VQ_STAT_PROB_DROP, /* u32 */ + TCA_GRED_VQ_STAT_PROB_MARK, /* u32 */ + TCA_GRED_VQ_STAT_FORCED_DROP, /* u32 */ + TCA_GRED_VQ_STAT_FORCED_MARK, /* u32 */ + TCA_GRED_VQ_STAT_PDROP, /* u32 */ + TCA_GRED_VQ_STAT_OTHER, /* u32 */ + TCA_GRED_VQ_FLAGS, /* u32 */ + __TCA_GRED_VQ_MAX +}; + +#define TCA_GRED_VQ_MAX (__TCA_GRED_VQ_MAX - 1) + +struct tc_gred_qopt { + __u32 limit; /* HARD maximal queue length (bytes) */ + __u32 qth_min; /* Min average length threshold (bytes) */ + __u32 qth_max; /* Max average length threshold (bytes) */ + __u32 DP; /* up to 2^32 DPs */ + __u32 backlog; + __u32 qave; + __u32 forced; + __u32 early; + __u32 other; + __u32 pdrop; + __u8 Wlog; /* log(W) */ + __u8 Plog; /* log(P_max/(qth_max-qth_min)) */ + __u8 Scell_log; /* cell size for idle damping */ + __u8 prio; /* prio of this VQ */ + __u32 packets; + __u32 bytesin; +}; + +/* gred setup */ +struct tc_gred_sopt { + __u32 DPs; + __u32 def_DP; + __u8 grio; + __u8 flags; + __u16 pad1; +}; + +/* CHOKe section */ + +enum { + TCA_CHOKE_UNSPEC, + TCA_CHOKE_PARMS, + TCA_CHOKE_STAB, + TCA_CHOKE_MAX_P, + __TCA_CHOKE_MAX, +}; + +#define TCA_CHOKE_MAX (__TCA_CHOKE_MAX - 1) + +struct tc_choke_qopt { + __u32 limit; /* Hard queue length (packets) */ + __u32 qth_min; /* Min average threshold (packets) */ + __u32 qth_max; /* Max average threshold (packets) */ + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + unsigned char flags; /* see RED flags */ +}; + +struct tc_choke_xstats { + __u32 early; /* Early drops */ + __u32 pdrop; /* Drops due to queue limits */ + __u32 other; /* Drops due to drop() calls */ + __u32 marked; /* Marked packets */ + __u32 matched; /* Drops due to flow match */ +}; + +/* HTB section */ +#define TC_HTB_NUMPRIO 8 +#define TC_HTB_MAXDEPTH 8 +#define TC_HTB_PROTOVER 3 /* the same as HTB and TC's major */ + +struct tc_htb_opt { + struct tc_ratespec rate; + struct tc_ratespec ceil; + __u32 buffer; + __u32 cbuffer; + __u32 quantum; + __u32 level; /* out only */ + __u32 prio; +}; +struct tc_htb_glob { + __u32 version; /* to match HTB/TC */ + __u32 rate2quantum; /* bps->quantum divisor */ + __u32 defcls; /* default class number */ + __u32 debug; /* debug flags */ + + /* stats */ + __u32 direct_pkts; /* count of non shaped packets */ +}; +enum { + TCA_HTB_UNSPEC, + TCA_HTB_PARMS, + TCA_HTB_INIT, + TCA_HTB_CTAB, + TCA_HTB_RTAB, + TCA_HTB_DIRECT_QLEN, + TCA_HTB_RATE64, + TCA_HTB_CEIL64, + TCA_HTB_PAD, + TCA_HTB_OFFLOAD, + __TCA_HTB_MAX, +}; + +#define TCA_HTB_MAX (__TCA_HTB_MAX - 1) + +struct tc_htb_xstats { + __u32 lends; + __u32 borrows; + __u32 giants; /* unused since 'Make HTB scheduler work with TSO.' */ + __s32 tokens; + __s32 ctokens; +}; + +/* HFSC section */ + +struct tc_hfsc_qopt { + __u16 defcls; /* default class */ +}; + +struct tc_service_curve { + __u32 m1; /* slope of the first segment in bps */ + __u32 d; /* x-projection of the first segment in us */ + __u32 m2; /* slope of the second segment in bps */ +}; + +struct tc_hfsc_stats { + __u64 work; /* total work done */ + __u64 rtwork; /* work done by real-time criteria */ + __u32 period; /* current period */ + __u32 level; /* class level in hierarchy */ +}; + +enum { + TCA_HFSC_UNSPEC, + TCA_HFSC_RSC, + TCA_HFSC_FSC, + TCA_HFSC_USC, + __TCA_HFSC_MAX, +}; + +#define TCA_HFSC_MAX (__TCA_HFSC_MAX - 1) + + +/* CBQ section */ + +#define TC_CBQ_MAXPRIO 8 +#define TC_CBQ_MAXLEVEL 8 +#define TC_CBQ_DEF_EWMA 5 + +struct tc_cbq_lssopt { + unsigned char change; + unsigned char flags; +#define TCF_CBQ_LSS_BOUNDED 1 +#define TCF_CBQ_LSS_ISOLATED 2 + unsigned char ewma_log; + unsigned char level; +#define TCF_CBQ_LSS_FLAGS 1 +#define TCF_CBQ_LSS_EWMA 2 +#define TCF_CBQ_LSS_MAXIDLE 4 +#define TCF_CBQ_LSS_MINIDLE 8 +#define TCF_CBQ_LSS_OFFTIME 0x10 +#define TCF_CBQ_LSS_AVPKT 0x20 + __u32 maxidle; + __u32 minidle; + __u32 offtime; + __u32 avpkt; +}; + +struct tc_cbq_wrropt { + unsigned char flags; + unsigned char priority; + unsigned char cpriority; + unsigned char __reserved; + __u32 allot; + __u32 weight; +}; + +struct tc_cbq_ovl { + unsigned char strategy; +#define TC_CBQ_OVL_CLASSIC 0 +#define TC_CBQ_OVL_DELAY 1 +#define TC_CBQ_OVL_LOWPRIO 2 +#define TC_CBQ_OVL_DROP 3 +#define TC_CBQ_OVL_RCLASSIC 4 + unsigned char priority2; + __u16 pad; + __u32 penalty; +}; + +struct tc_cbq_police { + unsigned char police; + unsigned char __res1; + unsigned short __res2; +}; + +struct tc_cbq_fopt { + __u32 split; + __u32 defmap; + __u32 defchange; +}; + +struct tc_cbq_xstats { + __u32 borrows; + __u32 overactions; + __s32 avgidle; + __s32 undertime; +}; + +enum { + TCA_CBQ_UNSPEC, + TCA_CBQ_LSSOPT, + TCA_CBQ_WRROPT, + TCA_CBQ_FOPT, + TCA_CBQ_OVL_STRATEGY, + TCA_CBQ_RATE, + TCA_CBQ_RTAB, + TCA_CBQ_POLICE, + __TCA_CBQ_MAX, +}; + +#define TCA_CBQ_MAX (__TCA_CBQ_MAX - 1) + +/* dsmark section */ + +enum { + TCA_DSMARK_UNSPEC, + TCA_DSMARK_INDICES, + TCA_DSMARK_DEFAULT_INDEX, + TCA_DSMARK_SET_TC_INDEX, + TCA_DSMARK_MASK, + TCA_DSMARK_VALUE, + __TCA_DSMARK_MAX, +}; + +#define TCA_DSMARK_MAX (__TCA_DSMARK_MAX - 1) + +/* ATM section */ + +enum { + TCA_ATM_UNSPEC, + TCA_ATM_FD, /* file/socket descriptor */ + TCA_ATM_PTR, /* pointer to descriptor - later */ + TCA_ATM_HDR, /* LL header */ + TCA_ATM_EXCESS, /* excess traffic class (0 for CLP) */ + TCA_ATM_ADDR, /* PVC address (for output only) */ + TCA_ATM_STATE, /* VC state (ATM_VS_*; for output only) */ + __TCA_ATM_MAX, +}; + +#define TCA_ATM_MAX (__TCA_ATM_MAX - 1) + +/* Network emulator */ + +enum { + TCA_NETEM_UNSPEC, + TCA_NETEM_CORR, + TCA_NETEM_DELAY_DIST, + TCA_NETEM_REORDER, + TCA_NETEM_CORRUPT, + TCA_NETEM_LOSS, + TCA_NETEM_RATE, + TCA_NETEM_ECN, + TCA_NETEM_RATE64, + TCA_NETEM_PAD, + TCA_NETEM_LATENCY64, + TCA_NETEM_JITTER64, + TCA_NETEM_SLOT, + TCA_NETEM_SLOT_DIST, + __TCA_NETEM_MAX, +}; + +#define TCA_NETEM_MAX (__TCA_NETEM_MAX - 1) + +struct tc_netem_qopt { + __u32 latency; /* added delay (us) */ + __u32 limit; /* fifo limit (packets) */ + __u32 loss; /* random packet loss (0=none ~0=100%) */ + __u32 gap; /* re-ordering gap (0 for none) */ + __u32 duplicate; /* random packet dup (0=none ~0=100%) */ + __u32 jitter; /* random jitter in latency (us) */ +}; + +struct tc_netem_corr { + __u32 delay_corr; /* delay correlation */ + __u32 loss_corr; /* packet loss correlation */ + __u32 dup_corr; /* duplicate correlation */ +}; + +struct tc_netem_reorder { + __u32 probability; + __u32 correlation; +}; + +struct tc_netem_corrupt { + __u32 probability; + __u32 correlation; +}; + +struct tc_netem_rate { + __u32 rate; /* byte/s */ + __s32 packet_overhead; + __u32 cell_size; + __s32 cell_overhead; +}; + +struct tc_netem_slot { + __s64 min_delay; /* nsec */ + __s64 max_delay; + __s32 max_packets; + __s32 max_bytes; + __s64 dist_delay; /* nsec */ + __s64 dist_jitter; /* nsec */ +}; + +enum { + NETEM_LOSS_UNSPEC, + NETEM_LOSS_GI, /* General Intuitive - 4 state model */ + NETEM_LOSS_GE, /* Gilbert Elliot models */ + __NETEM_LOSS_MAX +}; +#define NETEM_LOSS_MAX (__NETEM_LOSS_MAX - 1) + +/* State transition probabilities for 4 state model */ +struct tc_netem_gimodel { + __u32 p13; + __u32 p31; + __u32 p32; + __u32 p14; + __u32 p23; +}; + +/* Gilbert-Elliot models */ +struct tc_netem_gemodel { + __u32 p; + __u32 r; + __u32 h; + __u32 k1; +}; + +#define NETEM_DIST_SCALE 8192 +#define NETEM_DIST_MAX 16384 + +/* DRR */ + +enum { + TCA_DRR_UNSPEC, + TCA_DRR_QUANTUM, + __TCA_DRR_MAX +}; + +#define TCA_DRR_MAX (__TCA_DRR_MAX - 1) + +struct tc_drr_stats { + __u32 deficit; +}; + +/* MQPRIO */ +#define TC_QOPT_BITMASK 15 +#define TC_QOPT_MAX_QUEUE 16 + +enum { + TC_MQPRIO_HW_OFFLOAD_NONE, /* no offload requested */ + TC_MQPRIO_HW_OFFLOAD_TCS, /* offload TCs, no queue counts */ + __TC_MQPRIO_HW_OFFLOAD_MAX +}; + +#define TC_MQPRIO_HW_OFFLOAD_MAX (__TC_MQPRIO_HW_OFFLOAD_MAX - 1) + +enum { + TC_MQPRIO_MODE_DCB, + TC_MQPRIO_MODE_CHANNEL, + __TC_MQPRIO_MODE_MAX +}; + +#define __TC_MQPRIO_MODE_MAX (__TC_MQPRIO_MODE_MAX - 1) + +enum { + TC_MQPRIO_SHAPER_DCB, + TC_MQPRIO_SHAPER_BW_RATE, /* Add new shapers below */ + __TC_MQPRIO_SHAPER_MAX +}; + +#define __TC_MQPRIO_SHAPER_MAX (__TC_MQPRIO_SHAPER_MAX - 1) + +enum { + TC_FP_EXPRESS = 1, + TC_FP_PREEMPTIBLE = 2, +}; + +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_TC_ENTRY_UNSPEC, + TCA_MQPRIO_TC_ENTRY_INDEX, /* u32 */ + TCA_MQPRIO_TC_ENTRY_FP, /* u32 */ + + /* add new constants above here */ + __TCA_MQPRIO_TC_ENTRY_CNT, + TCA_MQPRIO_TC_ENTRY_MAX = (__TCA_MQPRIO_TC_ENTRY_CNT - 1) +}; + +enum { + TCA_MQPRIO_UNSPEC, + TCA_MQPRIO_MODE, + TCA_MQPRIO_SHAPER, + TCA_MQPRIO_MIN_RATE64, + TCA_MQPRIO_MAX_RATE64, + TCA_MQPRIO_TC_ENTRY, + __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 */ + +#define FQ_CODEL_QUANTUM_MAX (1 << 20) + +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_CE_THRESHOLD_SELECTOR, + TCA_FQ_CODEL_CE_THRESHOLD_MASK, + __TCA_FQ_CODEL_MAX +}; + +#define TCA_FQ_CODEL_MAX (__TCA_FQ_CODEL_MAX - 1) + +enum { + TCA_FQ_CODEL_XSTATS_QDISC, + TCA_FQ_CODEL_XSTATS_CLASS, +}; + +struct tc_fq_codel_qd_stats { + __u32 maxpacket; /* largest packet we've seen so far */ + __u32 drop_overlimit; /* number of time max qdisc + * packet limit was hit + */ + __u32 ecn_mark; /* number of packets we ECN marked + * instead of being dropped + */ + __u32 new_flow_count; /* number of time packets + * created a 'new flow' + */ + __u32 new_flows_len; /* count of flows in new list */ + __u32 old_flows_len; /* count of flows in old list */ + __u32 ce_mark; /* packets above ce_threshold */ + __u32 memory_usage; /* in bytes */ + __u32 drop_overmemory; +}; + +struct tc_fq_codel_cl_stats { + __s32 deficit; + __u32 ldelay; /* in-queue delay seen by most recently + * dequeued packet + */ + __u32 count; + __u32 lastcount; + __u32 dropping; + __s32 drop_next; +}; + +struct tc_fq_codel_xstats { + __u32 type; + union { + struct tc_fq_codel_qd_stats qdisc_stats; + struct tc_fq_codel_cl_stats class_stats; + }; +}; + +/* FQ */ + +enum { + TCA_FQ_UNSPEC, + + TCA_FQ_PLIMIT, /* limit of total number of packets in queue */ + + TCA_FQ_FLOW_PLIMIT, /* limit of packets per flow */ + + TCA_FQ_QUANTUM, /* RR quantum */ + + TCA_FQ_INITIAL_QUANTUM, /* RR quantum for new flow */ + + TCA_FQ_RATE_ENABLE, /* enable/disable rate limiting */ + + TCA_FQ_FLOW_DEFAULT_RATE,/* obsolete, do not use */ + + TCA_FQ_FLOW_MAX_RATE, /* per flow max rate */ + + TCA_FQ_BUCKETS_LOG, /* log2(number of buckets) */ + + TCA_FQ_FLOW_REFILL_DELAY, /* flow credit refill delay in usec */ + + TCA_FQ_ORPHAN_MASK, /* mask applied to orphaned skb hashes */ + + TCA_FQ_LOW_RATE_THRESHOLD, /* per packet delay under this rate */ + + TCA_FQ_CE_THRESHOLD, /* DCTCP-like CE-marking threshold */ + + TCA_FQ_TIMER_SLACK, /* timer slack */ + + TCA_FQ_HORIZON, /* time horizon in us */ + + TCA_FQ_HORIZON_DROP, /* drop packets beyond horizon, or cap their EDT */ + + __TCA_FQ_MAX +}; + +#define TCA_FQ_MAX (__TCA_FQ_MAX - 1) + +struct tc_fq_qd_stats { + __u64 gc_flows; + __u64 highprio_packets; + __u64 tcp_retrans; + __u64 throttled; + __u64 flows_plimit; + __u64 pkts_too_long; + __u64 allocation_errors; + __s64 time_next_delayed_flow; + __u32 flows; + __u32 inactive_flows; + __u32 throttled_flows; + __u32 unthrottle_latency_ns; + __u64 ce_mark; /* packets above ce_threshold */ + __u64 horizon_drops; + __u64 horizon_caps; +}; + +/* Heavy-Hitter Filter */ + +enum { + TCA_HHF_UNSPEC, + TCA_HHF_BACKLOG_LIMIT, + TCA_HHF_QUANTUM, + TCA_HHF_HH_FLOWS_LIMIT, + TCA_HHF_RESET_TIMEOUT, + TCA_HHF_ADMIT_BYTES, + TCA_HHF_EVICT_TIMEOUT, + TCA_HHF_NON_HH_WEIGHT, + __TCA_HHF_MAX +}; + +#define TCA_HHF_MAX (__TCA_HHF_MAX - 1) + +struct tc_hhf_xstats { + __u32 drop_overlimit; /* number of times max qdisc packet limit + * was hit + */ + __u32 hh_overlimit; /* number of times max heavy-hitters was hit */ + __u32 hh_tot_count; /* number of captured heavy-hitters so far */ + __u32 hh_cur_count; /* number of current heavy-hitters */ +}; + +/* PIE */ +enum { + TCA_PIE_UNSPEC, + TCA_PIE_TARGET, + TCA_PIE_LIMIT, + TCA_PIE_TUPDATE, + TCA_PIE_ALPHA, + TCA_PIE_BETA, + TCA_PIE_ECN, + TCA_PIE_BYTEMODE, + TCA_PIE_DQ_RATE_ESTIMATOR, + __TCA_PIE_MAX +}; +#define TCA_PIE_MAX (__TCA_PIE_MAX - 1) + +struct tc_pie_xstats { + __u64 prob; /* current probability */ + __u32 delay; /* current delay in ms */ + __u32 avg_dq_rate; /* current average dq_rate in + * bits/pie_time + */ + __u32 dq_rate_estimating; /* is avg_dq_rate being calculated? */ + __u32 packets_in; /* total number of packets enqueued */ + __u32 dropped; /* packets dropped due to pie_action */ + __u32 overlimit; /* dropped due to lack of space + * in queue + */ + __u32 maxq; /* maximum queue size */ + __u32 ecn_mark; /* packets marked with ecn*/ +}; + +/* FQ PIE */ +enum { + TCA_FQ_PIE_UNSPEC, + TCA_FQ_PIE_LIMIT, + TCA_FQ_PIE_FLOWS, + TCA_FQ_PIE_TARGET, + TCA_FQ_PIE_TUPDATE, + TCA_FQ_PIE_ALPHA, + TCA_FQ_PIE_BETA, + TCA_FQ_PIE_QUANTUM, + TCA_FQ_PIE_MEMORY_LIMIT, + TCA_FQ_PIE_ECN_PROB, + TCA_FQ_PIE_ECN, + TCA_FQ_PIE_BYTEMODE, + TCA_FQ_PIE_DQ_RATE_ESTIMATOR, + __TCA_FQ_PIE_MAX +}; +#define TCA_FQ_PIE_MAX (__TCA_FQ_PIE_MAX - 1) + +struct tc_fq_pie_xstats { + __u32 packets_in; /* total number of packets enqueued */ + __u32 dropped; /* packets dropped due to fq_pie_action */ + __u32 overlimit; /* dropped due to lack of space in queue */ + __u32 overmemory; /* dropped due to lack of memory in queue */ + __u32 ecn_mark; /* packets marked with ecn */ + __u32 new_flow_count; /* count of new flows created by packets */ + __u32 new_flows_len; /* count of flows in new list */ + __u32 old_flows_len; /* count of flows in old list */ + __u32 memory_usage; /* total memory across all queues */ +}; + +/* CBS */ +struct tc_cbs_qopt { + __u8 offload; + __u8 _pad[3]; + __s32 hicredit; + __s32 locredit; + __s32 idleslope; + __s32 sendslope; +}; + +enum { + TCA_CBS_UNSPEC, + TCA_CBS_PARMS, + __TCA_CBS_MAX, +}; + +#define TCA_CBS_MAX (__TCA_CBS_MAX - 1) + + +/* ETF */ +struct tc_etf_qopt { + __s32 delta; + __s32 clockid; + __u32 flags; +#define TC_ETF_DEADLINE_MODE_ON _BITUL(0) +#define TC_ETF_OFFLOAD_ON _BITUL(1) +#define TC_ETF_SKIP_SOCK_CHECK _BITUL(2) +}; + +enum { + TCA_ETF_UNSPEC, + TCA_ETF_PARMS, + __TCA_ETF_MAX, +}; + +#define TCA_ETF_MAX (__TCA_ETF_MAX - 1) + + +/* CAKE */ +enum { + TCA_CAKE_UNSPEC, + TCA_CAKE_PAD, + TCA_CAKE_BASE_RATE64, + TCA_CAKE_DIFFSERV_MODE, + TCA_CAKE_ATM, + TCA_CAKE_FLOW_MODE, + TCA_CAKE_OVERHEAD, + TCA_CAKE_RTT, + TCA_CAKE_TARGET, + TCA_CAKE_AUTORATE, + TCA_CAKE_MEMORY, + TCA_CAKE_NAT, + TCA_CAKE_RAW, + TCA_CAKE_WASH, + TCA_CAKE_MPU, + TCA_CAKE_INGRESS, + TCA_CAKE_ACK_FILTER, + TCA_CAKE_SPLIT_GSO, + TCA_CAKE_FWMARK, + __TCA_CAKE_MAX +}; +#define TCA_CAKE_MAX (__TCA_CAKE_MAX - 1) + +enum { + __TCA_CAKE_STATS_INVALID, + TCA_CAKE_STATS_PAD, + TCA_CAKE_STATS_CAPACITY_ESTIMATE64, + TCA_CAKE_STATS_MEMORY_LIMIT, + TCA_CAKE_STATS_MEMORY_USED, + TCA_CAKE_STATS_AVG_NETOFF, + TCA_CAKE_STATS_MIN_NETLEN, + TCA_CAKE_STATS_MAX_NETLEN, + TCA_CAKE_STATS_MIN_ADJLEN, + TCA_CAKE_STATS_MAX_ADJLEN, + TCA_CAKE_STATS_TIN_STATS, + TCA_CAKE_STATS_DEFICIT, + TCA_CAKE_STATS_COBALT_COUNT, + TCA_CAKE_STATS_DROPPING, + TCA_CAKE_STATS_DROP_NEXT_US, + TCA_CAKE_STATS_P_DROP, + TCA_CAKE_STATS_BLUE_TIMER_US, + __TCA_CAKE_STATS_MAX +}; +#define TCA_CAKE_STATS_MAX (__TCA_CAKE_STATS_MAX - 1) + +enum { + __TCA_CAKE_TIN_STATS_INVALID, + TCA_CAKE_TIN_STATS_PAD, + TCA_CAKE_TIN_STATS_SENT_PACKETS, + TCA_CAKE_TIN_STATS_SENT_BYTES64, + TCA_CAKE_TIN_STATS_DROPPED_PACKETS, + TCA_CAKE_TIN_STATS_DROPPED_BYTES64, + TCA_CAKE_TIN_STATS_ACKS_DROPPED_PACKETS, + TCA_CAKE_TIN_STATS_ACKS_DROPPED_BYTES64, + TCA_CAKE_TIN_STATS_ECN_MARKED_PACKETS, + TCA_CAKE_TIN_STATS_ECN_MARKED_BYTES64, + TCA_CAKE_TIN_STATS_BACKLOG_PACKETS, + TCA_CAKE_TIN_STATS_BACKLOG_BYTES, + TCA_CAKE_TIN_STATS_THRESHOLD_RATE64, + TCA_CAKE_TIN_STATS_TARGET_US, + TCA_CAKE_TIN_STATS_INTERVAL_US, + TCA_CAKE_TIN_STATS_WAY_INDIRECT_HITS, + TCA_CAKE_TIN_STATS_WAY_MISSES, + TCA_CAKE_TIN_STATS_WAY_COLLISIONS, + TCA_CAKE_TIN_STATS_PEAK_DELAY_US, + TCA_CAKE_TIN_STATS_AVG_DELAY_US, + TCA_CAKE_TIN_STATS_BASE_DELAY_US, + TCA_CAKE_TIN_STATS_SPARSE_FLOWS, + TCA_CAKE_TIN_STATS_BULK_FLOWS, + TCA_CAKE_TIN_STATS_UNRESPONSIVE_FLOWS, + TCA_CAKE_TIN_STATS_MAX_SKBLEN, + TCA_CAKE_TIN_STATS_FLOW_QUANTUM, + __TCA_CAKE_TIN_STATS_MAX +}; +#define TCA_CAKE_TIN_STATS_MAX (__TCA_CAKE_TIN_STATS_MAX - 1) +#define TC_CAKE_MAX_TINS (8) + +enum { + CAKE_FLOW_NONE = 0, + CAKE_FLOW_SRC_IP, + CAKE_FLOW_DST_IP, + CAKE_FLOW_HOSTS, /* = CAKE_FLOW_SRC_IP | CAKE_FLOW_DST_IP */ + CAKE_FLOW_FLOWS, + CAKE_FLOW_DUAL_SRC, /* = CAKE_FLOW_SRC_IP | CAKE_FLOW_FLOWS */ + CAKE_FLOW_DUAL_DST, /* = CAKE_FLOW_DST_IP | CAKE_FLOW_FLOWS */ + CAKE_FLOW_TRIPLE, /* = CAKE_FLOW_HOSTS | CAKE_FLOW_FLOWS */ + CAKE_FLOW_MAX, +}; + +enum { + CAKE_DIFFSERV_DIFFSERV3 = 0, + CAKE_DIFFSERV_DIFFSERV4, + CAKE_DIFFSERV_DIFFSERV8, + CAKE_DIFFSERV_BESTEFFORT, + CAKE_DIFFSERV_PRECEDENCE, + CAKE_DIFFSERV_MAX +}; + +enum { + CAKE_ACK_NONE = 0, + CAKE_ACK_FILTER, + CAKE_ACK_AGGRESSIVE, + CAKE_ACK_MAX +}; + +enum { + CAKE_ATM_NONE = 0, + CAKE_ATM_ATM, + CAKE_ATM_PTM, + CAKE_ATM_MAX +}; + + +/* TAPRIO */ +enum { + TC_TAPRIO_CMD_SET_GATES = 0x00, + TC_TAPRIO_CMD_SET_AND_HOLD = 0x01, + TC_TAPRIO_CMD_SET_AND_RELEASE = 0x02, +}; + +enum { + TCA_TAPRIO_SCHED_ENTRY_UNSPEC, + TCA_TAPRIO_SCHED_ENTRY_INDEX, /* u32 */ + TCA_TAPRIO_SCHED_ENTRY_CMD, /* u8 */ + TCA_TAPRIO_SCHED_ENTRY_GATE_MASK, /* u32 */ + TCA_TAPRIO_SCHED_ENTRY_INTERVAL, /* u32 */ + __TCA_TAPRIO_SCHED_ENTRY_MAX, +}; +#define TCA_TAPRIO_SCHED_ENTRY_MAX (__TCA_TAPRIO_SCHED_ENTRY_MAX - 1) + +/* The format for schedule entry list is: + * [TCA_TAPRIO_SCHED_ENTRY_LIST] + * [TCA_TAPRIO_SCHED_ENTRY] + * [TCA_TAPRIO_SCHED_ENTRY_CMD] + * [TCA_TAPRIO_SCHED_ENTRY_GATES] + * [TCA_TAPRIO_SCHED_ENTRY_INTERVAL] + */ +enum { + TCA_TAPRIO_SCHED_UNSPEC, + TCA_TAPRIO_SCHED_ENTRY, + __TCA_TAPRIO_SCHED_MAX, +}; + +#define TCA_TAPRIO_SCHED_MAX (__TCA_TAPRIO_SCHED_MAX - 1) + +/* The format for the admin sched (dump only): + * [TCA_TAPRIO_SCHED_ADMIN_SCHED] + * [TCA_TAPRIO_ATTR_SCHED_BASE_TIME] + * [TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST] + * [TCA_TAPRIO_ATTR_SCHED_ENTRY] + * [TCA_TAPRIO_ATTR_SCHED_ENTRY_CMD] + * [TCA_TAPRIO_ATTR_SCHED_ENTRY_GATES] + * [TCA_TAPRIO_ATTR_SCHED_ENTRY_INTERVAL] + */ + +#define TCA_TAPRIO_ATTR_FLAG_TXTIME_ASSIST _BITUL(0) +#define TCA_TAPRIO_ATTR_FLAG_FULL_OFFLOAD _BITUL(1) + +enum { + TCA_TAPRIO_TC_ENTRY_UNSPEC, + TCA_TAPRIO_TC_ENTRY_INDEX, /* u32 */ + TCA_TAPRIO_TC_ENTRY_MAX_SDU, /* u32 */ + TCA_TAPRIO_TC_ENTRY_FP, /* u32 */ + + /* add new constants above here */ + __TCA_TAPRIO_TC_ENTRY_CNT, + TCA_TAPRIO_TC_ENTRY_MAX = (__TCA_TAPRIO_TC_ENTRY_CNT - 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_TC_ENTRY, /* nest */ + __TCA_TAPRIO_ATTR_MAX, +}; + +#define TCA_TAPRIO_ATTR_MAX (__TCA_TAPRIO_ATTR_MAX - 1) + +/* ETS */ + +#define TCQ_ETS_MAX_BANDS 16 + +enum { + TCA_ETS_UNSPEC, + TCA_ETS_NBANDS, /* u8 */ + TCA_ETS_NSTRICT, /* u8 */ + TCA_ETS_QUANTA, /* nested TCA_ETS_QUANTA_BAND */ + TCA_ETS_QUANTA_BAND, /* u32 */ + TCA_ETS_PRIOMAP, /* nested TCA_ETS_PRIOMAP_BAND */ + TCA_ETS_PRIOMAP_BAND, /* u8 */ + __TCA_ETS_MAX, +}; + +#define TCA_ETS_MAX (__TCA_ETS_MAX - 1) + +#endif diff --git a/libnl/include/linux-private/linux/posix_types.h b/libnl/include/linux-private/linux/posix_types.h new file mode 100644 index 0000000..9a7a740 --- /dev/null +++ b/libnl/include/linux-private/linux/posix_types.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_POSIX_TYPES_H +#define _LINUX_POSIX_TYPES_H + +#include + +/* + * This allows for 1024 file descriptors: if NR_OPEN is ever grown + * beyond that you'll have to change this too. But 1024 fd's seem to be + * enough even for such "real" unices like OSF/1, so hopefully this is + * one limit that doesn't have to be changed [again]. + * + * Note that POSIX wants the FD_CLEAR(fd,fdsetp) defines to be in + * (and thus ) - but this is a more logical + * place for them. Solved by having dummy defines in . + */ + +/* + * This macro may have been defined in . But we always + * use the one here. + */ +#undef __FD_SETSIZE +#define __FD_SETSIZE 1024 + +typedef struct { + unsigned long fds_bits[__FD_SETSIZE / (8 * sizeof(long))]; +} __kernel_fd_set; + +/* Type of a signal handler. */ +typedef void (*__kernel_sighandler_t)(int); + +/* Type of a SYSV IPC key. */ +typedef int __kernel_key_t; +typedef int __kernel_mqd_t; + +#include + +#endif /* _LINUX_POSIX_TYPES_H */ diff --git a/libnl/include/linux-private/linux/rtnetlink.h b/libnl/include/linux-private/linux/rtnetlink.h new file mode 100644 index 0000000..2132e94 --- /dev/null +++ b/libnl/include/linux-private/linux/rtnetlink.h @@ -0,0 +1,826 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_RTNETLINK_H +#define __LINUX_RTNETLINK_H + +#include +#include +#include +#include +#include + +/* rtnetlink families. Values up to 127 are reserved for real address + * families, values above 128 may be used arbitrarily. + */ +#define RTNL_FAMILY_IPMR 128 +#define RTNL_FAMILY_IP6MR 129 +#define RTNL_FAMILY_MAX 129 + +/**** + * Routing/neighbour discovery messages. + ****/ + +/* Types of messages */ + +enum { + RTM_BASE = 16, +#define RTM_BASE RTM_BASE + + RTM_NEWLINK = 16, +#define RTM_NEWLINK RTM_NEWLINK + RTM_DELLINK, +#define RTM_DELLINK RTM_DELLINK + RTM_GETLINK, +#define RTM_GETLINK RTM_GETLINK + RTM_SETLINK, +#define RTM_SETLINK RTM_SETLINK + + RTM_NEWADDR = 20, +#define RTM_NEWADDR RTM_NEWADDR + RTM_DELADDR, +#define RTM_DELADDR RTM_DELADDR + RTM_GETADDR, +#define RTM_GETADDR RTM_GETADDR + + RTM_NEWROUTE = 24, +#define RTM_NEWROUTE RTM_NEWROUTE + RTM_DELROUTE, +#define RTM_DELROUTE RTM_DELROUTE + RTM_GETROUTE, +#define RTM_GETROUTE RTM_GETROUTE + + RTM_NEWNEIGH = 28, +#define RTM_NEWNEIGH RTM_NEWNEIGH + RTM_DELNEIGH, +#define RTM_DELNEIGH RTM_DELNEIGH + RTM_GETNEIGH, +#define RTM_GETNEIGH RTM_GETNEIGH + + RTM_NEWRULE = 32, +#define RTM_NEWRULE RTM_NEWRULE + RTM_DELRULE, +#define RTM_DELRULE RTM_DELRULE + RTM_GETRULE, +#define RTM_GETRULE RTM_GETRULE + + RTM_NEWQDISC = 36, +#define RTM_NEWQDISC RTM_NEWQDISC + RTM_DELQDISC, +#define RTM_DELQDISC RTM_DELQDISC + RTM_GETQDISC, +#define RTM_GETQDISC RTM_GETQDISC + + RTM_NEWTCLASS = 40, +#define RTM_NEWTCLASS RTM_NEWTCLASS + RTM_DELTCLASS, +#define RTM_DELTCLASS RTM_DELTCLASS + RTM_GETTCLASS, +#define RTM_GETTCLASS RTM_GETTCLASS + + RTM_NEWTFILTER = 44, +#define RTM_NEWTFILTER RTM_NEWTFILTER + RTM_DELTFILTER, +#define RTM_DELTFILTER RTM_DELTFILTER + RTM_GETTFILTER, +#define RTM_GETTFILTER RTM_GETTFILTER + + RTM_NEWACTION = 48, +#define RTM_NEWACTION RTM_NEWACTION + RTM_DELACTION, +#define RTM_DELACTION RTM_DELACTION + RTM_GETACTION, +#define RTM_GETACTION RTM_GETACTION + + RTM_NEWPREFIX = 52, +#define RTM_NEWPREFIX RTM_NEWPREFIX + + RTM_GETMULTICAST = 58, +#define RTM_GETMULTICAST RTM_GETMULTICAST + + RTM_GETANYCAST = 62, +#define RTM_GETANYCAST RTM_GETANYCAST + + RTM_NEWNEIGHTBL = 64, +#define RTM_NEWNEIGHTBL RTM_NEWNEIGHTBL + RTM_GETNEIGHTBL = 66, +#define RTM_GETNEIGHTBL RTM_GETNEIGHTBL + RTM_SETNEIGHTBL, +#define RTM_SETNEIGHTBL RTM_SETNEIGHTBL + + RTM_NEWNDUSEROPT = 68, +#define RTM_NEWNDUSEROPT RTM_NEWNDUSEROPT + + RTM_NEWADDRLABEL = 72, +#define RTM_NEWADDRLABEL RTM_NEWADDRLABEL + RTM_DELADDRLABEL, +#define RTM_DELADDRLABEL RTM_DELADDRLABEL + RTM_GETADDRLABEL, +#define RTM_GETADDRLABEL RTM_GETADDRLABEL + + RTM_GETDCB = 78, +#define RTM_GETDCB RTM_GETDCB + RTM_SETDCB, +#define RTM_SETDCB RTM_SETDCB + + RTM_NEWNETCONF = 80, +#define RTM_NEWNETCONF RTM_NEWNETCONF + RTM_DELNETCONF, +#define RTM_DELNETCONF RTM_DELNETCONF + RTM_GETNETCONF = 82, +#define RTM_GETNETCONF RTM_GETNETCONF + + RTM_NEWMDB = 84, +#define RTM_NEWMDB RTM_NEWMDB + RTM_DELMDB = 85, +#define RTM_DELMDB RTM_DELMDB + RTM_GETMDB = 86, +#define RTM_GETMDB RTM_GETMDB + + RTM_NEWNSID = 88, +#define RTM_NEWNSID RTM_NEWNSID + RTM_DELNSID = 89, +#define RTM_DELNSID RTM_DELNSID + RTM_GETNSID = 90, +#define RTM_GETNSID RTM_GETNSID + + RTM_NEWSTATS = 92, +#define RTM_NEWSTATS RTM_NEWSTATS + RTM_GETSTATS = 94, +#define RTM_GETSTATS RTM_GETSTATS + RTM_SETSTATS, +#define RTM_SETSTATS RTM_SETSTATS + + RTM_NEWCACHEREPORT = 96, +#define RTM_NEWCACHEREPORT RTM_NEWCACHEREPORT + + RTM_NEWCHAIN = 100, +#define RTM_NEWCHAIN RTM_NEWCHAIN + RTM_DELCHAIN, +#define RTM_DELCHAIN RTM_DELCHAIN + RTM_GETCHAIN, +#define RTM_GETCHAIN RTM_GETCHAIN + + RTM_NEWNEXTHOP = 104, +#define RTM_NEWNEXTHOP RTM_NEWNEXTHOP + RTM_DELNEXTHOP, +#define RTM_DELNEXTHOP RTM_DELNEXTHOP + RTM_GETNEXTHOP, +#define RTM_GETNEXTHOP RTM_GETNEXTHOP + + RTM_NEWLINKPROP = 108, +#define RTM_NEWLINKPROP RTM_NEWLINKPROP + RTM_DELLINKPROP, +#define RTM_DELLINKPROP RTM_DELLINKPROP + RTM_GETLINKPROP, +#define RTM_GETLINKPROP RTM_GETLINKPROP + + RTM_NEWVLAN = 112, +#define RTM_NEWNVLAN RTM_NEWVLAN + RTM_DELVLAN, +#define RTM_DELVLAN RTM_DELVLAN + RTM_GETVLAN, +#define RTM_GETVLAN RTM_GETVLAN + + RTM_NEWNEXTHOPBUCKET = 116, +#define RTM_NEWNEXTHOPBUCKET RTM_NEWNEXTHOPBUCKET + RTM_DELNEXTHOPBUCKET, +#define RTM_DELNEXTHOPBUCKET RTM_DELNEXTHOPBUCKET + RTM_GETNEXTHOPBUCKET, +#define RTM_GETNEXTHOPBUCKET RTM_GETNEXTHOPBUCKET + + RTM_NEWTUNNEL = 120, +#define RTM_NEWTUNNEL RTM_NEWTUNNEL + RTM_DELTUNNEL, +#define RTM_DELTUNNEL RTM_DELTUNNEL + RTM_GETTUNNEL, +#define RTM_GETTUNNEL RTM_GETTUNNEL + + __RTM_MAX, +#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) +}; + +#define RTM_NR_MSGTYPES (RTM_MAX + 1 - RTM_BASE) +#define RTM_NR_FAMILIES (RTM_NR_MSGTYPES >> 2) +#define RTM_FAM(cmd) (((cmd) - RTM_BASE) >> 2) + +/* + Generic structure for encapsulation of optional route information. + It is reminiscent of sockaddr, but with sa_family replaced + with attribute type. + */ + +struct rtattr { + unsigned short rta_len; + unsigned short rta_type; +}; + +/* Macros to handle rtattributes */ + +#define RTA_ALIGNTO 4U +#define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) ) +#define RTA_OK(rta,len) ((len) >= (int)sizeof(struct rtattr) && \ + (rta)->rta_len >= sizeof(struct rtattr) && \ + (rta)->rta_len <= (len)) +#define RTA_NEXT(rta,attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \ + (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len))) +#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) +#define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len)) +#define RTA_DATA(rta) ((void*)(((char*)(rta)) + RTA_LENGTH(0))) +#define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0)) + + + + +/****************************************************************************** + * Definitions used in routing table administration. + ****/ + +struct rtmsg { + unsigned char rtm_family; + unsigned char rtm_dst_len; + unsigned char rtm_src_len; + unsigned char rtm_tos; + + unsigned char rtm_table; /* Routing table id */ + unsigned char rtm_protocol; /* Routing protocol; see below */ + unsigned char rtm_scope; /* See below */ + unsigned char rtm_type; /* See below */ + + unsigned rtm_flags; +}; + +/* rtm_type */ + +enum { + RTN_UNSPEC, + RTN_UNICAST, /* Gateway or direct route */ + RTN_LOCAL, /* Accept locally */ + RTN_BROADCAST, /* Accept locally as broadcast, + send as broadcast */ + RTN_ANYCAST, /* Accept locally as broadcast, + but send as unicast */ + RTN_MULTICAST, /* Multicast route */ + RTN_BLACKHOLE, /* Drop */ + RTN_UNREACHABLE, /* Destination is unreachable */ + RTN_PROHIBIT, /* Administratively prohibited */ + RTN_THROW, /* Not in this table */ + RTN_NAT, /* Translate this address */ + RTN_XRESOLVE, /* Use external resolver */ + __RTN_MAX +}; + +#define RTN_MAX (__RTN_MAX - 1) + + +/* rtm_protocol */ + +#define RTPROT_UNSPEC 0 +#define RTPROT_REDIRECT 1 /* Route installed by ICMP redirects; + not used by current IPv4 */ +#define RTPROT_KERNEL 2 /* Route installed by kernel */ +#define RTPROT_BOOT 3 /* Route installed during boot */ +#define RTPROT_STATIC 4 /* Route installed by administrator */ + +/* Values of protocol >= RTPROT_STATIC are not interpreted by kernel; + they are just passed from user and back as is. + It will be used by hypothetical multiple routing daemons. + Note that protocol values should be standardized in order to + avoid conflicts. + */ + +#define RTPROT_GATED 8 /* Apparently, GateD */ +#define RTPROT_RA 9 /* RDISC/ND router advertisements */ +#define RTPROT_MRT 10 /* Merit MRT */ +#define RTPROT_ZEBRA 11 /* Zebra */ +#define RTPROT_BIRD 12 /* BIRD */ +#define RTPROT_DNROUTED 13 /* DECnet routing daemon */ +#define RTPROT_XORP 14 /* XORP */ +#define RTPROT_NTK 15 /* Netsukuku */ +#define RTPROT_DHCP 16 /* DHCP client */ +#define RTPROT_MROUTED 17 /* Multicast daemon */ +#define RTPROT_KEEPALIVED 18 /* Keepalived daemon */ +#define RTPROT_BABEL 42 /* Babel daemon */ +#define RTPROT_OPENR 99 /* Open Routing (Open/R) Routes */ +#define RTPROT_BGP 186 /* BGP Routes */ +#define RTPROT_ISIS 187 /* ISIS Routes */ +#define RTPROT_OSPF 188 /* OSPF Routes */ +#define RTPROT_RIP 189 /* RIP Routes */ +#define RTPROT_EIGRP 192 /* EIGRP Routes */ + +/* rtm_scope + + Really it is not scope, but sort of distance to the destination. + NOWHERE are reserved for not existing destinations, HOST is our + local addresses, LINK are destinations, located on directly attached + link and UNIVERSE is everywhere in the Universe. + + Intermediate values are also possible f.e. interior routes + could be assigned a value between UNIVERSE and LINK. +*/ + +enum rt_scope_t { + RT_SCOPE_UNIVERSE=0, +/* User defined values */ + RT_SCOPE_SITE=200, + RT_SCOPE_LINK=253, + RT_SCOPE_HOST=254, + RT_SCOPE_NOWHERE=255 +}; + +/* rtm_flags */ + +#define RTM_F_NOTIFY 0x100 /* Notify user of route change */ +#define RTM_F_CLONED 0x200 /* This route is cloned */ +#define RTM_F_EQUALIZE 0x400 /* Multipath equalizer: NI */ +#define RTM_F_PREFIX 0x800 /* Prefix addresses */ +#define RTM_F_LOOKUP_TABLE 0x1000 /* set rtm_table to FIB lookup result */ +#define RTM_F_FIB_MATCH 0x2000 /* return full fib lookup match */ +#define RTM_F_OFFLOAD 0x4000 /* route is offloaded */ +#define RTM_F_TRAP 0x8000 /* route is trapping packets */ +#define RTM_F_OFFLOAD_FAILED 0x20000000 /* route offload failed, this value + * is chosen to avoid conflicts with + * other flags defined in + * include/uapi/linux/ipv6_route.h + */ + +/* 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 /* Nexthop is offloaded */ +#define RTNH_F_LINKDOWN 16 /* carrier-down on nexthop */ +#define RTNH_F_UNRESOLVED 32 /* The entry is unresolved (ipmr) */ +#define RTNH_F_TRAP 64 /* Nexthop is trapping packets */ + +#define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN | \ + RTNH_F_OFFLOAD | RTNH_F_TRAP) + +/* 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[]; +}; + +/* RTM_CACHEINFO */ + +struct rta_cacheinfo { + __u32 rta_clntref; + __u32 rta_lastuse; + __s32 rta_expires; + __u32 rta_error; + __u32 rta_used; + +#define RTNETLINK_HAVE_PEERINFO 1 + __u32 rta_id; + __u32 rta_ts; + __u32 rta_tsage; +}; + +/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */ + +enum { + RTAX_UNSPEC, +#define RTAX_UNSPEC RTAX_UNSPEC + RTAX_LOCK, +#define RTAX_LOCK RTAX_LOCK + RTAX_MTU, +#define RTAX_MTU RTAX_MTU + RTAX_WINDOW, +#define RTAX_WINDOW RTAX_WINDOW + RTAX_RTT, +#define RTAX_RTT RTAX_RTT + RTAX_RTTVAR, +#define RTAX_RTTVAR RTAX_RTTVAR + RTAX_SSTHRESH, +#define RTAX_SSTHRESH RTAX_SSTHRESH + RTAX_CWND, +#define RTAX_CWND RTAX_CWND + RTAX_ADVMSS, +#define RTAX_ADVMSS RTAX_ADVMSS + RTAX_REORDERING, +#define RTAX_REORDERING RTAX_REORDERING + RTAX_HOPLIMIT, +#define RTAX_HOPLIMIT RTAX_HOPLIMIT + RTAX_INITCWND, +#define RTAX_INITCWND RTAX_INITCWND + RTAX_FEATURES, +#define RTAX_FEATURES RTAX_FEATURES + RTAX_RTO_MIN, +#define RTAX_RTO_MIN RTAX_RTO_MIN + RTAX_INITRWND, +#define RTAX_INITRWND RTAX_INITRWND + RTAX_QUICKACK, +#define RTAX_QUICKACK RTAX_QUICKACK + RTAX_CC_ALGO, +#define RTAX_CC_ALGO RTAX_CC_ALGO + RTAX_FASTOPEN_NO_COOKIE, +#define RTAX_FASTOPEN_NO_COOKIE RTAX_FASTOPEN_NO_COOKIE + __RTAX_MAX +}; + +#define RTAX_MAX (__RTAX_MAX - 1) + +#define RTAX_FEATURE_ECN (1 << 0) +#define RTAX_FEATURE_SACK (1 << 1) +#define RTAX_FEATURE_TIMESTAMP (1 << 2) +#define RTAX_FEATURE_ALLFRAG (1 << 3) + +#define RTAX_FEATURE_MASK (RTAX_FEATURE_ECN | RTAX_FEATURE_SACK | \ + RTAX_FEATURE_TIMESTAMP | RTAX_FEATURE_ALLFRAG) + +struct rta_session { + __u8 proto; + __u8 pad1; + __u16 pad2; + + union { + struct { + __u16 sport; + __u16 dport; + } ports; + + struct { + __u8 type; + __u8 code; + __u16 ident; + } icmpt; + + __u32 spi; + } u; +}; + +struct rta_mfc_stats { + __u64 mfcs_packets; + __u64 mfcs_bytes; + __u64 mfcs_wrong_if; +}; + +/**** + * General form of address family dependent message. + ****/ + +struct rtgenmsg { + unsigned char rtgen_family; +}; + +/***************************************************************** + * Link layer specific messages. + ****/ + +/* struct ifinfomsg + * passes link level specific information, not dependent + * on network protocol. + */ + +struct ifinfomsg { + unsigned char ifi_family; + unsigned char __ifi_pad; + unsigned short ifi_type; /* ARPHRD_* */ + int ifi_index; /* Link index */ + unsigned ifi_flags; /* IFF_* flags */ + unsigned ifi_change; /* IFF_* change mask */ +}; + +/******************************************************************** + * prefix information + ****/ + +struct prefixmsg { + unsigned char prefix_family; + unsigned char prefix_pad1; + unsigned short prefix_pad2; + int prefix_ifindex; + unsigned char prefix_type; + unsigned char prefix_len; + unsigned char prefix_flags; + unsigned char prefix_pad3; +}; + +enum +{ + PREFIX_UNSPEC, + PREFIX_ADDRESS, + PREFIX_CACHEINFO, + __PREFIX_MAX +}; + +#define PREFIX_MAX (__PREFIX_MAX - 1) + +struct prefix_cacheinfo { + __u32 preferred_time; + __u32 valid_time; +}; + + +/***************************************************************** + * Traffic control messages. + ****/ + +struct tcmsg { + unsigned char tcm_family; + unsigned char tcm__pad1; + unsigned short tcm__pad2; + int tcm_ifindex; + __u32 tcm_handle; + __u32 tcm_parent; +/* tcm_block_index is used instead of tcm_parent + * in case tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK + */ +#define tcm_block_index tcm_parent + __u32 tcm_info; +}; + +/* For manipulation of filters in shared block, tcm_ifindex is set to + * TCM_IFINDEX_MAGIC_BLOCK, and tcm_parent is aliased to tcm_block_index + * which is the block index. + */ +#define TCM_IFINDEX_MAGIC_BLOCK (0xFFFFFFFFU) + +enum { + TCA_UNSPEC, + TCA_KIND, + TCA_OPTIONS, + TCA_STATS, + TCA_XSTATS, + TCA_RATE, + TCA_FCNT, + TCA_STATS2, + TCA_STAB, + TCA_PAD, + TCA_DUMP_INVISIBLE, + TCA_CHAIN, + TCA_HW_OFFLOAD, + TCA_INGRESS_BLOCK, + TCA_EGRESS_BLOCK, + TCA_DUMP_FLAGS, + TCA_EXT_WARN_MSG, + __TCA_MAX +}; + +#define TCA_MAX (__TCA_MAX - 1) + +#define TCA_DUMP_FLAGS_TERSE (1 << 0) /* Means that in dump user gets only basic + * data necessary to identify the objects + * (handle, cookie, etc.) and stats. + */ + +#define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg)))) +#define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg)) + +/******************************************************************** + * Neighbor Discovery userland options + ****/ + +struct nduseroptmsg { + unsigned char nduseropt_family; + unsigned char nduseropt_pad1; + unsigned short nduseropt_opts_len; /* Total length of options */ + int nduseropt_ifindex; + __u8 nduseropt_icmp_type; + __u8 nduseropt_icmp_code; + unsigned short nduseropt_pad2; + unsigned int nduseropt_pad3; + /* Followed by one or more ND options */ +}; + +enum { + NDUSEROPT_UNSPEC, + NDUSEROPT_SRCADDR, + __NDUSEROPT_MAX +}; + +#define NDUSEROPT_MAX (__NDUSEROPT_MAX - 1) + +/* RTnetlink multicast groups - backwards compatibility for userspace */ +#define RTMGRP_LINK 1 +#define RTMGRP_NOTIFY 2 +#define RTMGRP_NEIGH 4 +#define RTMGRP_TC 8 + +#define RTMGRP_IPV4_IFADDR 0x10 +#define RTMGRP_IPV4_MROUTE 0x20 +#define RTMGRP_IPV4_ROUTE 0x40 +#define RTMGRP_IPV4_RULE 0x80 + +#define RTMGRP_IPV6_IFADDR 0x100 +#define RTMGRP_IPV6_MROUTE 0x200 +#define RTMGRP_IPV6_ROUTE 0x400 +#define RTMGRP_IPV6_IFINFO 0x800 + +#define RTMGRP_DECnet_IFADDR 0x1000 +#define RTMGRP_DECnet_ROUTE 0x4000 + +#define RTMGRP_IPV6_PREFIX 0x20000 + +/* RTnetlink multicast groups */ +enum rtnetlink_groups { + RTNLGRP_NONE, +#define RTNLGRP_NONE RTNLGRP_NONE + RTNLGRP_LINK, +#define RTNLGRP_LINK RTNLGRP_LINK + RTNLGRP_NOTIFY, +#define RTNLGRP_NOTIFY RTNLGRP_NOTIFY + RTNLGRP_NEIGH, +#define RTNLGRP_NEIGH RTNLGRP_NEIGH + RTNLGRP_TC, +#define RTNLGRP_TC RTNLGRP_TC + RTNLGRP_IPV4_IFADDR, +#define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR + RTNLGRP_IPV4_MROUTE, +#define RTNLGRP_IPV4_MROUTE RTNLGRP_IPV4_MROUTE + RTNLGRP_IPV4_ROUTE, +#define RTNLGRP_IPV4_ROUTE RTNLGRP_IPV4_ROUTE + RTNLGRP_IPV4_RULE, +#define RTNLGRP_IPV4_RULE RTNLGRP_IPV4_RULE + RTNLGRP_IPV6_IFADDR, +#define RTNLGRP_IPV6_IFADDR RTNLGRP_IPV6_IFADDR + RTNLGRP_IPV6_MROUTE, +#define RTNLGRP_IPV6_MROUTE RTNLGRP_IPV6_MROUTE + RTNLGRP_IPV6_ROUTE, +#define RTNLGRP_IPV6_ROUTE RTNLGRP_IPV6_ROUTE + RTNLGRP_IPV6_IFINFO, +#define RTNLGRP_IPV6_IFINFO RTNLGRP_IPV6_IFINFO + RTNLGRP_DECnet_IFADDR, +#define RTNLGRP_DECnet_IFADDR RTNLGRP_DECnet_IFADDR + RTNLGRP_NOP2, + RTNLGRP_DECnet_ROUTE, +#define RTNLGRP_DECnet_ROUTE RTNLGRP_DECnet_ROUTE + RTNLGRP_DECnet_RULE, +#define RTNLGRP_DECnet_RULE RTNLGRP_DECnet_RULE + RTNLGRP_NOP4, + RTNLGRP_IPV6_PREFIX, +#define RTNLGRP_IPV6_PREFIX RTNLGRP_IPV6_PREFIX + RTNLGRP_IPV6_RULE, +#define RTNLGRP_IPV6_RULE RTNLGRP_IPV6_RULE + RTNLGRP_ND_USEROPT, +#define RTNLGRP_ND_USEROPT RTNLGRP_ND_USEROPT + RTNLGRP_PHONET_IFADDR, +#define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR + RTNLGRP_PHONET_ROUTE, +#define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE + RTNLGRP_DCB, +#define RTNLGRP_DCB RTNLGRP_DCB + RTNLGRP_IPV4_NETCONF, +#define RTNLGRP_IPV4_NETCONF RTNLGRP_IPV4_NETCONF + RTNLGRP_IPV6_NETCONF, +#define RTNLGRP_IPV6_NETCONF RTNLGRP_IPV6_NETCONF + RTNLGRP_MDB, +#define RTNLGRP_MDB RTNLGRP_MDB + RTNLGRP_MPLS_ROUTE, +#define RTNLGRP_MPLS_ROUTE RTNLGRP_MPLS_ROUTE + RTNLGRP_NSID, +#define RTNLGRP_NSID RTNLGRP_NSID + RTNLGRP_MPLS_NETCONF, +#define RTNLGRP_MPLS_NETCONF RTNLGRP_MPLS_NETCONF + RTNLGRP_IPV4_MROUTE_R, +#define RTNLGRP_IPV4_MROUTE_R RTNLGRP_IPV4_MROUTE_R + RTNLGRP_IPV6_MROUTE_R, +#define RTNLGRP_IPV6_MROUTE_R RTNLGRP_IPV6_MROUTE_R + RTNLGRP_NEXTHOP, +#define RTNLGRP_NEXTHOP RTNLGRP_NEXTHOP + RTNLGRP_BRVLAN, +#define RTNLGRP_BRVLAN RTNLGRP_BRVLAN + RTNLGRP_MCTP_IFADDR, +#define RTNLGRP_MCTP_IFADDR RTNLGRP_MCTP_IFADDR + RTNLGRP_TUNNEL, +#define RTNLGRP_TUNNEL RTNLGRP_TUNNEL + RTNLGRP_STATS, +#define RTNLGRP_STATS RTNLGRP_STATS + __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_EXT_WARN_MSG, + __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_ACT_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 + * + * TCA_ACT_FLAG_TERSE_DUMP user->kernel to request terse (brief) dump that only + * includes essential action info (kind, index, etc.) + * + */ +#define TCA_FLAG_LARGE_DUMP_ON (1 << 0) +#define TCA_ACT_FLAG_LARGE_DUMP_ON TCA_FLAG_LARGE_DUMP_ON +#define TCA_ACT_FLAG_TERSE_DUMP (1 << 1) + +/* New extended info filters for IFLA_EXT_MASK */ +#define RTEXT_FILTER_VF (1 << 0) +#define RTEXT_FILTER_BRVLAN (1 << 1) +#define RTEXT_FILTER_BRVLAN_COMPRESSED (1 << 2) +#define RTEXT_FILTER_SKIP_STATS (1 << 3) +#define RTEXT_FILTER_MRP (1 << 4) +#define RTEXT_FILTER_CFM_CONFIG (1 << 5) +#define RTEXT_FILTER_CFM_STATUS (1 << 6) +#define RTEXT_FILTER_MST (1 << 7) + +/* End of information exported to user level */ + + + +#endif /* __LINUX_RTNETLINK_H */ diff --git a/libnl/include/linux-private/linux/seg6.h b/libnl/include/linux-private/linux/seg6.h new file mode 100644 index 0000000..e888b00 --- /dev/null +++ b/libnl/include/linux-private/linux/seg6.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_SEG6_H +#define _LINUX_SEG6_H + +#include +#include /* For struct in6_addr. */ + +/* + * SRH + */ +struct ipv6_sr_hdr { + __u8 nexthdr; + __u8 hdrlen; + __u8 type; + __u8 segments_left; + __u8 first_segment; /* Represents the last_entry field of SRH */ + __u8 flags; + __u16 tag; + + struct in6_addr segments[]; +}; + +#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/libnl/include/linux-private/linux/seg6_hmac.h b/libnl/include/linux-private/linux/seg6_hmac.h new file mode 100644 index 0000000..3fb3412 --- /dev/null +++ b/libnl/include/linux-private/linux/seg6_hmac.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_SEG6_HMAC_H +#define _LINUX_SEG6_HMAC_H + +#include +#include + +#define SEG6_HMAC_SECRET_LEN 64 +#define SEG6_HMAC_FIELD_LEN 32 + +struct sr6_tlv_hmac { + struct sr6_tlv tlvhdr; + __u16 reserved; + __be32 hmackeyid; + __u8 hmac[SEG6_HMAC_FIELD_LEN]; +}; + +enum { + SEG6_HMAC_ALGO_SHA1 = 1, + SEG6_HMAC_ALGO_SHA256 = 2, +}; + +#endif diff --git a/libnl/include/linux-private/linux/seg6_iptunnel.h b/libnl/include/linux-private/linux/seg6_iptunnel.h new file mode 100644 index 0000000..e1929d2 --- /dev/null +++ b/libnl/include/linux-private/linux/seg6_iptunnel.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_SEG6_IPTUNNEL_H +#define _LINUX_SEG6_IPTUNNEL_H + +#include /* For struct ipv6_sr_hdr. */ + +enum { + SEG6_IPTUNNEL_UNSPEC, + SEG6_IPTUNNEL_SRH, + __SEG6_IPTUNNEL_MAX, +}; +#define SEG6_IPTUNNEL_MAX (__SEG6_IPTUNNEL_MAX - 1) + +struct seg6_iptunnel_encap { + int mode; + struct ipv6_sr_hdr srh[]; +}; + +#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, + SEG6_IPTUN_MODE_ENCAP_RED, + SEG6_IPTUN_MODE_L2ENCAP_RED, +}; + +#endif diff --git a/libnl/include/linux-private/linux/seg6_local.h b/libnl/include/linux-private/linux/seg6_local.h new file mode 100644 index 0000000..6e71d97 --- /dev/null +++ b/libnl/include/linux-private/linux/seg6_local.h @@ -0,0 +1,137 @@ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_SEG6_LOCAL_H +#define _LINUX_SEG6_LOCAL_H + +#include + +enum { + SEG6_LOCAL_UNSPEC, + SEG6_LOCAL_ACTION, + SEG6_LOCAL_SRH, + SEG6_LOCAL_TABLE, + SEG6_LOCAL_NH4, + SEG6_LOCAL_NH6, + SEG6_LOCAL_IIF, + SEG6_LOCAL_OIF, + SEG6_LOCAL_BPF, + SEG6_LOCAL_VRFTABLE, + SEG6_LOCAL_COUNTERS, + SEG6_LOCAL_FLAVORS, + __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, + /* decap and lookup of DA in v4 or v6 table */ + SEG6_LOCAL_ACTION_END_DT46 = 16, + + __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) + +/* SRv6 Behavior counters are encoded as netlink attributes guaranteeing the + * correct alignment. + * Each counter is identified by a different attribute type (i.e. + * SEG6_LOCAL_CNT_PACKETS). + * + * - SEG6_LOCAL_CNT_PACKETS: identifies a counter that counts the number of + * packets that have been CORRECTLY processed by an SRv6 Behavior instance + * (i.e., packets that generate errors or are dropped are NOT counted). + * + * - SEG6_LOCAL_CNT_BYTES: identifies a counter that counts the total amount + * of traffic in bytes of all packets that have been CORRECTLY processed by + * an SRv6 Behavior instance (i.e., packets that generate errors or are + * dropped are NOT counted). + * + * - SEG6_LOCAL_CNT_ERRORS: identifies a counter that counts the number of + * packets that have NOT been properly processed by an SRv6 Behavior instance + * (i.e., packets that generate errors or are dropped). + */ +enum { + SEG6_LOCAL_CNT_UNSPEC, + SEG6_LOCAL_CNT_PAD, /* pad for 64 bits values */ + SEG6_LOCAL_CNT_PACKETS, + SEG6_LOCAL_CNT_BYTES, + SEG6_LOCAL_CNT_ERRORS, + __SEG6_LOCAL_CNT_MAX, +}; + +#define SEG6_LOCAL_CNT_MAX (__SEG6_LOCAL_CNT_MAX - 1) + +/* SRv6 End* Flavor attributes */ +enum { + SEG6_LOCAL_FLV_UNSPEC, + SEG6_LOCAL_FLV_OPERATION, + SEG6_LOCAL_FLV_LCBLOCK_BITS, + SEG6_LOCAL_FLV_LCNODE_FN_BITS, + __SEG6_LOCAL_FLV_MAX, +}; + +#define SEG6_LOCAL_FLV_MAX (__SEG6_LOCAL_FLV_MAX - 1) + +/* Designed flavor operations for SRv6 End* Behavior */ +enum { + SEG6_LOCAL_FLV_OP_UNSPEC, + SEG6_LOCAL_FLV_OP_PSP, + SEG6_LOCAL_FLV_OP_USP, + SEG6_LOCAL_FLV_OP_USD, + SEG6_LOCAL_FLV_OP_NEXT_CSID, + __SEG6_LOCAL_FLV_OP_MAX +}; + +#define SEG6_LOCAL_FLV_OP_MAX (__SEG6_LOCAL_FLV_OP_MAX - 1) + +#endif diff --git a/libnl/include/linux-private/linux/snmp.h b/libnl/include/linux-private/linux/snmp.h new file mode 100644 index 0000000..26f33a4 --- /dev/null +++ b/libnl/include/linux-private/linux/snmp.h @@ -0,0 +1,356 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Definitions for MIBs + * + * Author: Hideaki YOSHIFUJI + */ + +#ifndef _LINUX_SNMP_H +#define _LINUX_SNMP_H + +/* ipstats mib definitions */ +/* + * RFC 1213: MIB-II + * RFC 2011 (updates 1213): SNMPv2-MIB-IP + * RFC 2863: Interfaces Group MIB + * RFC 2465: IPv6 MIB: General Group + * draft-ietf-ipv6-rfc2011-update-10.txt: MIB for IP: IP Statistics Tables + */ +enum +{ + IPSTATS_MIB_NUM = 0, +/* frequently written fields in fast path, kept in same cache line */ + IPSTATS_MIB_INPKTS, /* InReceives */ + IPSTATS_MIB_INOCTETS, /* InOctets */ + IPSTATS_MIB_INDELIVERS, /* InDelivers */ + IPSTATS_MIB_OUTFORWDATAGRAMS, /* OutForwDatagrams */ + IPSTATS_MIB_OUTPKTS, /* OutRequests */ + IPSTATS_MIB_OUTOCTETS, /* OutOctets */ +/* other fields */ + IPSTATS_MIB_INHDRERRORS, /* InHdrErrors */ + IPSTATS_MIB_INTOOBIGERRORS, /* InTooBigErrors */ + IPSTATS_MIB_INNOROUTES, /* InNoRoutes */ + IPSTATS_MIB_INADDRERRORS, /* InAddrErrors */ + IPSTATS_MIB_INUNKNOWNPROTOS, /* InUnknownProtos */ + IPSTATS_MIB_INTRUNCATEDPKTS, /* InTruncatedPkts */ + IPSTATS_MIB_INDISCARDS, /* InDiscards */ + IPSTATS_MIB_OUTDISCARDS, /* OutDiscards */ + IPSTATS_MIB_OUTNOROUTES, /* OutNoRoutes */ + IPSTATS_MIB_REASMTIMEOUT, /* ReasmTimeout */ + IPSTATS_MIB_REASMREQDS, /* ReasmReqds */ + IPSTATS_MIB_REASMOKS, /* ReasmOKs */ + IPSTATS_MIB_REASMFAILS, /* ReasmFails */ + IPSTATS_MIB_FRAGOKS, /* FragOKs */ + IPSTATS_MIB_FRAGFAILS, /* FragFails */ + IPSTATS_MIB_FRAGCREATES, /* FragCreates */ + IPSTATS_MIB_INMCASTPKTS, /* InMcastPkts */ + IPSTATS_MIB_OUTMCASTPKTS, /* OutMcastPkts */ + IPSTATS_MIB_INBCASTPKTS, /* InBcastPkts */ + IPSTATS_MIB_OUTBCASTPKTS, /* OutBcastPkts */ + IPSTATS_MIB_INMCASTOCTETS, /* InMcastOctets */ + IPSTATS_MIB_OUTMCASTOCTETS, /* OutMcastOctets */ + IPSTATS_MIB_INBCASTOCTETS, /* InBcastOctets */ + IPSTATS_MIB_OUTBCASTOCTETS, /* OutBcastOctets */ + IPSTATS_MIB_CSUMERRORS, /* InCsumErrors */ + IPSTATS_MIB_NOECTPKTS, /* InNoECTPkts */ + IPSTATS_MIB_ECT1PKTS, /* InECT1Pkts */ + IPSTATS_MIB_ECT0PKTS, /* InECT0Pkts */ + IPSTATS_MIB_CEPKTS, /* InCEPkts */ + IPSTATS_MIB_REASM_OVERLAPS, /* ReasmOverlaps */ + __IPSTATS_MIB_MAX +}; + +/* icmp mib definitions */ +/* + * RFC 1213: MIB-II ICMP Group + * RFC 2011 (updates 1213): SNMPv2 MIB for IP: ICMP group + */ +enum +{ + ICMP_MIB_NUM = 0, + ICMP_MIB_INMSGS, /* InMsgs */ + ICMP_MIB_INERRORS, /* InErrors */ + ICMP_MIB_INDESTUNREACHS, /* InDestUnreachs */ + ICMP_MIB_INTIMEEXCDS, /* InTimeExcds */ + ICMP_MIB_INPARMPROBS, /* InParmProbs */ + ICMP_MIB_INSRCQUENCHS, /* InSrcQuenchs */ + ICMP_MIB_INREDIRECTS, /* InRedirects */ + ICMP_MIB_INECHOS, /* InEchos */ + ICMP_MIB_INECHOREPS, /* InEchoReps */ + ICMP_MIB_INTIMESTAMPS, /* InTimestamps */ + ICMP_MIB_INTIMESTAMPREPS, /* InTimestampReps */ + ICMP_MIB_INADDRMASKS, /* InAddrMasks */ + ICMP_MIB_INADDRMASKREPS, /* InAddrMaskReps */ + ICMP_MIB_OUTMSGS, /* OutMsgs */ + ICMP_MIB_OUTERRORS, /* OutErrors */ + ICMP_MIB_OUTDESTUNREACHS, /* OutDestUnreachs */ + ICMP_MIB_OUTTIMEEXCDS, /* OutTimeExcds */ + ICMP_MIB_OUTPARMPROBS, /* OutParmProbs */ + ICMP_MIB_OUTSRCQUENCHS, /* OutSrcQuenchs */ + ICMP_MIB_OUTREDIRECTS, /* OutRedirects */ + ICMP_MIB_OUTECHOS, /* OutEchos */ + ICMP_MIB_OUTECHOREPS, /* OutEchoReps */ + ICMP_MIB_OUTTIMESTAMPS, /* OutTimestamps */ + ICMP_MIB_OUTTIMESTAMPREPS, /* OutTimestampReps */ + ICMP_MIB_OUTADDRMASKS, /* OutAddrMasks */ + ICMP_MIB_OUTADDRMASKREPS, /* OutAddrMaskReps */ + ICMP_MIB_CSUMERRORS, /* InCsumErrors */ + ICMP_MIB_RATELIMITGLOBAL, /* OutRateLimitGlobal */ + ICMP_MIB_RATELIMITHOST, /* OutRateLimitHost */ + __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_RATELIMITHOST, /* OutRateLimitHost */ + __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_MEMERRORS, /* MemErrors */ + __UDP_MIB_MAX +}; + +/* linux mib definitions */ +enum +{ + LINUX_MIB_NUM = 0, + LINUX_MIB_SYNCOOKIESSENT, /* SyncookiesSent */ + LINUX_MIB_SYNCOOKIESRECV, /* SyncookiesRecv */ + LINUX_MIB_SYNCOOKIESFAILED, /* SyncookiesFailed */ + LINUX_MIB_EMBRYONICRSTS, /* EmbryonicRsts */ + LINUX_MIB_PRUNECALLED, /* PruneCalled */ + LINUX_MIB_RCVPRUNED, /* RcvPruned */ + LINUX_MIB_OFOPRUNED, /* OfoPruned */ + LINUX_MIB_OUTOFWINDOWICMPS, /* OutOfWindowIcmps */ + LINUX_MIB_LOCKDROPPEDICMPS, /* LockDroppedIcmps */ + LINUX_MIB_ARPFILTER, /* ArpFilter */ + LINUX_MIB_TIMEWAITED, /* TimeWaited */ + LINUX_MIB_TIMEWAITRECYCLED, /* TimeWaitRecycled */ + LINUX_MIB_TIMEWAITKILLED, /* TimeWaitKilled */ + LINUX_MIB_PAWSACTIVEREJECTED, /* PAWSActiveRejected */ + LINUX_MIB_PAWSESTABREJECTED, /* PAWSEstabRejected */ + LINUX_MIB_DELAYEDACKS, /* DelayedACKs */ + LINUX_MIB_DELAYEDACKLOCKED, /* DelayedACKLocked */ + LINUX_MIB_DELAYEDACKLOST, /* DelayedACKLost */ + LINUX_MIB_LISTENOVERFLOWS, /* ListenOverflows */ + LINUX_MIB_LISTENDROPS, /* ListenDrops */ + LINUX_MIB_TCPHPHITS, /* TCPHPHits */ + LINUX_MIB_TCPPUREACKS, /* TCPPureAcks */ + LINUX_MIB_TCPHPACKS, /* TCPHPAcks */ + LINUX_MIB_TCPRENORECOVERY, /* TCPRenoRecovery */ + LINUX_MIB_TCPSACKRECOVERY, /* TCPSackRecovery */ + LINUX_MIB_TCPSACKRENEGING, /* TCPSACKReneging */ + LINUX_MIB_TCPSACKREORDER, /* TCPSACKReorder */ + LINUX_MIB_TCPRENOREORDER, /* TCPRenoReorder */ + LINUX_MIB_TCPTSREORDER, /* TCPTSReorder */ + LINUX_MIB_TCPFULLUNDO, /* TCPFullUndo */ + LINUX_MIB_TCPPARTIALUNDO, /* TCPPartialUndo */ + LINUX_MIB_TCPDSACKUNDO, /* TCPDSACKUndo */ + LINUX_MIB_TCPLOSSUNDO, /* TCPLossUndo */ + LINUX_MIB_TCPLOSTRETRANSMIT, /* TCPLostRetransmit */ + LINUX_MIB_TCPRENOFAILURES, /* TCPRenoFailures */ + LINUX_MIB_TCPSACKFAILURES, /* TCPSackFailures */ + LINUX_MIB_TCPLOSSFAILURES, /* TCPLossFailures */ + LINUX_MIB_TCPFASTRETRANS, /* TCPFastRetrans */ + LINUX_MIB_TCPSLOWSTARTRETRANS, /* TCPSlowStartRetrans */ + LINUX_MIB_TCPTIMEOUTS, /* TCPTimeouts */ + LINUX_MIB_TCPLOSSPROBES, /* TCPLossProbes */ + LINUX_MIB_TCPLOSSPROBERECOVERY, /* TCPLossProbeRecovery */ + LINUX_MIB_TCPRENORECOVERYFAIL, /* TCPRenoRecoveryFail */ + LINUX_MIB_TCPSACKRECOVERYFAIL, /* TCPSackRecoveryFail */ + LINUX_MIB_TCPRCVCOLLAPSED, /* TCPRcvCollapsed */ + LINUX_MIB_TCPDSACKOLDSENT, /* TCPDSACKOldSent */ + LINUX_MIB_TCPDSACKOFOSENT, /* TCPDSACKOfoSent */ + LINUX_MIB_TCPDSACKRECV, /* TCPDSACKRecv */ + LINUX_MIB_TCPDSACKOFORECV, /* TCPDSACKOfoRecv */ + LINUX_MIB_TCPABORTONDATA, /* TCPAbortOnData */ + LINUX_MIB_TCPABORTONCLOSE, /* TCPAbortOnClose */ + LINUX_MIB_TCPABORTONMEMORY, /* TCPAbortOnMemory */ + LINUX_MIB_TCPABORTONTIMEOUT, /* TCPAbortOnTimeout */ + LINUX_MIB_TCPABORTONLINGER, /* TCPAbortOnLinger */ + LINUX_MIB_TCPABORTFAILED, /* TCPAbortFailed */ + LINUX_MIB_TCPMEMORYPRESSURES, /* TCPMemoryPressures */ + LINUX_MIB_TCPMEMORYPRESSURESCHRONO, /* TCPMemoryPressuresChrono */ + LINUX_MIB_TCPSACKDISCARD, /* TCPSACKDiscard */ + LINUX_MIB_TCPDSACKIGNOREDOLD, /* TCPSACKIgnoredOld */ + LINUX_MIB_TCPDSACKIGNOREDNOUNDO, /* TCPSACKIgnoredNoUndo */ + LINUX_MIB_TCPSPURIOUSRTOS, /* TCPSpuriousRTOs */ + LINUX_MIB_TCPMD5NOTFOUND, /* TCPMD5NotFound */ + LINUX_MIB_TCPMD5UNEXPECTED, /* TCPMD5Unexpected */ + LINUX_MIB_TCPMD5FAILURE, /* TCPMD5Failure */ + LINUX_MIB_SACKSHIFTED, + LINUX_MIB_SACKMERGED, + LINUX_MIB_SACKSHIFTFALLBACK, + LINUX_MIB_TCPBACKLOGDROP, + LINUX_MIB_PFMEMALLOCDROP, + LINUX_MIB_TCPMINTTLDROP, /* RFC 5082 */ + LINUX_MIB_TCPDEFERACCEPTDROP, + LINUX_MIB_IPRPFILTER, /* IP Reverse Path Filter (rp_filter) */ + LINUX_MIB_TCPTIMEWAITOVERFLOW, /* TCPTimeWaitOverflow */ + LINUX_MIB_TCPREQQFULLDOCOOKIES, /* TCPReqQFullDoCookies */ + LINUX_MIB_TCPREQQFULLDROP, /* TCPReqQFullDrop */ + LINUX_MIB_TCPRETRANSFAIL, /* TCPRetransFail */ + LINUX_MIB_TCPRCVCOALESCE, /* TCPRcvCoalesce */ + LINUX_MIB_TCPBACKLOGCOALESCE, /* TCPBacklogCoalesce */ + LINUX_MIB_TCPOFOQUEUE, /* TCPOFOQueue */ + LINUX_MIB_TCPOFODROP, /* TCPOFODrop */ + LINUX_MIB_TCPOFOMERGE, /* TCPOFOMerge */ + LINUX_MIB_TCPCHALLENGEACK, /* TCPChallengeACK */ + LINUX_MIB_TCPSYNCHALLENGE, /* TCPSYNChallenge */ + LINUX_MIB_TCPFASTOPENACTIVE, /* TCPFastOpenActive */ + LINUX_MIB_TCPFASTOPENACTIVEFAIL, /* TCPFastOpenActiveFail */ + LINUX_MIB_TCPFASTOPENPASSIVE, /* TCPFastOpenPassive*/ + LINUX_MIB_TCPFASTOPENPASSIVEFAIL, /* TCPFastOpenPassiveFail */ + LINUX_MIB_TCPFASTOPENLISTENOVERFLOW, /* TCPFastOpenListenOverflow */ + LINUX_MIB_TCPFASTOPENCOOKIEREQD, /* TCPFastOpenCookieReqd */ + LINUX_MIB_TCPFASTOPENBLACKHOLE, /* TCPFastOpenBlackholeDetect */ + LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES, /* TCPSpuriousRtxHostQueues */ + LINUX_MIB_BUSYPOLLRXPACKETS, /* BusyPollRxPackets */ + LINUX_MIB_TCPAUTOCORKING, /* TCPAutoCorking */ + LINUX_MIB_TCPFROMZEROWINDOWADV, /* TCPFromZeroWindowAdv */ + LINUX_MIB_TCPTOZEROWINDOWADV, /* TCPToZeroWindowAdv */ + LINUX_MIB_TCPWANTZEROWINDOWADV, /* TCPWantZeroWindowAdv */ + LINUX_MIB_TCPSYNRETRANS, /* TCPSynRetrans */ + LINUX_MIB_TCPORIGDATASENT, /* TCPOrigDataSent */ + LINUX_MIB_TCPHYSTARTTRAINDETECT, /* TCPHystartTrainDetect */ + LINUX_MIB_TCPHYSTARTTRAINCWND, /* TCPHystartTrainCwnd */ + LINUX_MIB_TCPHYSTARTDELAYDETECT, /* TCPHystartDelayDetect */ + LINUX_MIB_TCPHYSTARTDELAYCWND, /* TCPHystartDelayCwnd */ + LINUX_MIB_TCPACKSKIPPEDSYNRECV, /* TCPACKSkippedSynRecv */ + LINUX_MIB_TCPACKSKIPPEDPAWS, /* TCPACKSkippedPAWS */ + LINUX_MIB_TCPACKSKIPPEDSEQ, /* TCPACKSkippedSeq */ + LINUX_MIB_TCPACKSKIPPEDFINWAIT2, /* TCPACKSkippedFinWait2 */ + LINUX_MIB_TCPACKSKIPPEDTIMEWAIT, /* TCPACKSkippedTimeWait */ + LINUX_MIB_TCPACKSKIPPEDCHALLENGE, /* TCPACKSkippedChallenge */ + LINUX_MIB_TCPWINPROBE, /* TCPWinProbe */ + LINUX_MIB_TCPKEEPALIVE, /* TCPKeepAlive */ + LINUX_MIB_TCPMTUPFAIL, /* TCPMTUPFail */ + LINUX_MIB_TCPMTUPSUCCESS, /* TCPMTUPSuccess */ + LINUX_MIB_TCPDELIVERED, /* TCPDelivered */ + LINUX_MIB_TCPDELIVEREDCE, /* TCPDeliveredCE */ + LINUX_MIB_TCPACKCOMPRESSED, /* TCPAckCompressed */ + LINUX_MIB_TCPZEROWINDOWDROP, /* TCPZeroWindowDrop */ + LINUX_MIB_TCPRCVQDROP, /* TCPRcvQDrop */ + LINUX_MIB_TCPWQUEUETOOBIG, /* TCPWqueueTooBig */ + LINUX_MIB_TCPFASTOPENPASSIVEALTKEY, /* TCPFastOpenPassiveAltKey */ + LINUX_MIB_TCPTIMEOUTREHASH, /* TCPTimeoutRehash */ + LINUX_MIB_TCPDUPLICATEDATAREHASH, /* TCPDuplicateDataRehash */ + LINUX_MIB_TCPDSACKRECVSEGS, /* TCPDSACKRecvSegs */ + LINUX_MIB_TCPDSACKIGNOREDDUBIOUS, /* TCPDSACKIgnoredDubious */ + LINUX_MIB_TCPMIGRATEREQSUCCESS, /* TCPMigrateReqSuccess */ + LINUX_MIB_TCPMIGRATEREQFAILURE, /* TCPMigrateReqFailure */ + LINUX_MIB_TCPPLBREHASH, /* TCPPLBRehash */ + __LINUX_MIB_MAX +}; + +/* linux Xfrm mib definitions */ +enum +{ + LINUX_MIB_XFRMNUM = 0, + LINUX_MIB_XFRMINERROR, /* XfrmInError */ + LINUX_MIB_XFRMINBUFFERERROR, /* XfrmInBufferError */ + LINUX_MIB_XFRMINHDRERROR, /* XfrmInHdrError */ + LINUX_MIB_XFRMINNOSTATES, /* XfrmInNoStates */ + LINUX_MIB_XFRMINSTATEPROTOERROR, /* XfrmInStateProtoError */ + LINUX_MIB_XFRMINSTATEMODEERROR, /* XfrmInStateModeError */ + LINUX_MIB_XFRMINSTATESEQERROR, /* XfrmInStateSeqError */ + LINUX_MIB_XFRMINSTATEEXPIRED, /* XfrmInStateExpired */ + LINUX_MIB_XFRMINSTATEMISMATCH, /* XfrmInStateMismatch */ + LINUX_MIB_XFRMINSTATEINVALID, /* XfrmInStateInvalid */ + LINUX_MIB_XFRMINTMPLMISMATCH, /* XfrmInTmplMismatch */ + LINUX_MIB_XFRMINNOPOLS, /* XfrmInNoPols */ + LINUX_MIB_XFRMINPOLBLOCK, /* XfrmInPolBlock */ + LINUX_MIB_XFRMINPOLERROR, /* XfrmInPolError */ + LINUX_MIB_XFRMOUTERROR, /* XfrmOutError */ + LINUX_MIB_XFRMOUTBUNDLEGENERROR, /* XfrmOutBundleGenError */ + LINUX_MIB_XFRMOUTBUNDLECHECKERROR, /* XfrmOutBundleCheckError */ + LINUX_MIB_XFRMOUTNOSTATES, /* XfrmOutNoStates */ + LINUX_MIB_XFRMOUTSTATEPROTOERROR, /* XfrmOutStateProtoError */ + LINUX_MIB_XFRMOUTSTATEMODEERROR, /* XfrmOutStateModeError */ + LINUX_MIB_XFRMOUTSTATESEQERROR, /* XfrmOutStateSeqError */ + LINUX_MIB_XFRMOUTSTATEEXPIRED, /* XfrmOutStateExpired */ + LINUX_MIB_XFRMOUTPOLBLOCK, /* XfrmOutPolBlock */ + LINUX_MIB_XFRMOUTPOLDEAD, /* XfrmOutPolDead */ + LINUX_MIB_XFRMOUTPOLERROR, /* XfrmOutPolError */ + LINUX_MIB_XFRMFWDHDRERROR, /* XfrmFwdHdrError*/ + LINUX_MIB_XFRMOUTSTATEINVALID, /* XfrmOutStateInvalid */ + LINUX_MIB_XFRMACQUIREERROR, /* XfrmAcquireError */ + __LINUX_MIB_XFRMMAX +}; + +/* linux TLS mib definitions */ +enum +{ + LINUX_MIB_TLSNUM = 0, + LINUX_MIB_TLSCURRTXSW, /* TlsCurrTxSw */ + LINUX_MIB_TLSCURRRXSW, /* TlsCurrRxSw */ + LINUX_MIB_TLSCURRTXDEVICE, /* TlsCurrTxDevice */ + LINUX_MIB_TLSCURRRXDEVICE, /* TlsCurrRxDevice */ + LINUX_MIB_TLSTXSW, /* TlsTxSw */ + LINUX_MIB_TLSRXSW, /* TlsRxSw */ + LINUX_MIB_TLSTXDEVICE, /* TlsTxDevice */ + LINUX_MIB_TLSRXDEVICE, /* TlsRxDevice */ + LINUX_MIB_TLSDECRYPTERROR, /* TlsDecryptError */ + LINUX_MIB_TLSRXDEVICERESYNC, /* TlsRxDeviceResync */ + LINUX_MIB_TLSDECRYPTRETRY, /* TlsDecryptRetry */ + LINUX_MIB_TLSRXNOPADVIOL, /* TlsRxNoPadViolation */ + __LINUX_MIB_TLSMAX +}; + +#endif /* _LINUX_SNMP_H */ diff --git a/libnl/include/linux-private/linux/sock_diag.h b/libnl/include/linux-private/linux/sock_diag.h new file mode 100644 index 0000000..35c0ce6 --- /dev/null +++ b/libnl/include/linux-private/linux/sock_diag.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __SOCK_DIAG_H__ +#define __SOCK_DIAG_H__ + +#include + +#define SOCK_DIAG_BY_FAMILY 20 +#define SOCK_DESTROY 21 + +struct sock_diag_req { + __u8 sdiag_family; + __u8 sdiag_protocol; +}; + +enum { + SK_MEMINFO_RMEM_ALLOC, + SK_MEMINFO_RCVBUF, + SK_MEMINFO_WMEM_ALLOC, + SK_MEMINFO_SNDBUF, + SK_MEMINFO_FWD_ALLOC, + SK_MEMINFO_WMEM_QUEUED, + SK_MEMINFO_OPTMEM, + SK_MEMINFO_BACKLOG, + SK_MEMINFO_DROPS, + + SK_MEMINFO_VARS, +}; + +enum sknetlink_groups { + SKNLGRP_NONE, + SKNLGRP_INET_TCP_DESTROY, + SKNLGRP_INET_UDP_DESTROY, + SKNLGRP_INET6_TCP_DESTROY, + SKNLGRP_INET6_UDP_DESTROY, + __SKNLGRP_MAX, +}; +#define SKNLGRP_MAX (__SKNLGRP_MAX - 1) + +enum { + SK_DIAG_BPF_STORAGE_REQ_NONE, + SK_DIAG_BPF_STORAGE_REQ_MAP_FD, + __SK_DIAG_BPF_STORAGE_REQ_MAX, +}; + +#define SK_DIAG_BPF_STORAGE_REQ_MAX (__SK_DIAG_BPF_STORAGE_REQ_MAX - 1) + +enum { + SK_DIAG_BPF_STORAGE_REP_NONE, + SK_DIAG_BPF_STORAGE, + __SK_DIAG_BPF_STORAGE_REP_MAX, +}; + +#define SK_DIAB_BPF_STORAGE_REP_MAX (__SK_DIAG_BPF_STORAGE_REP_MAX - 1) + +enum { + SK_DIAG_BPF_STORAGE_NONE, + SK_DIAG_BPF_STORAGE_PAD, + SK_DIAG_BPF_STORAGE_MAP_ID, + SK_DIAG_BPF_STORAGE_MAP_VALUE, + __SK_DIAG_BPF_STORAGE_MAX, +}; + +#define SK_DIAG_BPF_STORAGE_MAX (__SK_DIAG_BPF_STORAGE_MAX - 1) + +#endif /* __SOCK_DIAG_H__ */ diff --git a/libnl/include/linux-private/linux/socket.h b/libnl/include/linux-private/linux/socket.h new file mode 100644 index 0000000..89c227f --- /dev/null +++ b/libnl/include/linux-private/linux/socket.h @@ -0,0 +1,38 @@ +/* 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 */ + }; +}; + +#define SOCK_SNDBUF_LOCK 1 +#define SOCK_RCVBUF_LOCK 2 + +#define SOCK_BUF_LOCK_MASK (SOCK_SNDBUF_LOCK | SOCK_RCVBUF_LOCK) + +#define SOCK_TXREHASH_DEFAULT 255 +#define SOCK_TXREHASH_DISABLED 0 +#define SOCK_TXREHASH_ENABLED 1 + +#endif /* _LINUX_SOCKET_H */ diff --git a/libnl/include/linux-private/linux/stddef.h b/libnl/include/linux-private/linux/stddef.h new file mode 100644 index 0000000..bb6ea51 --- /dev/null +++ b/libnl/include/linux-private/linux/stddef.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_STDDEF_H +#define _LINUX_STDDEF_H + + + +#ifndef __always_inline +#define __always_inline __inline__ +#endif + +/** + * __struct_group() - Create a mirrored named and anonyomous struct + * + * @TAG: The tag name for the named sub-struct (usually empty) + * @NAME: The identifier name of the mirrored sub-struct + * @ATTRS: Any struct attributes (usually empty) + * @MEMBERS: The member declarations for the mirrored structs + * + * Used to create an anonymous union of two structs with identical layout + * and size: one anonymous and one named. The former's members can be used + * normally without sub-struct naming, and the latter can be used to + * reason about the start, end, and size of the group of struct members. + * The named struct can also be explicitly tagged for layer reuse, as well + * as both having struct attributes appended. + */ +#define __struct_group(TAG, NAME, ATTRS, MEMBERS...) \ + union { \ + struct { MEMBERS } ATTRS; \ + struct TAG { MEMBERS } ATTRS NAME; \ + } + +/** + * __DECLARE_FLEX_ARRAY() - Declare a flexible array usable in a union + * + * @TYPE: The type of each flexible array element + * @NAME: The name of the flexible array member + * + * In order to have a flexible array member in a union or alone in a + * struct, it needs to be wrapped in an anonymous struct with at least 1 + * named member, but that member can be empty. + */ +#define __DECLARE_FLEX_ARRAY(TYPE, NAME) \ + struct { \ + struct { } __empty_ ## NAME; \ + TYPE NAME[]; \ + } +#endif diff --git a/libnl/include/linux-private/linux/swab.h b/libnl/include/linux-private/linux/swab.h new file mode 100644 index 0000000..7e3bad5 --- /dev/null +++ b/libnl/include/linux-private/linux/swab.h @@ -0,0 +1,305 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_SWAB_H +#define _LINUX_SWAB_H + +#include +#include +#include +#include + +/* + * casts are necessary for constants, because we never know how for sure + * how U/UL/ULL map to __u16, __u32, __u64. At least not in a portable way. + */ +#define ___constant_swab16(x) ((__u16)( \ + (((__u16)(x) & (__u16)0x00ffU) << 8) | \ + (((__u16)(x) & (__u16)0xff00U) >> 8))) + +#define ___constant_swab32(x) ((__u32)( \ + (((__u32)(x) & (__u32)0x000000ffUL) << 24) | \ + (((__u32)(x) & (__u32)0x0000ff00UL) << 8) | \ + (((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | \ + (((__u32)(x) & (__u32)0xff000000UL) >> 24))) + +#define ___constant_swab64(x) ((__u64)( \ + (((__u64)(x) & (__u64)0x00000000000000ffULL) << 56) | \ + (((__u64)(x) & (__u64)0x000000000000ff00ULL) << 40) | \ + (((__u64)(x) & (__u64)0x0000000000ff0000ULL) << 24) | \ + (((__u64)(x) & (__u64)0x00000000ff000000ULL) << 8) | \ + (((__u64)(x) & (__u64)0x000000ff00000000ULL) >> 8) | \ + (((__u64)(x) & (__u64)0x0000ff0000000000ULL) >> 24) | \ + (((__u64)(x) & (__u64)0x00ff000000000000ULL) >> 40) | \ + (((__u64)(x) & (__u64)0xff00000000000000ULL) >> 56))) + +#define ___constant_swahw32(x) ((__u32)( \ + (((__u32)(x) & (__u32)0x0000ffffUL) << 16) | \ + (((__u32)(x) & (__u32)0xffff0000UL) >> 16))) + +#define ___constant_swahb32(x) ((__u32)( \ + (((__u32)(x) & (__u32)0x00ff00ffUL) << 8) | \ + (((__u32)(x) & (__u32)0xff00ff00UL) >> 8))) + +/* + * Implement the following as inlines, but define the interface using + * macros to allow constant folding when possible: + * ___swab16, ___swab32, ___swab64, ___swahw32, ___swahb32 + */ + +static __inline__ __u16 __fswab16(__u16 val) +{ +#if defined (__arch_swab16) + return __arch_swab16(val); +#else + return ___constant_swab16(val); +#endif +} + +static __inline__ __u32 __fswab32(__u32 val) +{ +#if defined(__arch_swab32) + return __arch_swab32(val); +#else + return ___constant_swab32(val); +#endif +} + +static __inline__ __u64 __fswab64(__u64 val) +{ +#if defined (__arch_swab64) + return __arch_swab64(val); +#elif defined(__SWAB_64_THRU_32__) + __u32 h = val >> 32; + __u32 l = val & ((1ULL << 32) - 1); + return (((__u64)__fswab32(l)) << 32) | ((__u64)(__fswab32(h))); +#else + return ___constant_swab64(val); +#endif +} + +static __inline__ __u32 __fswahw32(__u32 val) +{ +#ifdef __arch_swahw32 + return __arch_swahw32(val); +#else + return ___constant_swahw32(val); +#endif +} + +static __inline__ __u32 __fswahb32(__u32 val) +{ +#ifdef __arch_swahb32 + return __arch_swahb32(val); +#else + return ___constant_swahb32(val); +#endif +} + +/** + * __swab16 - return a byteswapped 16-bit value + * @x: value to byteswap + */ +#ifdef __HAVE_BUILTIN_BSWAP16__ +#define __swab16(x) (__u16)__builtin_bswap16((__u16)(x)) +#else +#define __swab16(x) \ + (__u16)(__builtin_constant_p(x) ? \ + ___constant_swab16(x) : \ + __fswab16(x)) +#endif + +/** + * __swab32 - return a byteswapped 32-bit value + * @x: value to byteswap + */ +#ifdef __HAVE_BUILTIN_BSWAP32__ +#define __swab32(x) (__u32)__builtin_bswap32((__u32)(x)) +#else +#define __swab32(x) \ + (__u32)(__builtin_constant_p(x) ? \ + ___constant_swab32(x) : \ + __fswab32(x)) +#endif + +/** + * __swab64 - return a byteswapped 64-bit value + * @x: value to byteswap + */ +#ifdef __HAVE_BUILTIN_BSWAP64__ +#define __swab64(x) (__u64)__builtin_bswap64((__u64)(x)) +#else +#define __swab64(x) \ + (__u64)(__builtin_constant_p(x) ? \ + ___constant_swab64(x) : \ + __fswab64(x)) +#endif + +static __always_inline unsigned long __swab(const unsigned long y) +{ +#if __BITS_PER_LONG == 64 + return __swab64(y); +#else /* __BITS_PER_LONG == 32 */ + return __swab32(y); +#endif +} + +/** + * __swahw32 - return a word-swapped 32-bit value + * @x: value to wordswap + * + * __swahw32(0x12340000) is 0x00001234 + */ +#define __swahw32(x) \ + (__builtin_constant_p((__u32)(x)) ? \ + ___constant_swahw32(x) : \ + __fswahw32(x)) + +/** + * __swahb32 - return a high and low byte-swapped 32-bit value + * @x: value to byteswap + * + * __swahb32(0x12345678) is 0x34127856 + */ +#define __swahb32(x) \ + (__builtin_constant_p((__u32)(x)) ? \ + ___constant_swahb32(x) : \ + __fswahb32(x)) + +/** + * __swab16p - return a byteswapped 16-bit value from a pointer + * @p: pointer to a naturally-aligned 16-bit value + */ +static __always_inline __u16 __swab16p(const __u16 *p) +{ +#ifdef __arch_swab16p + return __arch_swab16p(p); +#else + return __swab16(*p); +#endif +} + +/** + * __swab32p - return a byteswapped 32-bit value from a pointer + * @p: pointer to a naturally-aligned 32-bit value + */ +static __always_inline __u32 __swab32p(const __u32 *p) +{ +#ifdef __arch_swab32p + return __arch_swab32p(p); +#else + return __swab32(*p); +#endif +} + +/** + * __swab64p - return a byteswapped 64-bit value from a pointer + * @p: pointer to a naturally-aligned 64-bit value + */ +static __always_inline __u64 __swab64p(const __u64 *p) +{ +#ifdef __arch_swab64p + return __arch_swab64p(p); +#else + return __swab64(*p); +#endif +} + +/** + * __swahw32p - return a wordswapped 32-bit value from a pointer + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahw32() for details of wordswapping. + */ +static __inline__ __u32 __swahw32p(const __u32 *p) +{ +#ifdef __arch_swahw32p + return __arch_swahw32p(p); +#else + return __swahw32(*p); +#endif +} + +/** + * __swahb32p - return a high and low byteswapped 32-bit value from a pointer + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahb32() for details of high/low byteswapping. + */ +static __inline__ __u32 __swahb32p(const __u32 *p) +{ +#ifdef __arch_swahb32p + return __arch_swahb32p(p); +#else + return __swahb32(*p); +#endif +} + +/** + * __swab16s - byteswap a 16-bit value in-place + * @p: pointer to a naturally-aligned 16-bit value + */ +static __inline__ void __swab16s(__u16 *p) +{ +#ifdef __arch_swab16s + __arch_swab16s(p); +#else + *p = __swab16p(p); +#endif +} +/** + * __swab32s - byteswap a 32-bit value in-place + * @p: pointer to a naturally-aligned 32-bit value + */ +static __always_inline void __swab32s(__u32 *p) +{ +#ifdef __arch_swab32s + __arch_swab32s(p); +#else + *p = __swab32p(p); +#endif +} + +/** + * __swab64s - byteswap a 64-bit value in-place + * @p: pointer to a naturally-aligned 64-bit value + */ +static __always_inline void __swab64s(__u64 *p) +{ +#ifdef __arch_swab64s + __arch_swab64s(p); +#else + *p = __swab64p(p); +#endif +} + +/** + * __swahw32s - wordswap a 32-bit value in-place + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahw32() for details of wordswapping + */ +static __inline__ void __swahw32s(__u32 *p) +{ +#ifdef __arch_swahw32s + __arch_swahw32s(p); +#else + *p = __swahw32p(p); +#endif +} + +/** + * __swahb32s - high and low byteswap a 32-bit value in-place + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahb32() for details of high and low byte swapping + */ +static __inline__ void __swahb32s(__u32 *p) +{ +#ifdef __arch_swahb32s + __arch_swahb32s(p); +#else + *p = __swahb32p(p); +#endif +} + + +#endif /* _LINUX_SWAB_H */ diff --git a/libnl/include/linux-private/linux/taskstats.h b/libnl/include/linux-private/linux/taskstats.h new file mode 100644 index 0000000..b50b2eb --- /dev/null +++ b/libnl/include/linux-private/linux/taskstats.h @@ -0,0 +1,252 @@ +/* SPDX-License-Identifier: LGPL-2.1 WITH Linux-syscall-note */ +/* taskstats.h - exporting per-task statistics + * + * Copyright (C) Shailabh Nagar, IBM Corp. 2006 + * (C) Balbir Singh, IBM Corp. 2006 + * (C) Jay Lan, SGI, 2006 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef _LINUX_TASKSTATS_H +#define _LINUX_TASKSTATS_H + +#include + +/* Format for per-task data returned to userland when + * - a task exits + * - listener requests stats for a task + * + * The struct is versioned. Newer versions should only add fields to + * the bottom of the struct to maintain backward compatibility. + * + * + * To add new fields + * a) bump up TASKSTATS_VERSION + * b) add comment indicating new version number at end of struct + * c) add new fields after version comment; maintain 64-bit alignment + */ + + +#define TASKSTATS_VERSION 14 +#define TS_COMM_LEN 32 /* should be >= TASK_COMM_LEN + * in linux/sched.h */ + +struct taskstats { + + /* The version number of this struct. This field is always set to + * TAKSTATS_VERSION, which is defined in . + * Each time the struct is changed, the value should be incremented. + */ + __u16 version; + __u32 ac_exitcode; /* Exit status */ + + /* The accounting flags of a task as defined in + * Defined values are AFORK, ASU, ACOMPAT, ACORE, AXSIG, and AGROUP. + * (AGROUP since version 12). + */ + __u8 ac_flag; /* Record flags */ + __u8 ac_nice; /* task_nice */ + + /* Delay accounting fields start + * + * All values, until comment "Delay accounting fields end" are + * available only if delay accounting is enabled, even though the last + * few fields are not delays + * + * xxx_count is the number of delay values recorded + * xxx_delay_total is the corresponding cumulative delay in nanoseconds + * + * xxx_delay_total wraps around to zero on overflow + * xxx_count incremented regardless of overflow + */ + + /* Delay waiting for cpu, while runnable + * count, delay_total NOT updated atomically + */ + __u64 cpu_count __attribute__((aligned(8))); + __u64 cpu_delay_total; + + /* Following four fields atomically updated using task->delays->lock */ + + /* Delay waiting for synchronous block I/O to complete + * does not account for delays in I/O submission + */ + __u64 blkio_count; + __u64 blkio_delay_total; + + /* Delay waiting for page fault I/O (swap in only) */ + __u64 swapin_count; + __u64 swapin_delay_total; + + /* cpu "wall-clock" running time + * On some architectures, value will adjust for cpu time stolen + * from the kernel in involuntary waits due to virtualization. + * Value is cumulative, in nanoseconds, without a corresponding count + * and wraps around to zero silently on overflow + */ + __u64 cpu_run_real_total; + + /* cpu "virtual" running time + * Uses time intervals seen by the kernel i.e. no adjustment + * for kernel's involuntary waits due to virtualization. + * Value is cumulative, in nanoseconds, without a corresponding count + * and wraps around to zero silently on overflow + */ + __u64 cpu_run_virtual_total; + /* Delay accounting fields end */ + /* version 1 ends here */ + + /* Basic Accounting Fields start */ + char ac_comm[TS_COMM_LEN]; /* Command name */ + __u8 ac_sched __attribute__((aligned(8))); + /* Scheduling discipline */ + __u8 ac_pad[3]; + __u32 ac_uid __attribute__((aligned(8))); + /* User ID */ + __u32 ac_gid; /* Group ID */ + __u32 ac_pid; /* Process ID */ + __u32 ac_ppid; /* Parent process ID */ + /* __u32 range means times from 1970 to 2106 */ + __u32 ac_btime; /* Begin time [sec since 1970] */ + __u64 ac_etime __attribute__((aligned(8))); + /* Elapsed time [usec] */ + __u64 ac_utime; /* User CPU time [usec] */ + __u64 ac_stime; /* SYstem CPU time [usec] */ + __u64 ac_minflt; /* Minor Page Fault Count */ + __u64 ac_majflt; /* Major Page Fault Count */ + /* Basic Accounting Fields end */ + + /* Extended accounting fields start */ + /* Accumulated RSS usage in duration of a task, in MBytes-usecs. + * The current rss usage is added to this counter every time + * a tick is charged to a task's system time. So, at the end we + * will have memory usage multiplied by system time. Thus an + * average usage per system time unit can be calculated. + */ + __u64 coremem; /* accumulated RSS usage in MB-usec */ + /* Accumulated virtual memory usage in duration of a task. + * Same as acct_rss_mem1 above except that we keep track of VM usage. + */ + __u64 virtmem; /* accumulated VM usage in MB-usec */ + + /* High watermark of RSS and virtual memory usage in duration of + * a task, in KBytes. + */ + __u64 hiwater_rss; /* High-watermark of RSS usage, in KB */ + __u64 hiwater_vm; /* High-water VM usage, in KB */ + + /* The following four fields are I/O statistics of a task. */ + __u64 read_char; /* bytes read */ + __u64 write_char; /* bytes written */ + __u64 read_syscalls; /* read syscalls */ + __u64 write_syscalls; /* write syscalls */ + /* Extended accounting fields end */ + +#define TASKSTATS_HAS_IO_ACCOUNTING + /* Per-task storage I/O accounting starts */ + __u64 read_bytes; /* bytes of read I/O */ + __u64 write_bytes; /* bytes of write I/O */ + __u64 cancelled_write_bytes; /* bytes of cancelled write I/O */ + + __u64 nvcsw; /* voluntary_ctxt_switches */ + __u64 nivcsw; /* nonvoluntary_ctxt_switches */ + + /* time accounting for SMT machines */ + __u64 ac_utimescaled; /* utime scaled on frequency etc */ + __u64 ac_stimescaled; /* stime scaled on frequency etc */ + __u64 cpu_scaled_run_real_total; /* scaled cpu_run_real_total */ + + /* Delay waiting for memory reclaim */ + __u64 freepages_count; + __u64 freepages_delay_total; + + /* Delay waiting for thrashing page */ + __u64 thrashing_count; + __u64 thrashing_delay_total; + + /* v10: 64-bit btime to avoid overflow */ + __u64 ac_btime64; /* 64-bit begin time */ + + /* v11: Delay waiting for memory compact */ + __u64 compact_count; + __u64 compact_delay_total; + + /* v12 begin */ + __u32 ac_tgid; /* thread group ID */ + /* Thread group walltime up to now. This is total process walltime if + * AGROUP flag is set. + */ + __u64 ac_tgetime __attribute__((aligned(8))); + /* Lightweight information to identify process binary files. + * This leaves userspace to match this to a file system path, using + * MAJOR() and MINOR() macros to identify a device and mount point, + * the inode to identify the executable file. This is /proc/self/exe + * at the end, so matching the most recent exec(). Values are zero + * for kernel threads. + */ + __u64 ac_exe_dev; /* program binary device ID */ + __u64 ac_exe_inode; /* program binary inode number */ + /* v12 end */ + + /* v13: Delay waiting for write-protect copy */ + __u64 wpcopy_count; + __u64 wpcopy_delay_total; + + /* v14: Delay waiting for IRQ/SOFTIRQ */ + __u64 irq_count; + __u64 irq_delay_total; +}; + + +/* + * Commands sent from userspace + * Not versioned. New commands should only be inserted at the enum's end + * prior to __TASKSTATS_CMD_MAX + */ + +enum { + TASKSTATS_CMD_UNSPEC = 0, /* Reserved */ + TASKSTATS_CMD_GET, /* user->kernel request/get-response */ + TASKSTATS_CMD_NEW, /* kernel->user event */ + __TASKSTATS_CMD_MAX, +}; + +#define TASKSTATS_CMD_MAX (__TASKSTATS_CMD_MAX - 1) + +enum { + TASKSTATS_TYPE_UNSPEC = 0, /* Reserved */ + TASKSTATS_TYPE_PID, /* Process id */ + TASKSTATS_TYPE_TGID, /* Thread group id */ + TASKSTATS_TYPE_STATS, /* taskstats structure */ + TASKSTATS_TYPE_AGGR_PID, /* contains pid + stats */ + TASKSTATS_TYPE_AGGR_TGID, /* contains tgid + stats */ + TASKSTATS_TYPE_NULL, /* contains nothing */ + __TASKSTATS_TYPE_MAX, +}; + +#define TASKSTATS_TYPE_MAX (__TASKSTATS_TYPE_MAX - 1) + +enum { + TASKSTATS_CMD_ATTR_UNSPEC = 0, + TASKSTATS_CMD_ATTR_PID, + TASKSTATS_CMD_ATTR_TGID, + TASKSTATS_CMD_ATTR_REGISTER_CPUMASK, + TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK, + __TASKSTATS_CMD_ATTR_MAX, +}; + +#define TASKSTATS_CMD_ATTR_MAX (__TASKSTATS_CMD_ATTR_MAX - 1) + +/* NETLINK_GENERIC related info */ + +#define TASKSTATS_GENL_NAME "TASKSTATS" +#define TASKSTATS_GENL_VERSION 0x1 + +#endif /* _LINUX_TASKSTATS_H */ diff --git a/libnl/include/linux-private/linux/tc_act/tc_gact.h b/libnl/include/linux-private/linux/tc_act/tc_gact.h new file mode 100644 index 0000000..37e5392 --- /dev/null +++ b/libnl/include/linux-private/linux/tc_act/tc_gact.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_GACT_H +#define __LINUX_TC_GACT_H + +#include +#include + +struct tc_gact { + tc_gen; + +}; + +struct tc_gact_p { +#define PGACT_NONE 0 +#define PGACT_NETRAND 1 +#define PGACT_DETERM 2 +#define MAX_RAND (PGACT_DETERM + 1 ) + __u16 ptype; + __u16 pval; + int paction; +}; + +enum { + TCA_GACT_UNSPEC, + TCA_GACT_TM, + TCA_GACT_PARMS, + TCA_GACT_PROB, + TCA_GACT_PAD, + __TCA_GACT_MAX +}; +#define TCA_GACT_MAX (__TCA_GACT_MAX - 1) + +#endif diff --git a/libnl/include/linux-private/linux/tc_act/tc_mirred.h b/libnl/include/linux-private/linux/tc_act/tc_mirred.h new file mode 100644 index 0000000..2500a00 --- /dev/null +++ b/libnl/include/linux-private/linux/tc_act/tc_mirred.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_MIR_H +#define __LINUX_TC_MIR_H + +#include +#include + +#define TCA_EGRESS_REDIR 1 /* packet redirect to EGRESS*/ +#define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */ +#define TCA_INGRESS_REDIR 3 /* packet redirect to INGRESS*/ +#define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */ + +struct tc_mirred { + tc_gen; + int eaction; /* one of IN/EGRESS_MIRROR/REDIR */ + __u32 ifindex; /* ifindex of egress port */ +}; + +enum { + TCA_MIRRED_UNSPEC, + TCA_MIRRED_TM, + TCA_MIRRED_PARMS, + TCA_MIRRED_PAD, + __TCA_MIRRED_MAX +}; +#define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1) + +#endif diff --git a/libnl/include/linux-private/linux/tc_act/tc_nat.h b/libnl/include/linux-private/linux/tc_act/tc_nat.h new file mode 100644 index 0000000..21399c2 --- /dev/null +++ b/libnl/include/linux-private/linux/tc_act/tc_nat.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_NAT_H +#define __LINUX_TC_NAT_H + +#include +#include + +enum { + TCA_NAT_UNSPEC, + TCA_NAT_PARMS, + TCA_NAT_TM, + TCA_NAT_PAD, + __TCA_NAT_MAX +}; +#define TCA_NAT_MAX (__TCA_NAT_MAX - 1) + +#define TCA_NAT_FLAG_EGRESS 1 + +struct tc_nat { + tc_gen; + __be32 old_addr; + __be32 new_addr; + __be32 mask; + __u32 flags; +}; + +#endif diff --git a/libnl/include/linux-private/linux/tc_act/tc_skbedit.h b/libnl/include/linux-private/linux/tc_act/tc_skbedit.h new file mode 100644 index 0000000..6403251 --- /dev/null +++ b/libnl/include/linux-private/linux/tc_act/tc_skbedit.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (c) 2008, Intel Corporation. + * + * Author: Alexander Duyck + */ + +#ifndef __LINUX_TC_SKBEDIT_H +#define __LINUX_TC_SKBEDIT_H + +#include + +#define SKBEDIT_F_PRIORITY 0x1 +#define SKBEDIT_F_QUEUE_MAPPING 0x2 +#define SKBEDIT_F_MARK 0x4 +#define SKBEDIT_F_PTYPE 0x8 +#define SKBEDIT_F_MASK 0x10 +#define SKBEDIT_F_INHERITDSFIELD 0x20 +#define SKBEDIT_F_TXQ_SKBHASH 0x40 + +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_QUEUE_MAPPING_MAX, + __TCA_SKBEDIT_MAX +}; +#define TCA_SKBEDIT_MAX (__TCA_SKBEDIT_MAX - 1) + +#endif diff --git a/libnl/include/linux-private/linux/tc_act/tc_vlan.h b/libnl/include/linux-private/linux/tc_act/tc_vlan.h new file mode 100644 index 0000000..3e1f8e5 --- /dev/null +++ b/libnl/include/linux-private/linux/tc_act/tc_vlan.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Copyright (c) 2014 Jiri Pirko + */ + +#ifndef __LINUX_TC_VLAN_H +#define __LINUX_TC_VLAN_H + +#include + +#define TCA_VLAN_ACT_POP 1 +#define TCA_VLAN_ACT_PUSH 2 +#define TCA_VLAN_ACT_MODIFY 3 +#define TCA_VLAN_ACT_POP_ETH 4 +#define TCA_VLAN_ACT_PUSH_ETH 5 + +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_PUSH_ETH_DST, + TCA_VLAN_PUSH_ETH_SRC, + __TCA_VLAN_MAX, +}; +#define TCA_VLAN_MAX (__TCA_VLAN_MAX - 1) + +#endif diff --git a/libnl/include/linux-private/linux/tc_ematch/tc_em_cmp.h b/libnl/include/linux-private/linux/tc_ematch/tc_em_cmp.h new file mode 100644 index 0000000..2549d9d --- /dev/null +++ b/libnl/include/linux-private/linux/tc_ematch/tc_em_cmp.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_EM_CMP_H +#define __LINUX_TC_EM_CMP_H + +#include +#include + +struct tcf_em_cmp { + __u32 val; + __u32 mask; + __u16 off; + __u8 align:4; + __u8 flags:4; + __u8 layer:4; + __u8 opnd:4; +}; + +enum { + TCF_EM_ALIGN_U8 = 1, + TCF_EM_ALIGN_U16 = 2, + TCF_EM_ALIGN_U32 = 4 +}; + +#define TCF_EM_CMP_TRANS 1 + +#endif diff --git a/libnl/include/linux-private/linux/tc_ematch/tc_em_meta.h b/libnl/include/linux-private/linux/tc_ematch/tc_em_meta.h new file mode 100644 index 0000000..cf30b5b --- /dev/null +++ b/libnl/include/linux-private/linux/tc_ematch/tc_em_meta.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_EM_META_H +#define __LINUX_TC_EM_META_H + +#include +#include + +enum { + TCA_EM_META_UNSPEC, + TCA_EM_META_HDR, + TCA_EM_META_LVALUE, + TCA_EM_META_RVALUE, + __TCA_EM_META_MAX +}; +#define TCA_EM_META_MAX (__TCA_EM_META_MAX - 1) + +struct tcf_meta_val { + __u16 kind; + __u8 shift; + __u8 op; +}; + +#define TCF_META_TYPE_MASK (0xf << 12) +#define TCF_META_TYPE(kind) (((kind) & TCF_META_TYPE_MASK) >> 12) +#define TCF_META_ID_MASK 0x7ff +#define TCF_META_ID(kind) ((kind) & TCF_META_ID_MASK) + +enum { + TCF_META_TYPE_VAR, + TCF_META_TYPE_INT, + __TCF_META_TYPE_MAX +}; +#define TCF_META_TYPE_MAX (__TCF_META_TYPE_MAX - 1) + +enum { + TCF_META_ID_VALUE, + TCF_META_ID_RANDOM, + TCF_META_ID_LOADAVG_0, + TCF_META_ID_LOADAVG_1, + TCF_META_ID_LOADAVG_2, + TCF_META_ID_DEV, + TCF_META_ID_PRIORITY, + TCF_META_ID_PROTOCOL, + TCF_META_ID_PKTTYPE, + TCF_META_ID_PKTLEN, + TCF_META_ID_DATALEN, + TCF_META_ID_MACLEN, + TCF_META_ID_NFMARK, + TCF_META_ID_TCINDEX, + TCF_META_ID_RTCLASSID, + TCF_META_ID_RTIIF, + TCF_META_ID_SK_FAMILY, + TCF_META_ID_SK_STATE, + TCF_META_ID_SK_REUSE, + TCF_META_ID_SK_BOUND_IF, + TCF_META_ID_SK_REFCNT, + TCF_META_ID_SK_SHUTDOWN, + TCF_META_ID_SK_PROTO, + TCF_META_ID_SK_TYPE, + TCF_META_ID_SK_RCVBUF, + TCF_META_ID_SK_RMEM_ALLOC, + TCF_META_ID_SK_WMEM_ALLOC, + TCF_META_ID_SK_OMEM_ALLOC, + TCF_META_ID_SK_WMEM_QUEUED, + TCF_META_ID_SK_RCV_QLEN, + TCF_META_ID_SK_SND_QLEN, + TCF_META_ID_SK_ERR_QLEN, + TCF_META_ID_SK_FORWARD_ALLOCS, + TCF_META_ID_SK_SNDBUF, + TCF_META_ID_SK_ALLOCS, + __TCF_META_ID_SK_ROUTE_CAPS, /* unimplemented but in ABI already */ + TCF_META_ID_SK_HASH, + TCF_META_ID_SK_LINGERTIME, + TCF_META_ID_SK_ACK_BACKLOG, + TCF_META_ID_SK_MAX_ACK_BACKLOG, + TCF_META_ID_SK_PRIO, + TCF_META_ID_SK_RCVLOWAT, + TCF_META_ID_SK_RCVTIMEO, + TCF_META_ID_SK_SNDTIMEO, + TCF_META_ID_SK_SENDMSG_OFF, + TCF_META_ID_SK_WRITE_PENDING, + TCF_META_ID_VLAN_TAG, + TCF_META_ID_RXHASH, + __TCF_META_ID_MAX +}; +#define TCF_META_ID_MAX (__TCF_META_ID_MAX - 1) + +struct tcf_meta_hdr { + struct tcf_meta_val left; + struct tcf_meta_val right; +}; + +#endif diff --git a/libnl/include/linux-private/linux/tc_ematch/tc_em_nbyte.h b/libnl/include/linux-private/linux/tc_ematch/tc_em_nbyte.h new file mode 100644 index 0000000..c76333f --- /dev/null +++ b/libnl/include/linux-private/linux/tc_ematch/tc_em_nbyte.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_EM_NBYTE_H +#define __LINUX_TC_EM_NBYTE_H + +#include +#include + +struct tcf_em_nbyte { + __u16 off; + __u16 len:12; + __u8 layer:4; +}; + +#endif diff --git a/libnl/include/linux-private/linux/tc_ematch/tc_em_text.h b/libnl/include/linux-private/linux/tc_ematch/tc_em_text.h new file mode 100644 index 0000000..b0a9225 --- /dev/null +++ b/libnl/include/linux-private/linux/tc_ematch/tc_em_text.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_EM_TEXT_H +#define __LINUX_TC_EM_TEXT_H + +#include +#include + +#define TC_EM_TEXT_ALGOSIZ 16 + +struct tcf_em_text { + char algo[TC_EM_TEXT_ALGOSIZ]; + __u16 from_offset; + __u16 to_offset; + __u16 pattern_len; + __u8 from_layer:4; + __u8 to_layer:4; + __u8 pad; +}; + +#endif diff --git a/libnl/include/linux-private/linux/types.h b/libnl/include/linux-private/linux/types.h new file mode 100644 index 0000000..6546100 --- /dev/null +++ b/libnl/include/linux-private/linux/types.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_TYPES_H +#define _LINUX_TYPES_H + +#include + +#ifndef __ASSEMBLY__ + +#include + + +/* + * Below are truly Linux-specific types that should never collide with + * any application/library that wants linux/types.h. + */ + +/* sparse defines __CHECKER__; see Documentation/dev-tools/sparse.rst */ +#ifdef __CHECKER__ +#define __bitwise __attribute__((bitwise)) +#else +#define __bitwise +#endif + +/* The kernel doesn't use this legacy form, but user space does */ +#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/libnl/include/linux-private/linux/version.h b/libnl/include/linux-private/linux/version.h new file mode 100644 index 0000000..4ae7bc0 --- /dev/null +++ b/libnl/include/linux-private/linux/version.h @@ -0,0 +1,5 @@ +#define LINUX_VERSION_CODE 394240 +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + ((c) > 255 ? 255 : (c))) +#define LINUX_VERSION_MAJOR 6 +#define LINUX_VERSION_PATCHLEVEL 4 +#define LINUX_VERSION_SUBLEVEL 0 diff --git a/libnl/include/linux-private/linux/veth.h b/libnl/include/linux-private/linux/veth.h new file mode 100644 index 0000000..52b58e5 --- /dev/null +++ b/libnl/include/linux-private/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/libnl/include/linux-private/linux/xfrm.h b/libnl/include/linux-private/linux/xfrm.h new file mode 100644 index 0000000..10bdef8 --- /dev/null +++ b/libnl/include/linux-private/linux/xfrm.h @@ -0,0 +1,568 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_XFRM_H +#define _LINUX_XFRM_H + +#include +#include + +/* All of the structures in this file may not change size as they are + * passed into the kernel from userspace via netlink sockets. + */ + +/* Structure to encapsulate addresses. I do not want to use + * "standard" structure. My apologies. + */ +typedef union { + __be32 a4; + __be32 a6[4]; + struct in6_addr in6; +} xfrm_address_t; + +/* Ident of a specific xfrm_state. It is used on input to lookup + * the state by (spi,daddr,ah/esp) or to store information about + * spi, protocol and tunnel address on output. + */ +struct xfrm_id { + xfrm_address_t daddr; + __be32 spi; + __u8 proto; +}; + +struct xfrm_sec_ctx { + __u8 ctx_doi; + __u8 ctx_alg; + __u16 ctx_len; + __u32 ctx_sid; + char ctx_str[]; +}; + +/* 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[]; +}; + +struct xfrm_algo { + char alg_name[64]; + unsigned int alg_key_len; /* in bits */ + char alg_key[]; +}; + +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[]; +}; + +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[]; +}; + +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_SETDEFAULT, +#define XFRM_MSG_SETDEFAULT XFRM_MSG_SETDEFAULT + XFRM_MSG_GETDEFAULT, +#define XFRM_MSG_GETDEFAULT XFRM_MSG_GETDEFAULT + __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, /* __u64 */ + XFRMA_POLICY_TYPE, /* struct xfrm_userpolicy_type */ + XFRMA_MIGRATE, + XFRMA_ALG_AEAD, /* struct xfrm_algo_aead */ + XFRMA_KMADDRESS, /* struct xfrm_user_kmaddress */ + XFRMA_ALG_AUTH_TRUNC, /* struct xfrm_algo_auth */ + XFRMA_MARK, /* struct xfrm_mark */ + XFRMA_TFCPAD, /* __u32 */ + XFRMA_REPLAY_ESN_VAL, /* struct xfrm_replay_state_esn */ + XFRMA_SA_EXTRA_FLAGS, /* __u32 */ + XFRMA_PROTO, /* __u8 */ + XFRMA_ADDRESS_FILTER, /* struct xfrm_address_filter */ + XFRMA_PAD, + XFRMA_OFFLOAD_DEV, /* struct xfrm_user_offload */ + XFRMA_SET_MARK, /* __u32 */ + XFRMA_SET_MARK_MASK, /* __u32 */ + XFRMA_IF_ID, /* __u32 */ + XFRMA_MTIMER_THRESH, /* __u32 in seconds for input SA */ + __XFRMA_MAX + +#define XFRMA_OUTPUT_MARK XFRMA_SET_MARK /* Compatibility */ +#define XFRMA_MAX (__XFRMA_MAX - 1) +}; + +struct xfrm_mark { + __u32 v; /* value */ + __u32 m; /* mask */ +}; + +enum xfrm_sadattr_type_t { + XFRMA_SAD_UNSPEC, + XFRMA_SAD_CNT, + XFRMA_SAD_HINFO, + __XFRMA_SAD_MAX + +#define XFRMA_SAD_MAX (__XFRMA_SAD_MAX - 1) +}; + +struct xfrmu_sadhinfo { + __u32 sadhcnt; /* current hash bkts */ + __u32 sadhmcnt; /* max allowed hash bkts */ +}; + +enum xfrm_spdattr_type_t { + XFRMA_SPD_UNSPEC, + XFRMA_SPD_INFO, + XFRMA_SPD_HINFO, + XFRMA_SPD_IPV4_HTHRESH, + XFRMA_SPD_IPV6_HTHRESH, + __XFRMA_SPD_MAX + +#define XFRMA_SPD_MAX (__XFRMA_SPD_MAX - 1) +}; + +struct xfrmu_spdinfo { + __u32 incnt; + __u32 outcnt; + __u32 fwdcnt; + __u32 inscnt; + __u32 outscnt; + __u32 fwdscnt; +}; + +struct xfrmu_spdhinfo { + __u32 spdhcnt; + __u32 spdhmcnt; +}; + +struct xfrmu_spdhthresh { + __u8 lbits; + __u8 rbits; +}; + +struct xfrm_usersa_info { + struct xfrm_selector sel; + struct xfrm_id id; + xfrm_address_t saddr; + struct xfrm_lifetime_cfg lft; + struct xfrm_lifetime_cur curlft; + struct xfrm_stats stats; + __u32 seq; + __u32 reqid; + __u16 family; + __u8 mode; /* XFRM_MODE_xxx */ + __u8 replay_window; + __u8 flags; +#define XFRM_STATE_NOECN 1 +#define XFRM_STATE_DECAP_DSCP 2 +#define XFRM_STATE_NOPMTUDISC 4 +#define XFRM_STATE_WILDRECV 8 +#define XFRM_STATE_ICMP 16 +#define XFRM_STATE_AF_UNSPEC 32 +#define XFRM_STATE_ALIGN4 64 +#define XFRM_STATE_ESN 128 +}; + +#define XFRM_SA_XFLAG_DONT_ENCAP_DSCP 1 +#define XFRM_SA_XFLAG_OSEQ_MAY_WRAP 2 + +struct xfrm_usersa_id { + xfrm_address_t daddr; + __be32 spi; + __u16 family; + __u8 proto; +}; + +struct xfrm_aevent_id { + struct xfrm_usersa_id sa_id; + xfrm_address_t saddr; + __u32 flags; + __u32 reqid; +}; + +struct xfrm_userspi_info { + struct xfrm_usersa_info info; + __u32 min; + __u32 max; +}; + +struct xfrm_userpolicy_info { + struct xfrm_selector sel; + struct xfrm_lifetime_cfg lft; + struct xfrm_lifetime_cur curlft; + __u32 priority; + __u32 index; + __u8 dir; + __u8 action; +#define XFRM_POLICY_ALLOW 0 +#define XFRM_POLICY_BLOCK 1 + __u8 flags; +#define XFRM_POLICY_LOCALOK 1 /* Allow user to override global policy */ + /* Automatically expand selector to include matching ICMP payloads. */ +#define XFRM_POLICY_ICMP 2 + __u8 share; +}; + +struct xfrm_userpolicy_id { + struct xfrm_selector sel; + __u32 index; + __u8 dir; +}; + +struct xfrm_user_acquire { + struct xfrm_id id; + xfrm_address_t saddr; + struct xfrm_selector sel; + struct xfrm_userpolicy_info policy; + __u32 aalgos; + __u32 ealgos; + __u32 calgos; + __u32 seq; +}; + +struct xfrm_user_expire { + struct xfrm_usersa_info state; + __u8 hard; +}; + +struct xfrm_user_polexpire { + struct xfrm_userpolicy_info pol; + __u8 hard; +}; + +struct xfrm_usersa_flush { + __u8 proto; +}; + +struct xfrm_user_report { + __u8 proto; + struct xfrm_selector sel; +}; + +/* Used by MIGRATE to pass addresses IKE should use to perform + * SA negotiation with the peer */ +struct xfrm_user_kmaddress { + xfrm_address_t local; + xfrm_address_t remote; + __u32 reserved; + __u16 family; +}; + +struct xfrm_user_migrate { + xfrm_address_t old_daddr; + xfrm_address_t old_saddr; + xfrm_address_t new_daddr; + xfrm_address_t new_saddr; + __u8 proto; + __u8 mode; + __u16 reserved; + __u32 reqid; + __u16 old_family; + __u16 new_family; +}; + +struct xfrm_user_mapping { + struct xfrm_usersa_id id; + __u32 reqid; + xfrm_address_t old_saddr; + xfrm_address_t new_saddr; + __be16 old_sport; + __be16 new_sport; +}; + +struct xfrm_address_filter { + xfrm_address_t saddr; + xfrm_address_t daddr; + __u16 family; + __u8 splen; + __u8 dplen; +}; + +struct xfrm_user_offload { + int ifindex; + __u8 flags; +}; +/* This flag was exposed without any kernel code that supports it. + * Unfortunately, strongswan has the code that sets this flag, + * which makes it impossible to reuse this bit. + * + * So leave it here to make sure that it won't be reused by mistake. + */ +#define XFRM_OFFLOAD_IPV6 1 +#define XFRM_OFFLOAD_INBOUND 2 +/* Two bits above are relevant for state path only, while + * offload is used for both policy and state flows. + * + * In policy offload mode, they are free and can be safely reused. + */ +#define XFRM_OFFLOAD_PACKET 4 + +struct xfrm_userpolicy_default { +#define XFRM_USERPOLICY_UNSPEC 0 +#define XFRM_USERPOLICY_BLOCK 1 +#define XFRM_USERPOLICY_ACCEPT 2 + __u8 in; + __u8 fwd; + __u8 out; +}; + +/* 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/libnl/include/netlink/addr.h b/libnl/include/netlink/addr.h new file mode 100644 index 0000000..851e940 --- /dev/null +++ b/libnl/include/netlink/addr.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_ADDR_H_ +#define NETLINK_ADDR_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nlattr; + +struct nl_addr; + +/* Creation */ +extern struct nl_addr * nl_addr_alloc(size_t); +extern struct nl_addr * nl_addr_alloc_attr(const struct nlattr *, int); +extern struct nl_addr * nl_addr_build(int, const void *, size_t); +extern int nl_addr_parse(const char *, int, struct nl_addr **); +extern struct nl_addr * nl_addr_clone(const struct nl_addr *); + +/* Usage Management */ +extern struct nl_addr * nl_addr_get(struct nl_addr *); +extern void nl_addr_put(struct nl_addr *); +extern int nl_addr_shared(const struct nl_addr *); + +extern int nl_addr_cmp(const struct nl_addr *, + const struct nl_addr *); +extern int nl_addr_cmp_prefix(const struct nl_addr *, + const struct nl_addr *); +extern int nl_addr_iszero(const struct nl_addr *); +extern int nl_addr_valid(const char *, int); +extern int nl_addr_guess_family(const struct nl_addr *); +extern int nl_addr_fill_sockaddr(const struct nl_addr *, + struct sockaddr *, socklen_t *); +extern int nl_addr_info(const struct nl_addr *, + struct addrinfo **); +extern int nl_addr_resolve(const struct nl_addr *, char *, size_t); + +/* Access Functions */ +extern void nl_addr_set_family(struct nl_addr *, int); +extern int nl_addr_get_family(const struct nl_addr *); +extern int nl_addr_set_binary_addr(struct nl_addr *, const void *, + size_t); +extern void * nl_addr_get_binary_addr(const struct nl_addr *); +extern unsigned int nl_addr_get_len(const struct nl_addr *); +extern void nl_addr_set_prefixlen(struct nl_addr *, int); +extern unsigned int nl_addr_get_prefixlen(const struct nl_addr *); + +/* Address Family Translations */ +extern char * nl_af2str(int, char *, size_t); +extern int nl_str2af(const char *); + +/* Translations to Strings */ +extern char * nl_addr2str(const struct nl_addr *, char *, size_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/attr.h b/libnl/include/netlink/attr.h new file mode 100644 index 0000000..39d7cb2 --- /dev/null +++ b/libnl/include/netlink/attr.h @@ -0,0 +1,342 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_ATTR_H_ +#define NETLINK_ATTR_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nlattr; + +struct nl_msg; + +/** + * @name Basic Attribute Data Types + * @{ + */ + +/** + * @ingroup attr + * Basic attribute data types + * + * See section @core_doc{core_attr_parse,Attribute Parsing} for more details. + */ +enum { + NLA_UNSPEC, /**< Unspecified type, binary data chunk */ + NLA_U8, /**< 8 bit integer */ + NLA_U16, /**< 16 bit integer */ + NLA_U32, /**< 32 bit integer */ + NLA_U64, /**< 64 bit integer */ + NLA_STRING, /**< NUL terminated character string */ + NLA_FLAG, /**< Flag */ + NLA_MSECS, /**< Micro seconds (64bit) */ + NLA_NESTED, /**< Nested attributes */ + NLA_NESTED_COMPAT, + NLA_NUL_STRING, + NLA_BINARY, + NLA_S8, + NLA_S16, + NLA_S32, + NLA_S64, + /* Skip unimplemented attr types */ + NLA_SINT = 20, + NLA_UINT, + __NLA_TYPE_MAX, +}; + +#define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1) + +/** @} */ + +/** + * @ingroup attr + * Attribute validation policy. + * + * See section @core_doc{core_attr_parse,Attribute Parsing} for more details. + */ +struct nla_policy { + /** Type of attribute or NLA_UNSPEC */ + uint16_t type; + + /** Minimal length of payload required */ + uint16_t minlen; + + /** Maximal length of payload allowed */ + uint16_t maxlen; +}; + +/* Size calculations */ +extern int nla_attr_size(int payload); +extern int nla_total_size(int payload); +extern int nla_padlen(int payload); + +/* Attribute parsing */ +extern int nla_type(const struct nlattr *); +extern void * nla_data(const struct nlattr *); +extern int nla_len(const struct nlattr *); +extern int nla_ok(const struct nlattr *, int); +extern struct nlattr * nla_next(const struct nlattr *, int *); +extern int nla_parse(struct nlattr **, int, struct nlattr *, + int, const struct nla_policy *); +extern int nla_validate(const struct nlattr *, int, int, + const struct nla_policy *); +extern struct nlattr * nla_find(const struct nlattr *, int, int); + +/* Helper Functions */ +extern int nla_memcpy(void *, const struct nlattr *, int); +extern size_t nla_strlcpy(char *, const struct nlattr *, size_t); +extern int nla_memcmp(const struct nlattr *, const void *, size_t); +extern int nla_strcmp(const struct nlattr *, const char *); + +/* Unspecific attribute */ +extern struct nlattr * nla_reserve(struct nl_msg *, int, int); +extern int nla_put(struct nl_msg *, int, int, const void *); +extern int nla_put_data(struct nl_msg *, int, + const struct nl_data *); +extern int nla_put_addr(struct nl_msg *, int, struct nl_addr *); + +/* Integer attribute */ +extern int8_t nla_get_s8(const struct nlattr *); +extern int nla_put_s8(struct nl_msg *, int, int8_t); +extern uint8_t nla_get_u8(const struct nlattr *); +extern int nla_put_u8(struct nl_msg *, int, uint8_t); +extern int16_t nla_get_s16(const struct nlattr *); +extern int nla_put_s16(struct nl_msg *, int, int16_t); +extern uint16_t nla_get_u16(const struct nlattr *); +extern int nla_put_u16(struct nl_msg *, int, uint16_t); +extern int32_t nla_get_s32(const struct nlattr *); +extern int nla_put_s32(struct nl_msg *, int, int32_t); +extern uint32_t nla_get_u32(const struct nlattr *); +extern int nla_put_u32(struct nl_msg *, int, uint32_t); +extern int64_t nla_get_s64(const struct nlattr *); +extern int nla_put_s64(struct nl_msg *, int, int64_t); +extern uint64_t nla_get_u64(const struct nlattr *); +extern int nla_put_u64(struct nl_msg *, int, uint64_t); +extern int64_t nla_get_sint(const struct nlattr *); +extern int nla_put_sint(struct nl_msg *, int, int64_t); +extern uint64_t nla_get_uint(const struct nlattr *); +extern int nla_put_uint(struct nl_msg *, int, uint64_t); + +/* String attribute */ +extern char * nla_get_string(const struct nlattr *); +extern char * nla_strdup(const struct nlattr *); +extern int nla_put_string(struct nl_msg *, int, const char *); + +/* Flag attribute */ +extern int nla_get_flag(const struct nlattr *); +extern int nla_put_flag(struct nl_msg *, int); + +/* Msec attribute */ +extern unsigned long nla_get_msecs(const struct nlattr *); +extern int nla_put_msecs(struct nl_msg *, int, unsigned long); + +/* Attribute nesting */ +extern int nla_put_nested(struct nl_msg *, int, + const struct nl_msg *); +extern struct nlattr * nla_nest_start(struct nl_msg *, int); +extern int nla_nest_end(struct nl_msg *, struct nlattr *); +extern int nla_nest_end_keep_empty(struct nl_msg *, struct nlattr *); +extern void nla_nest_cancel(struct nl_msg *, const struct nlattr *); +extern int nla_parse_nested(struct nlattr **, int, struct nlattr *, + const struct nla_policy *); +extern int nla_is_nested(const struct nlattr *); + +/** + * @name Attribute Construction (Exception Based) + * @{ + */ + +/** + * @ingroup attr + * Add unspecific attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg attrlen Length of attribute payload. + * @arg data Head of attribute payload. + */ +#define NLA_PUT(msg, attrtype, attrlen, data) \ + do { \ + if (nla_put(msg, attrtype, attrlen, data) < 0) \ + goto nla_put_failure; \ + } while(0) + +/** + * @ingroup attr + * Add atomic type attribute to netlink message. + * @arg msg Netlink message. + * @arg type Atomic type. + * @arg attrtype Attribute type. + * @arg value Head of attribute payload. + */ +#define NLA_PUT_TYPE(msg, type, attrtype, value) \ + do { \ + type __tmp = value; \ + NLA_PUT(msg, attrtype, sizeof(type), &__tmp); \ + } while(0) + +/** + * Add 8 bit signed integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value. + */ +#define NLA_PUT_S8(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, int8_t, attrtype, value) + +/** + * Add 8 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value. + */ +#define NLA_PUT_U8(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, uint8_t, attrtype, value) + +/** + * Add 16 bit signed integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value. + */ +#define NLA_PUT_S16(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, int16_t, attrtype, value) + +/** + * Add 16 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value. + */ +#define NLA_PUT_U16(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, uint16_t, attrtype, value) + +/** + * Add 32 bit signed integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value. + */ +#define NLA_PUT_S32(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, int32_t, attrtype, value) + +/** + * Add 32 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value. + */ +#define NLA_PUT_U32(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, uint32_t, attrtype, value) + +/** + * Add 64 bit signed integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value. + */ +#define NLA_PUT_S64(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, int64_t, attrtype, value) + +/** + * Add 64 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value. + */ +#define NLA_PUT_U64(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, uint64_t, attrtype, value) + +/** + * Add string attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value NUL terminated character string. + */ +#define NLA_PUT_STRING(msg, attrtype, value) \ + NLA_PUT(msg, attrtype, (int) strlen(value) + 1, value) + +/** + * Add flag attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + */ +#define NLA_PUT_FLAG(msg, attrtype) \ + NLA_PUT(msg, attrtype, 0, NULL) + +/** + * Add msecs attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg msecs Numeric value in micro seconds. + */ +#define NLA_PUT_MSECS(msg, attrtype, msecs) \ + NLA_PUT_U64(msg, attrtype, msecs) + +/** + * Add address attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg addr Abstract address object. + */ +#define NLA_PUT_ADDR(msg, attrtype, addr) \ + NLA_PUT(msg, attrtype, nl_addr_get_len(addr), \ + nl_addr_get_binary_addr(addr)) + +/** + * Add abstract data attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg data Abstract data object. + */ +#define NLA_PUT_DATA(msg, attrtype, data) \ + NLA_PUT(msg, attrtype, nl_data_get_size(data), \ + nl_data_get(data)) + +/** @} */ + +/** + * @name Iterators + * @{ + */ + +/** + * @ingroup attr + * Iterate over a stream of attributes + * @arg pos loop counter, set to current attribute + * @arg head head of attribute stream + * @arg len length of attribute stream + * @arg rem initialized to len, holds bytes currently remaining in stream + */ +#define nla_for_each_attr(pos, head, len, rem) \ + for (pos = head, rem = len; \ + nla_ok(pos, rem); \ + pos = nla_next(pos, &(rem))) + +/** + * @ingroup attr + * Iterate over a stream of nested attributes + * @arg pos loop counter, set to current attribute + * @arg nla attribute containing the nested attributes + * @arg rem initialized to len, holds bytes currently remaining in stream + */ +#define nla_for_each_nested(pos, nla, rem) \ + for (pos = (struct nlattr *) nla_data(nla), rem = nla_len(nla); \ + nla_ok(pos, rem); \ + pos = nla_next(pos, &(rem))) + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/cache-api.h b/libnl/include/netlink/cache-api.h new file mode 100644 index 0000000..e8a4170 --- /dev/null +++ b/libnl/include/netlink/cache-api.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Thomas Graf + */ + +#ifndef NETLINK_DUMMY_CACHE_API_H_ +#define NETLINK_DUMMY_CACHE_API_H_ + +#include +#include + +#ifndef _NL_NO_WARN_DEPRECATED_HEADER +#warning "You are including a deprecated header file, include ." +#endif + +#endif diff --git a/libnl/include/netlink/cache.h b/libnl/include/netlink/cache.h new file mode 100644 index 0000000..955f979 --- /dev/null +++ b/libnl/include/netlink/cache.h @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef NETLINK_CACHE_H_ +#define NETLINK_CACHE_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + NL_ACT_UNSPEC, + NL_ACT_NEW, + NL_ACT_DEL, + NL_ACT_GET, + NL_ACT_SET, + NL_ACT_CHANGE, + __NL_ACT_MAX, +}; + +#define NL_ACT_MAX (__NL_ACT_MAX - 1) + +struct nl_cache; +typedef void (*change_func_t)(struct nl_cache *, struct nl_object *, int, void *); +typedef void (*change_func_v2_t)(struct nl_cache *, struct nl_object *old_obj, + struct nl_object *new_obj, uint64_t, int, void *); + +/** + * @ingroup cache + * Explicitely iterate over all address families when updating the cache + */ +#define NL_CACHE_AF_ITER 0x0001 + +/* Access Functions */ +extern int nl_cache_nitems(struct nl_cache *); +extern int nl_cache_nitems_filter(struct nl_cache *, + struct nl_object *); +extern struct nl_cache_ops * nl_cache_get_ops(struct nl_cache *); +extern struct nl_object * nl_cache_get_first(struct nl_cache *); +extern struct nl_object * nl_cache_get_last(struct nl_cache *); +extern struct nl_object * nl_cache_get_next(struct nl_object *); +extern struct nl_object * nl_cache_get_prev(struct nl_object *); + +extern struct nl_cache * nl_cache_alloc(struct nl_cache_ops *); +extern int nl_cache_alloc_and_fill(struct nl_cache_ops *, + struct nl_sock *, + struct nl_cache **); +extern int nl_cache_alloc_name(const char *, + struct nl_cache **); +extern struct nl_cache * nl_cache_subset(struct nl_cache *, + struct nl_object *); +extern struct nl_cache * nl_cache_clone(struct nl_cache *); +extern void nl_cache_clear(struct nl_cache *); +extern void nl_cache_get(struct nl_cache *); +extern void nl_cache_free(struct nl_cache *); +extern void nl_cache_put(struct nl_cache *cache); + +/* Cache modification */ +extern int nl_cache_add(struct nl_cache *, + struct nl_object *); +extern int nl_cache_parse_and_add(struct nl_cache *, + struct nl_msg *); +extern int nl_cache_move(struct nl_cache *, + struct nl_object *); +extern void nl_cache_remove(struct nl_object *); +extern int nl_cache_refill(struct nl_sock *, + struct nl_cache *); +extern int nl_cache_pickup(struct nl_sock *, + struct nl_cache *); +extern int nl_cache_pickup_checkdup(struct nl_sock *, + struct nl_cache *); +extern int nl_cache_resync(struct nl_sock *, + struct nl_cache *, + change_func_t, + void *); +extern int nl_cache_include(struct nl_cache *, + struct nl_object *, + change_func_t, + void *); +extern int nl_cache_include_v2(struct nl_cache *, + struct nl_object *, + change_func_v2_t, + void *); +extern void nl_cache_set_arg1(struct nl_cache *, int); +extern void nl_cache_set_arg2(struct nl_cache *, int); +extern void nl_cache_set_flags(struct nl_cache *, unsigned int); + +/* General */ +extern int nl_cache_is_empty(struct nl_cache *); +extern struct nl_object * nl_cache_search(struct nl_cache *, + struct nl_object *); +extern struct nl_object *nl_cache_find(struct nl_cache *, + struct nl_object *); +extern void nl_cache_mark_all(struct nl_cache *); + +/* Dumping */ +extern void nl_cache_dump(struct nl_cache *, + struct nl_dump_params *); +extern void nl_cache_dump_filter(struct nl_cache *, + struct nl_dump_params *, + struct nl_object *); + +/* Iterators */ +extern void nl_cache_foreach(struct nl_cache *, + void (*cb)(struct nl_object *, + void *), + void *arg); +extern void nl_cache_foreach_filter(struct nl_cache *, + struct nl_object *, + void (*cb)(struct + nl_object *, + void *), + void *arg); + +/* --- cache management --- */ + +/* Cache type management */ +extern struct nl_cache_ops * nl_cache_ops_lookup(const char *); +extern struct nl_cache_ops * nl_cache_ops_lookup_safe(const char *); +extern struct nl_cache_ops * nl_cache_ops_associate(int, int); +extern struct nl_cache_ops * nl_cache_ops_associate_safe(int, int); +extern struct nl_msgtype * nl_msgtype_lookup(struct nl_cache_ops *, int); +extern void nl_cache_ops_foreach(void (*cb)(struct nl_cache_ops *, void *), void *); +extern int nl_cache_mngt_register(struct nl_cache_ops *); +extern int nl_cache_mngt_unregister(struct nl_cache_ops *); + +/* Global cache provisioning/requiring */ +extern void nl_cache_mngt_provide(struct nl_cache *); +extern void nl_cache_mngt_unprovide(struct nl_cache *); +extern struct nl_cache * nl_cache_mngt_require(const char *); +extern struct nl_cache * nl_cache_mngt_require_safe(const char *); +extern struct nl_cache * __nl_cache_mngt_require(const char *); + +struct nl_cache_mngr; + +#define NL_AUTO_PROVIDE 1 +#define NL_ALLOCATED_SOCK 2 /* For internal use only, do not use */ + +extern int nl_cache_mngr_alloc(struct nl_sock *, + int, int, + struct nl_cache_mngr **); +extern int nl_cache_mngr_alloc_ex(struct nl_sock *, + struct nl_sock *, + int, int, + struct nl_cache_mngr **); +extern int nl_cache_mngr_add(struct nl_cache_mngr *, + const char *, + change_func_t, + void *, + struct nl_cache **); +extern int nl_cache_mngr_add_cache(struct nl_cache_mngr *mngr, + struct nl_cache *cache, + change_func_t cb, void *data); +extern int nl_cache_mngr_add_cache_v2(struct nl_cache_mngr *mngr, + struct nl_cache *cache, + change_func_v2_t cb, void *data); +extern int nl_cache_mngr_get_fd(struct nl_cache_mngr *); +extern int nl_cache_mngr_poll(struct nl_cache_mngr *, + int); +extern int nl_cache_mngr_data_ready(struct nl_cache_mngr *); +extern void nl_cache_mngr_info(struct nl_cache_mngr *, + struct nl_dump_params *); +extern void nl_cache_mngr_free(struct nl_cache_mngr *); + +extern void nl_cache_ops_get(struct nl_cache_ops *); +extern void nl_cache_ops_put(struct nl_cache_ops *); +extern void nl_cache_ops_set_flags(struct nl_cache_ops *, + unsigned int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/cli/addr.h b/libnl/include/netlink/cli/addr.h new file mode 100644 index 0000000..5fcf734 --- /dev/null +++ b/libnl/include/netlink/cli/addr.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2009 Thomas Graf + */ + +#ifndef __NETLINK_CLI_ADDR_H_ +#define __NETLINK_CLI_ADDR_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define nl_cli_addr_alloc_cache(sk) \ + nl_cli_alloc_cache((sk), "address", rtnl_addr_alloc_cache) + +extern struct rtnl_addr *nl_cli_addr_alloc(void); + +extern void nl_cli_addr_parse_family(struct rtnl_addr *, char *); +extern void nl_cli_addr_parse_local(struct rtnl_addr *, char *); +extern void nl_cli_addr_parse_dev(struct rtnl_addr *, struct nl_cache *,char *); +extern void nl_cli_addr_parse_label(struct rtnl_addr *, char *); +extern void nl_cli_addr_parse_peer(struct rtnl_addr *, char *); +extern void nl_cli_addr_parse_scope(struct rtnl_addr *, char *); +extern void nl_cli_addr_parse_broadcast(struct rtnl_addr *, char *); +extern void nl_cli_addr_parse_preferred(struct rtnl_addr *, char *); +extern void nl_cli_addr_parse_valid(struct rtnl_addr *, char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/cli/class.h b/libnl/include/netlink/cli/class.h new file mode 100644 index 0000000..9552374 --- /dev/null +++ b/libnl/include/netlink/cli/class.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#ifndef __NETLINK_CLI_CLASS_H_ +#define __NETLINK_CLI_CLASS_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_class *nl_cli_class_alloc(void); +extern struct nl_cache *nl_cli_class_alloc_cache(struct nl_sock *, int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/cli/cls.h b/libnl/include/netlink/cli/cls.h new file mode 100644 index 0000000..602b198 --- /dev/null +++ b/libnl/include/netlink/cli/cls.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#ifndef __NETLINK_CLI_CLS_H_ +#define __NETLINK_CLI_CLS_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_cls * nl_cli_cls_alloc(void); +extern struct nl_cache * nl_cli_cls_alloc_cache(struct nl_sock *, + int, uint32_t); +extern void nl_cli_cls_parse_proto(struct rtnl_cls *, char *); +extern struct rtnl_ematch_tree *nl_cli_cls_parse_ematch(struct rtnl_cls *, char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/cli/ct.h b/libnl/include/netlink/cli/ct.h new file mode 100644 index 0000000..a50b430 --- /dev/null +++ b/libnl/include/netlink/cli/ct.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2009 Thomas Graf + */ + +#ifndef __NETLINK_CLI_CT_H_ +#define __NETLINK_CLI_CT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct nfnl_ct *nl_cli_ct_alloc(void); +extern struct nl_cache *nl_cli_ct_alloc_cache(struct nl_sock *); + +extern void nl_cli_ct_parse_family(struct nfnl_ct *, char *); +extern void nl_cli_ct_parse_protocol(struct nfnl_ct *, char *); +extern void nl_cli_ct_parse_mark(struct nfnl_ct *, char *); +extern void nl_cli_ct_parse_timeout(struct nfnl_ct *, char *); +extern void nl_cli_ct_parse_id(struct nfnl_ct *, char *); +extern void nl_cli_ct_parse_use(struct nfnl_ct *, char *); +extern void nl_cli_ct_parse_src(struct nfnl_ct *, int, char *); +extern void nl_cli_ct_parse_dst(struct nfnl_ct *, int, char *); +extern void nl_cli_ct_parse_src_port(struct nfnl_ct *, int, char *); +extern void nl_cli_ct_parse_dst_port(struct nfnl_ct *, int, char *); +extern void nl_cli_ct_parse_tcp_state(struct nfnl_ct *, char *); +extern void nl_cli_ct_parse_status(struct nfnl_ct *, char *); +extern void nl_cli_ct_parse_zone(struct nfnl_ct *, char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/cli/exp.h b/libnl/include/netlink/cli/exp.h new file mode 100644 index 0000000..64d3b08 --- /dev/null +++ b/libnl/include/netlink/cli/exp.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2012 Rich Fought + * Copyright (c) 2008-2009 Thomas Graf + */ + +#ifndef __NETLINK_CLI_EXP_H_ +#define __NETLINK_CLI_EXP_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct nfnl_exp *nl_cli_exp_alloc(void); +extern struct nl_cache *nl_cli_exp_alloc_cache(struct nl_sock *); + +extern void nl_cli_exp_parse_family(struct nfnl_exp *, char *); +extern void nl_cli_exp_parse_timeout(struct nfnl_exp *, char *); +extern void nl_cli_exp_parse_id(struct nfnl_exp *, char *); +extern void nl_cli_exp_parse_helper_name(struct nfnl_exp *, char *); +extern void nl_cli_exp_parse_zone(struct nfnl_exp *, char *); +extern void nl_cli_exp_parse_flags(struct nfnl_exp *, char *); +extern void nl_cli_exp_parse_class(struct nfnl_exp *, char *); +extern void nl_cli_exp_parse_nat_dir(struct nfnl_exp *, char *); +extern void nl_cli_exp_parse_fn(struct nfnl_exp *, char *); + +extern void nl_cli_exp_parse_src(struct nfnl_exp *, int, char *); +extern void nl_cli_exp_parse_dst(struct nfnl_exp *, int, char *); +extern void nl_cli_exp_parse_l4protonum(struct nfnl_exp *, int, char *); +extern void nl_cli_exp_parse_src_port(struct nfnl_exp *, int, char *); +extern void nl_cli_exp_parse_dst_port(struct nfnl_exp *, int, char *); +extern void nl_cli_exp_parse_icmp_id(struct nfnl_exp *, int, char *); +extern void nl_cli_exp_parse_icmp_type(struct nfnl_exp *, int, char *); +extern void nl_cli_exp_parse_icmp_code(struct nfnl_exp *, int, char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/cli/link.h b/libnl/include/netlink/cli/link.h new file mode 100644 index 0000000..fb6c0a4 --- /dev/null +++ b/libnl/include/netlink/cli/link.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2010 Thomas Graf + */ + +#ifndef __NETLINK_CLI_LINK_H_ +#define __NETLINK_CLI_LINK_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_link *nl_cli_link_alloc(void); +extern struct nl_cache *nl_cli_link_alloc_cache_family(struct nl_sock *, int); +extern struct nl_cache *nl_cli_link_alloc_cache_family_flags(struct nl_sock *, int, + unsigned int); +extern struct nl_cache *nl_cli_link_alloc_cache(struct nl_sock *); +extern struct nl_cache *nl_cli_link_alloc_cache_flags(struct nl_sock *, + unsigned int); + +extern void nl_cli_link_parse_family(struct rtnl_link *, char *); +extern void nl_cli_link_parse_name(struct rtnl_link *, char *); +extern void nl_cli_link_parse_mtu(struct rtnl_link *, char *); +extern void nl_cli_link_parse_ifindex(struct rtnl_link *, char *); +extern void nl_cli_link_parse_txqlen(struct rtnl_link *, char *); +extern void nl_cli_link_parse_weight(struct rtnl_link *, char *); +extern void nl_cli_link_parse_ifalias(struct rtnl_link *, char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/cli/mdb.h b/libnl/include/netlink/cli/mdb.h new file mode 100644 index 0000000..cd37604 --- /dev/null +++ b/libnl/include/netlink/cli/mdb.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#ifndef __NETLINK_CLI_MDB_H_ +#define __NETLINK_CLI_MDB_H_ + +#include + +#define nl_cli_mdb_alloc_cache(sk) \ + nl_cli_alloc_cache_flags((sk), "mdb", NL_CACHE_AF_ITER, rtnl_mdb_alloc_cache) + +#endif diff --git a/libnl/include/netlink/cli/neigh.h b/libnl/include/netlink/cli/neigh.h new file mode 100644 index 0000000..89a336c --- /dev/null +++ b/libnl/include/netlink/cli/neigh.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2009 Thomas Graf + */ + +#ifndef __NETLINK_CLI_NEIGH_H_ +#define __NETLINK_CLI_NEIGH_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define nl_cli_neigh_alloc_cache(sk) \ + nl_cli_alloc_cache_flags((sk), "neighbour", NL_CACHE_AF_ITER, \ + rtnl_neigh_alloc_cache_flags) + +extern struct rtnl_neigh *nl_cli_neigh_alloc(void); +extern void nl_cli_neigh_parse_dst(struct rtnl_neigh *, char *); +extern void nl_cli_neigh_parse_lladdr(struct rtnl_neigh *, char *); +extern void nl_cli_neigh_parse_dev(struct rtnl_neigh *, struct nl_cache *, char *); +extern void nl_cli_neigh_parse_family(struct rtnl_neigh *, char *); +extern void nl_cli_neigh_parse_state(struct rtnl_neigh *, char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/cli/nh.h b/libnl/include/netlink/cli/nh.h new file mode 100644 index 0000000..ee08c8e --- /dev/null +++ b/libnl/include/netlink/cli/nh.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2022 Stanislav Zaikin + */ + +#ifndef __NETLINK_CLI_NH_H_ +#define __NETLINK_CLI_NH_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_nh *nl_cli_nh_alloc(void); +extern struct nl_cache *nl_cli_nh_alloc_cache_family(struct nl_sock *, int); +extern struct nl_cache *nl_cli_nh_alloc_cache_family_flags(struct nl_sock *, + int, unsigned int); +extern struct nl_cache *nl_cli_nh_alloc_cache(struct nl_sock *); +extern struct nl_cache *nl_cli_nh_alloc_cache_flags(struct nl_sock *, + unsigned int); + +extern void nl_cli_nh_parse_family(struct rtnl_nh *, char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/cli/qdisc.h b/libnl/include/netlink/cli/qdisc.h new file mode 100644 index 0000000..d70e559 --- /dev/null +++ b/libnl/include/netlink/cli/qdisc.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2011 Thomas Graf + */ + +#ifndef __NETLINK_CLI_QDISC_H_ +#define __NETLINK_CLI_QDISC_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define nl_cli_qdisc_alloc_cache(sk) \ + nl_cli_alloc_cache((sk), "queueing disciplines", \ + rtnl_qdisc_alloc_cache) + +extern struct rtnl_qdisc *nl_cli_qdisc_alloc(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/cli/route.h b/libnl/include/netlink/cli/route.h new file mode 100644 index 0000000..3650d8b --- /dev/null +++ b/libnl/include/netlink/cli/route.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2009 Thomas Graf + */ + +#ifndef __NETLINK_CLI_ROUTE_H_ +#define __NETLINK_CLI_ROUTE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_route *nl_cli_route_alloc(void); + +extern struct nl_cache *nl_cli_route_alloc_cache(struct nl_sock *, int); + +extern void nl_cli_route_parse_family(struct rtnl_route *, char *); +extern void nl_cli_route_parse_dst(struct rtnl_route *, char *); +extern void nl_cli_route_parse_src(struct rtnl_route *, char *); +extern void nl_cli_route_parse_pref_src(struct rtnl_route *, char *); +extern void nl_cli_route_parse_metric(struct rtnl_route *, char *); +extern void nl_cli_route_parse_nexthop(struct rtnl_route *, char *, struct nl_cache *); +extern void nl_cli_route_parse_table(struct rtnl_route *, char *); +extern void nl_cli_route_parse_prio(struct rtnl_route *, char *); +extern void nl_cli_route_parse_scope(struct rtnl_route *, char *); +extern void nl_cli_route_parse_protocol(struct rtnl_route *, char *); +extern void nl_cli_route_parse_type(struct rtnl_route *, char *); +extern void nl_cli_route_parse_iif(struct rtnl_route *, char *, struct nl_cache *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/cli/rule.h b/libnl/include/netlink/cli/rule.h new file mode 100644 index 0000000..82bf067 --- /dev/null +++ b/libnl/include/netlink/cli/rule.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2009 Thomas Graf + */ + +#ifndef __NETLINK_CLI_RULE_H_ +#define __NETLINK_CLI_RULE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_rule *nl_cli_rule_alloc(void); +extern struct nl_cache *nl_cli_rule_alloc_cache(struct nl_sock *); +extern void nl_cli_rule_parse_family(struct rtnl_rule *, char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/cli/tc.h b/libnl/include/netlink/cli/tc.h new file mode 100644 index 0000000..7d4ed7a --- /dev/null +++ b/libnl/include/netlink/cli/tc.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2011 Thomas Graf + */ + +#ifndef __NETLINK_CLI_TC_H_ +#define __NETLINK_CLI_TC_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_tc_ops; + +extern void nl_cli_tc_parse_dev(struct rtnl_tc *, struct nl_cache *, char *); +extern void nl_cli_tc_parse_parent(struct rtnl_tc *, char *); +extern void nl_cli_tc_parse_handle(struct rtnl_tc *, char *, int); +extern void nl_cli_tc_parse_mtu(struct rtnl_tc *, char *); +extern void nl_cli_tc_parse_mpu(struct rtnl_tc *, char *); +extern void nl_cli_tc_parse_overhead(struct rtnl_tc *, char *); +extern void nl_cli_tc_parse_linktype(struct rtnl_tc *, char *); +extern void nl_cli_tc_parse_kind(struct rtnl_tc *, char *); + +struct nl_cli_tc_module +{ + const char * tm_name; + enum rtnl_tc_type tm_type; + struct rtnl_tc_ops * tm_ops; + void (*tm_parse_argv)(struct rtnl_tc *, int, char **); + struct nl_list_head tm_list; +}; + +extern struct nl_cli_tc_module *nl_cli_tc_lookup(struct rtnl_tc_ops *); +extern void nl_cli_tc_register(struct nl_cli_tc_module *); +extern void nl_cli_tc_unregister(struct nl_cli_tc_module *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/cli/utils.h b/libnl/include/netlink/cli/utils.h new file mode 100644 index 0000000..7047d2e --- /dev/null +++ b/libnl/include/netlink/cli/utils.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#ifndef __NETLINK_CLI_UTILS_H_ +#define __NETLINK_CLI_UTILS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __init +#define __init __attribute__((constructor)) +#endif + +#ifndef __exit +#define __exit __attribute__((destructor)) +#endif + +extern uint32_t nl_cli_parse_u32(const char *); +extern void nl_cli_print_version(void) + __attribute__((noreturn)); +extern void nl_cli_fatal(int, const char *, ...) + __attribute__((noreturn)); +extern struct nl_addr * nl_cli_addr_parse(const char *, int); +extern int nl_cli_connect(struct nl_sock *, int); +extern struct nl_sock * nl_cli_alloc_socket(void); +extern int nl_cli_parse_dumptype(const char *); +extern int nl_cli_confirm(struct nl_object *, + struct nl_dump_params *, int); + +extern struct nl_cache *nl_cli_alloc_cache(struct nl_sock *, const char *, + int (*ac)(struct nl_sock *, struct nl_cache **)); + +extern struct nl_cache *nl_cli_alloc_cache_flags(struct nl_sock *, const char *, + unsigned int flags, + int (*ac)(struct nl_sock *, struct nl_cache **, unsigned int)); + +extern void nl_cli_load_module(const char *, const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/data.h b/libnl/include/netlink/data.h new file mode 100644 index 0000000..c9c76a1 --- /dev/null +++ b/libnl/include/netlink/data.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +#ifndef NETLINK_DATA_H_ +#define NETLINK_DATA_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nlattr; + +struct nl_data; + +/* General */ +extern struct nl_data * nl_data_alloc(const void *, size_t); +extern struct nl_data * nl_data_alloc_attr(const struct nlattr *); +extern struct nl_data * nl_data_clone(const struct nl_data *); +extern int nl_data_append(struct nl_data *, const void *, size_t); +extern void nl_data_free(struct nl_data *); + +/* Access Functions */ +extern void * nl_data_get(const struct nl_data *); +extern size_t nl_data_get_size(const struct nl_data *); + +/* Misc */ +extern int nl_data_cmp(const struct nl_data *, + const struct nl_data *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/errno.h b/libnl/include/netlink/errno.h new file mode 100644 index 0000000..6a5b4de --- /dev/null +++ b/libnl/include/netlink/errno.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008 Thomas Graf + */ + +#ifndef NETLINK_ERRNO_H_ +#define NETLINK_ERRNO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define NLE_SUCCESS 0 +#define NLE_FAILURE 1 +#define NLE_INTR 2 +#define NLE_BAD_SOCK 3 +#define NLE_AGAIN 4 +#define NLE_NOMEM 5 +#define NLE_EXIST 6 +#define NLE_INVAL 7 +#define NLE_RANGE 8 +#define NLE_MSGSIZE 9 +#define NLE_OPNOTSUPP 10 +#define NLE_AF_NOSUPPORT 11 +#define NLE_OBJ_NOTFOUND 12 +#define NLE_NOATTR 13 +#define NLE_MISSING_ATTR 14 +#define NLE_AF_MISMATCH 15 +#define NLE_SEQ_MISMATCH 16 +#define NLE_MSG_OVERFLOW 17 +#define NLE_MSG_TRUNC 18 +#define NLE_NOADDR 19 +#define NLE_SRCRT_NOSUPPORT 20 +#define NLE_MSG_TOOSHORT 21 +#define NLE_MSGTYPE_NOSUPPORT 22 +#define NLE_OBJ_MISMATCH 23 +#define NLE_NOCACHE 24 +#define NLE_BUSY 25 +#define NLE_PROTO_MISMATCH 26 +#define NLE_NOACCESS 27 +#define NLE_PERM 28 +#define NLE_PKTLOC_FILE 29 +#define NLE_PARSE_ERR 30 +#define NLE_NODEV 31 +#define NLE_IMMUTABLE 32 +#define NLE_DUMP_INTR 33 +#define NLE_ATTRSIZE 34 + +#define NLE_MAX NLE_ATTRSIZE + +extern const char * nl_geterror(int); +extern void nl_perror(int, const char *); +extern int nl_syserr2nlerr(int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/fib_lookup/lookup.h b/libnl/include/netlink/fib_lookup/lookup.h new file mode 100644 index 0000000..81fa435 --- /dev/null +++ b/libnl/include/netlink/fib_lookup/lookup.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +#ifndef NETLINK_FIB_LOOKUP_H_ +#define NETLINK_FIB_LOOKUP_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct flnl_result; + +extern struct flnl_result * flnl_result_alloc(void); +extern void flnl_result_put(struct flnl_result *); + +extern struct nl_cache * flnl_result_alloc_cache(void); + +extern int flnl_lookup_build_request(struct flnl_request *, + int, + struct nl_msg **); +extern int flnl_lookup(struct nl_sock *, + struct flnl_request *, + struct nl_cache *); + +extern int flnl_result_get_table_id(struct flnl_result *res); +extern int flnl_result_get_prefixlen(struct flnl_result *res); +extern int flnl_result_get_nexthop_sel(struct flnl_result *res); +extern int flnl_result_get_type(struct flnl_result *res); +extern int flnl_result_get_scope(struct flnl_result *res); +extern int flnl_result_get_error(struct flnl_result *res); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/fib_lookup/request.h b/libnl/include/netlink/fib_lookup/request.h new file mode 100644 index 0000000..ed7cdd8 --- /dev/null +++ b/libnl/include/netlink/fib_lookup/request.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_FIB_LOOKUP_REQUEST_H_ +#define NETLINK_FIB_LOOKUP_REQUEST_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct flnl_request; + +#define REQUEST_CAST(ptr) ((struct flnl_request *) (ptr)) + +extern struct flnl_request * flnl_request_alloc(void); + +extern void flnl_request_set_fwmark(struct flnl_request *, + uint64_t); +extern uint64_t flnl_request_get_fwmark(struct flnl_request *); +extern void flnl_request_set_tos(struct flnl_request *, + int); +extern int flnl_request_get_tos(struct flnl_request *); +extern void flnl_request_set_scope(struct flnl_request *, + int); +extern int flnl_request_get_scope(struct flnl_request *); +extern void flnl_request_set_table(struct flnl_request *, + int); +extern int flnl_request_get_table(struct flnl_request *); +extern int flnl_request_set_addr(struct flnl_request *, + struct nl_addr *); +extern struct nl_addr * flnl_request_get_addr(struct flnl_request *); + +extern int flnl_request_cmp(struct flnl_request *, + struct flnl_request *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/genl/ctrl.h b/libnl/include/netlink/genl/ctrl.h new file mode 100644 index 0000000..92d60b3 --- /dev/null +++ b/libnl/include/netlink/genl/ctrl.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef NETLINK_GENL_CTRL_H_ +#define NETLINK_GENL_CTRL_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct genl_family; + +extern int genl_ctrl_alloc_cache(struct nl_sock *, + struct nl_cache **); +extern struct genl_family * genl_ctrl_search(struct nl_cache *, int); +extern struct genl_family * genl_ctrl_search_by_name(struct nl_cache *, + const char *); +extern int genl_ctrl_resolve(struct nl_sock *, + const char *); +extern int genl_ctrl_resolve_grp(struct nl_sock *sk, + const char *family, + const char *grp); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/genl/family.h b/libnl/include/netlink/genl/family.h new file mode 100644 index 0000000..2e9f9fd --- /dev/null +++ b/libnl/include/netlink/genl/family.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef NETLINK_GENL_FAMILY_H_ +#define NETLINK_GENL_FAMILY_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct genl_family; + +extern struct genl_family * genl_family_alloc(void); +extern void genl_family_put(struct genl_family *); + +extern unsigned int genl_family_get_id(struct genl_family *); +extern void genl_family_set_id(struct genl_family *, unsigned int); +extern char * genl_family_get_name(struct genl_family *); +extern void genl_family_set_name(struct genl_family *, const char *); +extern uint8_t genl_family_get_version(struct genl_family *); +extern void genl_family_set_version(struct genl_family *, uint8_t); +extern uint32_t genl_family_get_hdrsize(struct genl_family *); +extern void genl_family_set_hdrsize(struct genl_family *, uint32_t); +extern uint32_t genl_family_get_maxattr(struct genl_family *); +extern void genl_family_set_maxattr(struct genl_family *, uint32_t); + +extern int genl_family_add_op(struct genl_family *, int, int); +extern int genl_family_add_grp(struct genl_family *, uint32_t , + const char *); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/genl/genl.h b/libnl/include/netlink/genl/genl.h new file mode 100644 index 0000000..f1e3a65 --- /dev/null +++ b/libnl/include/netlink/genl/genl.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef NETLINK_GENL_H_ +#define NETLINK_GENL_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int genl_connect(struct nl_sock *); +extern int genl_send_simple(struct nl_sock *, int, int, + int, int); + +extern void * genlmsg_put(struct nl_msg *, uint32_t, uint32_t, + int, int, int, uint8_t, uint8_t); + +extern int genlmsg_valid_hdr(struct nlmsghdr *, int); +extern int genlmsg_validate(struct nlmsghdr *, int, int, + const struct nla_policy *); +extern int genlmsg_parse(struct nlmsghdr *, int, struct nlattr **, + int, const struct nla_policy *); +extern struct genlmsghdr * + genlmsg_hdr(struct nlmsghdr *); +extern void * genlmsg_data(const struct genlmsghdr *); +extern void * genlmsg_user_hdr(const struct genlmsghdr *); +extern void * genlmsg_user_data(const struct genlmsghdr *, const int); +extern int genlmsg_user_datalen(const struct genlmsghdr *, + const int); +extern int genlmsg_len(const struct genlmsghdr *); +extern struct nlattr * genlmsg_attrdata(const struct genlmsghdr *, int); +extern int genlmsg_attrlen(const struct genlmsghdr *, int); + +extern char * genl_op2name(int, int, char *, size_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/genl/mngt.h b/libnl/include/netlink/genl/mngt.h new file mode 100644 index 0000000..af9edb3 --- /dev/null +++ b/libnl/include/netlink/genl/mngt.h @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef NETLINK_GENL_MNGT_H_ +#define NETLINK_GENL_MNGT_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_cache_ops; + +/** + * @ingroup genl_mngt + * @struct genl_info netlink/genl/mngt.h + * + * Informative structure passed on to message parser callbacks + * + * This structure is passed on to all message parser callbacks and contains + * information about the sender of the message as well as pointers to all + * relevant sections of the parsed message. + * + * @see genl_cmd::c_msg_parser + */ +struct genl_info +{ + /** Socket address of sender */ + struct sockaddr_nl * who; + + /** Pointer to Netlink message header */ + struct nlmsghdr * nlh; + + /** Pointer to Generic Netlink message header */ + struct genlmsghdr * genlhdr; + + /** Pointer to user header */ + void * userhdr; + + /** Pointer to array of parsed attributes */ + struct nlattr ** attrs; +}; + +/** + * @ingroup genl_mngt + * @struct genl_cmd netlink/genl/mngt.h + * + * Definition of a Generic Netlink command. + * + * This structure is used to define the list of available commands on the + * receiving side. + * + * @par Example: + * @code + * static struct genl_cmd foo_cmds[] = { + * { + * .c_id = FOO_CMD_NEW, + * .c_name = "NEWFOO" , + * .c_maxattr = FOO_ATTR_MAX, + * .c_attr_policy = foo_policy, + * .c_msg_parser = foo_msg_parser, + * }, + * { + * .c_id = FOO_CMD_DEL, + * .c_name = "DELFOO" , + * }, + * }; + * + * static struct genl_ops my_genl_ops = { + * [...] + * .o_cmds = foo_cmds, + * .o_ncmds = ARRAY_SIZE(foo_cmds), + * }; + * @endcode + */ +struct genl_cmd +{ + /** Numeric command identifier (required) */ + int c_id; + + /** Human readable name (required) */ + char * c_name; + + /** Maximum attribute identifier that the command is prepared to handle. */ + int c_maxattr; + + /** Called whenever a message for this command is received */ + int (*c_msg_parser)(struct nl_cache_ops *, + struct genl_cmd *, + struct genl_info *, void *); + + /** Attribute validation policy, enforced before the callback is called */ + struct nla_policy * c_attr_policy; +}; + +/** + * @ingroup genl_mngt + * @struct genl_ops netlink/genl/mngt.h + * + * Definition of a Generic Netlink family + * + * @par Example: + * @code + * static struct genl_cmd foo_cmds[] = { + * [...] + * }; + * + * static struct genl_ops my_genl_ops = { + * .o_name = "foo", + * .o_hdrsize = sizeof(struct my_hdr), + * .o_cmds = foo_cmds, + * .o_ncmds = ARRAY_SIZE(foo_cmds), + * }; + * + * if ((err = genl_register_family(&my_genl_ops)) < 0) + * // ERROR + * @endcode + * + * @see genl_cmd + */ +struct genl_ops +{ + /** Length of user header */ + unsigned int o_hdrsize; + + /** Numeric identifier, automatically filled in by genl_ops_resolve() */ + int o_id; + + /** Human readable name, used by genl_ops_resolve() to resolve numeric id */ + char * o_name; + + /** + * If registered via genl_register(), will point to the related + * cache operations. + */ + struct nl_cache_ops * o_cache_ops; + + /** Optional array defining the available Generic Netlink commands */ + struct genl_cmd * o_cmds; + + /** Number of elements in \c o_cmds array */ + int o_ncmds; + + /** + * @private + * Used internally to link together all registered operations. + */ + struct nl_list_head o_list; +}; + +extern int genl_register_family(struct genl_ops *); +extern int genl_unregister_family(struct genl_ops *); +extern int genl_handle_msg(struct nl_msg *, void *); + +extern int genl_register(struct nl_cache_ops *); +extern void genl_unregister(struct nl_cache_ops *); + +extern int genl_ops_resolve(struct nl_sock *, struct genl_ops *); +extern int genl_mngt_resolve(struct nl_sock *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/handlers.h b/libnl/include/netlink/handlers.h new file mode 100644 index 0000000..2043844 --- /dev/null +++ b/libnl/include/netlink/handlers.h @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_HANDLERS_H_ +#define NETLINK_HANDLERS_H_ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nlmsgerr; +struct sockaddr_nl; +struct ucred; + +struct nl_cb; +struct nl_sock; +struct nl_msg; + +/** + * @name Callback Typedefs + * @{ + */ + +/** + * nl_recvmsgs() callback for message processing customization + * @ingroup cb + * @arg msg netlink message being processed + * @arg arg argument passed on through caller + */ +typedef int (*nl_recvmsg_msg_cb_t)(struct nl_msg *msg, void *arg); + +/** + * nl_recvmsgs() callback for error message processing customization + * @ingroup cb + * @arg nla netlink address of the peer + * @arg nlerr netlink error message being processed + * @arg arg argument passed on through caller + */ +typedef int (*nl_recvmsg_err_cb_t)(struct sockaddr_nl *nla, + struct nlmsgerr *nlerr, void *arg); + +/** @} */ + +/** + * Callback actions + * @ingroup cb + */ +enum nl_cb_action { + /** Proceed with whatever would come next */ + NL_OK, + /** Skip this message */ + NL_SKIP, + /** Stop parsing altogether and discard remaining messages */ + NL_STOP, +}; + +/** + * Callback kinds + * @ingroup cb + */ +enum nl_cb_kind { + /** Default handlers (quiet) */ + NL_CB_DEFAULT, + /** Verbose default handlers (error messages printed) */ + NL_CB_VERBOSE, + /** Debug handlers for debugging */ + NL_CB_DEBUG, + /** Customized handler specified by the user */ + NL_CB_CUSTOM, + __NL_CB_KIND_MAX, +}; + +#define NL_CB_KIND_MAX (__NL_CB_KIND_MAX - 1) + +/** + * Callback types + * @ingroup cb + */ +enum nl_cb_type { + /** Message is valid */ + NL_CB_VALID, + /** Last message in a series of multi part messages received */ + NL_CB_FINISH, + /** Report received that data was lost */ + NL_CB_OVERRUN, + /** Message wants to be skipped */ + NL_CB_SKIPPED, + /** Message is an acknowledgement */ + NL_CB_ACK, + /** Called for every message received */ + NL_CB_MSG_IN, + /** Called for every message sent out except for nl_sendto() */ + NL_CB_MSG_OUT, + /** Message is malformed and invalid */ + NL_CB_INVALID, + /** Called instead of internal sequence number checking */ + NL_CB_SEQ_CHECK, + /** Sending of an acknowledge message has been requested */ + NL_CB_SEND_ACK, + /** Flag NLM_F_DUMP_INTR is set in message */ + NL_CB_DUMP_INTR, + __NL_CB_TYPE_MAX, +}; + +#define NL_CB_TYPE_MAX (__NL_CB_TYPE_MAX - 1) + +extern struct nl_cb * nl_cb_alloc(enum nl_cb_kind); +extern struct nl_cb * nl_cb_clone(struct nl_cb *); +extern struct nl_cb * nl_cb_get(struct nl_cb *); +extern void nl_cb_put(struct nl_cb *); + +extern int nl_cb_set(struct nl_cb *, enum nl_cb_type, enum nl_cb_kind, + nl_recvmsg_msg_cb_t, void *); +extern int nl_cb_set_all(struct nl_cb *, enum nl_cb_kind, + nl_recvmsg_msg_cb_t, void *); +extern int nl_cb_err(struct nl_cb *, enum nl_cb_kind, nl_recvmsg_err_cb_t, + void *); + +extern void nl_cb_overwrite_recvmsgs(struct nl_cb *, + int (*func)(struct nl_sock *, + struct nl_cb *)); +extern void nl_cb_overwrite_recv(struct nl_cb *, + int (*func)(struct nl_sock *, + struct sockaddr_nl *, + unsigned char **, + struct ucred **)); +extern void nl_cb_overwrite_send(struct nl_cb *, + int (*func)(struct nl_sock *, + struct nl_msg *)); + +extern enum nl_cb_type nl_cb_active_type(struct nl_cb *cb); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/hash.h b/libnl/include/netlink/hash.h new file mode 100644 index 0000000..5df4099 --- /dev/null +++ b/libnl/include/netlink/hash.h @@ -0,0 +1,78 @@ +/* + * This file was taken from http://ccodearchive.net/info/hash.html + * Changes to the original file include cleanups and removal of unwanted code + * and also code that depended on build_asert + */ +#ifndef CCAN_HASH_H +#define CCAN_HASH_H + +#include +#include +#include + +/* Stolen mostly from: lookup3.c, by Bob Jenkins, May 2006, Public Domain. + * + * http://burtleburtle.net/bob/c/lookup3.c + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __LITTLE_ENDIAN +# define HAVE_LITTLE_ENDIAN 1 +#elif __BIG_ENDIAN +# define HAVE_BIG_ENDIAN 1 +#else +#error Unknown endianness. Failure in endian.h +#endif + +/** + * hash - fast hash of an array for internal use + * @p: the array or pointer to first element + * @num: the number of elements to hash + * @base: the base number to roll into the hash (usually 0) + * + * The memory region pointed to by p is combined with the base to form + * a 32-bit hash. + * + * This hash will have different results on different machines, so is + * only useful for internal hashes (ie. not hashes sent across the + * network or saved to disk). + * + * It may also change with future versions: it could even detect at runtime + * what the fastest hash to use is. + * + * See also: hash64, hash_stable. + * + * Example: + * #include + * #include + * #include + * #include + * + * // Simple demonstration: idential strings will have the same hash, but + * // two different strings will probably not. + * int main(int argc, char *argv[]) + * { + * uint32_t hash1, hash2; + * + * if (argc != 3) + * err(1, "Usage: %s ", argv[0]); + * + * hash1 = __nl_hash(argv[1], strlen(argv[1]), 0); + * hash2 = __nl_hash(argv[2], strlen(argv[2]), 0); + * printf("Hash is %s\n", hash1 == hash2 ? "same" : "different"); + * return 0; + * } + */ +#define __nl_hash(p, num, base) nl_hash_any((p), (num)*sizeof(*(p)), (base)) + +/* Our underlying operations. */ +uint32_t nl_hash_any(const void *key, size_t length, uint32_t base); + +#ifdef __cplusplus +} +#endif + +#endif /* HASH_H */ diff --git a/libnl/include/netlink/hashtable.h b/libnl/include/netlink/hashtable.h new file mode 100644 index 0000000..32578dc --- /dev/null +++ b/libnl/include/netlink/hashtable.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2012 Cumulus Networks, Inc + */ + +#ifndef NETLINK_HASHTABLE_H_ +#define NETLINK_HASHTABLE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct nl_hash_node { + uint32_t key; + uint32_t key_size; + struct nl_object * obj; + struct nl_hash_node * next; +} nl_hash_node_t; + +typedef struct nl_hash_table { + int size; + nl_hash_node_t ** nodes; +} nl_hash_table_t; + +/* Default hash table size */ +#define NL_MAX_HASH_ENTRIES 1024 + +/* Access Functions */ +extern nl_hash_table_t * nl_hash_table_alloc(int size); +extern void nl_hash_table_free(nl_hash_table_t *ht); + +extern int nl_hash_table_add(nl_hash_table_t *ht, + struct nl_object *obj); +extern int nl_hash_table_del(nl_hash_table_t *ht, + struct nl_object *obj); + +extern struct nl_object * nl_hash_table_lookup(nl_hash_table_t *ht, + struct nl_object *obj); +extern uint32_t nl_hash(void *k, size_t length, + uint32_t initval); + +#ifdef __cplusplus +} +#endif + +#endif /* NETLINK_HASHTABLE_H_ */ diff --git a/libnl/include/netlink/idiag/idiagnl.h b/libnl/include/netlink/idiag/idiagnl.h new file mode 100644 index 0000000..7385d5f --- /dev/null +++ b/libnl/include/netlink/idiag/idiagnl.h @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Sassano Systems LLC + */ + +#ifndef NETLINK_IDIAGNL_H_ +#define NETLINK_IDIAGNL_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/************************************************************* + * The following part contains DEPRECATED names and defines. + * Don't use them. + *************************************************************/ + +/** + * Inet Diag message types + * + * deprecated: use TCPDIAG_GETSOCK, DCCPDIAG_GETSOCK and + * INET_DIAG_GETSOCK_MAX from linux/inet_diag.h + */ +#define IDIAG_TCPDIAG_GETSOCK 18 +#define IDIAG_DCCPDIAG_GETSOCK 19 +#define IDIAG_GETSOCK_MAX 24 + +/** + * Socket state identifiers + * @ingroup idiag + * @deprecated: use instead the TCP_* defines from netinet/tcp.h. + */ +enum { + IDIAG_SS_UNKNOWN = 0, + + IDIAG_SS_ESTABLISHED = 1, /* TCP_ESTABLISHED */ + IDIAG_SS_SYN_SENT = 2, /* TCP_SYN_SENT */ + IDIAG_SS_SYN_RECV = 3, /* TCP_SYN_RECV */ + IDIAG_SS_FIN_WAIT1 = 4, /* TCP_FIN_WAIT1 */ + IDIAG_SS_FIN_WAIT2 = 5, /* TCP_FIN_WAIT2 */ + IDIAG_SS_TIME_WAIT = 6, /* TCP_TIME_WAIT */ + IDIAG_SS_CLOSE = 7, /* TCP_CLOSE */ + IDIAG_SS_CLOSE_WAIT = 8, /* TCP_CLOSE_WAIT */ + IDIAG_SS_LAST_ACK = 9, /* TCP_LAST_ACK */ + IDIAG_SS_LISTEN = 10, /* TCP_LISTEN */ + IDIAG_SS_CLOSING = 11, /* TCP_CLOSING */ + + IDIAG_SS_MAX = 12, +}; + +/** + * Macro to represent all socket states. + * @ingroup idiag + * @deprecated + */ +#define IDIAG_SS_ALL IDIAGNL_SS_ALL + + +/** + * Inet Diag extended attributes + * @ingroup idiag + * @deprecated These attributes should not be used. They mirror the + * INET_DIAG_* extension flags from kernel headers. Use those instead. */ +enum { + IDIAG_ATTR_NONE = 0, /* INET_DIAG_NONE */ + IDIAG_ATTR_MEMINFO = 1, /* INET_DIAG_MEMINFO */ + IDIAG_ATTR_INFO = 2, /* INET_DIAG_INFO */ + IDIAG_ATTR_VEGASINFO = 3, /* INET_DIAG_VEGASINFO */ + IDIAG_ATTR_CONG = 4, /* INET_DIAG_CONG */ + IDIAG_ATTR_TOS = 5, /* INET_DIAG_TOS */ + IDIAG_ATTR_TCLASS = 6, /* INET_DIAG_TCLASS */ + IDIAG_ATTR_SKMEMINFO = 7, /* INET_DIAG_SKMEMINFO */ + IDIAG_ATTR_SHUTDOWN = 8, /* INET_DIAG_SHUTDOWN */ + + /* IDIAG_ATTR_MAX was wrong, because it did not correspond to + * INET_DIAG_MAX. Anyway, freeze it to the previous value. */ + IDIAG_ATTR_MAX = 9, + + IDIAG_ATTR_ALL = (1< + */ + +#ifndef NETLINK_IDIAGNL_MEMINFO_H_ +#define NETLINK_IDIAGNL_MEMINFO_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern struct nl_object_ops idiagnl_meminfo_obj_ops; + +extern struct idiagnl_meminfo *idiagnl_meminfo_alloc(void); +extern void idiagnl_meminfo_get(struct idiagnl_meminfo *); +extern void idiagnl_meminfo_put(struct idiagnl_meminfo *); + +extern uint32_t idiagnl_meminfo_get_rmem(const struct idiagnl_meminfo *); +extern uint32_t idiagnl_meminfo_get_wmem(const struct idiagnl_meminfo *); +extern uint32_t idiagnl_meminfo_get_fmem(const struct idiagnl_meminfo *); +extern uint32_t idiagnl_meminfo_get_tmem(const struct idiagnl_meminfo *); + +extern void idiagnl_meminfo_set_rmem(struct idiagnl_meminfo *, uint32_t); +extern void idiagnl_meminfo_set_wmem(struct idiagnl_meminfo *, uint32_t); +extern void idiagnl_meminfo_set_fmem(struct idiagnl_meminfo *, uint32_t); +extern void idiagnl_meminfo_set_tmem(struct idiagnl_meminfo *, uint32_t); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* NETLINK_IDIAGNL_MEMINFO_H_ */ diff --git a/libnl/include/netlink/idiag/msg.h b/libnl/include/netlink/idiag/msg.h new file mode 100644 index 0000000..f2a79ce --- /dev/null +++ b/libnl/include/netlink/idiag/msg.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Sassano Systems LLC + */ + +#ifndef NETLINK_IDIAGNL_MSG_H_ +#define NETLINK_IDIAGNL_MSG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +struct idiagnl_msg; + +/* @deprecated: DO NOT USE this variable. */ +extern struct nl_object_ops idiagnl_msg_obj_ops; + +extern struct idiagnl_msg * idiagnl_msg_alloc(void); +extern int idiagnl_msg_alloc_cache(struct nl_sock *, int, int, + struct nl_cache**); +extern void idiagnl_msg_get(struct idiagnl_msg *); +extern void idiagnl_msg_put(struct idiagnl_msg *); +extern uint8_t idiagnl_msg_get_family(const struct idiagnl_msg *); +extern void idiagnl_msg_set_family(struct idiagnl_msg *, uint8_t); +extern uint8_t idiagnl_msg_get_state(const struct idiagnl_msg *); +extern void idiagnl_msg_set_state(struct idiagnl_msg *, uint8_t); +extern uint8_t idiagnl_msg_get_timer(const struct idiagnl_msg *); +extern void idiagnl_msg_set_timer(struct idiagnl_msg *, uint8_t); +extern uint8_t idiagnl_msg_get_retrans(const struct idiagnl_msg *); +extern void idiagnl_msg_set_retrans(struct idiagnl_msg *, uint8_t); +extern uint16_t idiagnl_msg_get_sport(struct idiagnl_msg *); +extern void idiagnl_msg_set_sport(struct idiagnl_msg *, uint16_t); +extern uint16_t idiagnl_msg_get_dport(struct idiagnl_msg *); +extern void idiagnl_msg_set_dport(struct idiagnl_msg *, uint16_t); +extern struct nl_addr * idiagnl_msg_get_src(const struct idiagnl_msg *); +extern int idiagnl_msg_set_src(struct idiagnl_msg *, + struct nl_addr *); +extern struct nl_addr * idiagnl_msg_get_dst(const struct idiagnl_msg *); +extern int idiagnl_msg_set_dst(struct idiagnl_msg *, + struct nl_addr *); +extern uint32_t idiagnl_msg_get_ifindex(const struct idiagnl_msg *); +extern void idiagnl_msg_set_ifindex(struct idiagnl_msg *, uint32_t); +extern uint32_t idiagnl_msg_get_expires(const struct idiagnl_msg *); +extern void idiagnl_msg_set_expires(struct idiagnl_msg *, uint32_t); +extern uint32_t idiagnl_msg_get_rqueue(const struct idiagnl_msg *); +extern void idiagnl_msg_set_rqueue(struct idiagnl_msg *, uint32_t); +extern uint32_t idiagnl_msg_get_wqueue(const struct idiagnl_msg *); +extern void idiagnl_msg_set_wqueue(struct idiagnl_msg *, uint32_t); +extern uint32_t idiagnl_msg_get_uid(const struct idiagnl_msg *); +extern void idiagnl_msg_set_uid(struct idiagnl_msg *, uint32_t); +extern uint32_t idiagnl_msg_get_inode(const struct idiagnl_msg *); +extern void idiagnl_msg_set_inode(struct idiagnl_msg *, uint32_t); +extern uint8_t idiagnl_msg_get_tos(const struct idiagnl_msg *); +extern void idiagnl_msg_set_tos(struct idiagnl_msg *, uint8_t); +extern uint8_t idiagnl_msg_get_tclass(const struct idiagnl_msg *); +extern void idiagnl_msg_set_tclass(struct idiagnl_msg *, uint8_t); +extern uint8_t idiagnl_msg_get_shutdown(const struct idiagnl_msg *); +extern void idiagnl_msg_set_shutdown(struct idiagnl_msg *, uint8_t); +extern char * idiagnl_msg_get_cong(const struct idiagnl_msg *); +extern void idiagnl_msg_set_cong(struct idiagnl_msg *, char *); +extern struct idiagnl_meminfo *idiagnl_msg_get_meminfo(const struct idiagnl_msg *); +extern void idiagnl_msg_set_meminfo(struct idiagnl_msg *, + struct idiagnl_meminfo *); +extern struct idiagnl_vegasinfo *idiagnl_msg_get_vegasinfo(const struct idiagnl_msg *); +extern void idiagnl_msg_set_vegasinfo(struct idiagnl_msg *, + struct idiagnl_vegasinfo *); +extern struct tcp_info idiagnl_msg_get_tcpinfo(const struct idiagnl_msg *); +extern void idiagnl_msg_set_tcpinfo(struct idiagnl_msg *, + struct tcp_info *); + +extern int idiagnl_msg_parse(struct nlmsghdr *, + struct idiagnl_msg **); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* NETLINK_IDIAGNL_MSG_H_ */ diff --git a/libnl/include/netlink/idiag/req.h b/libnl/include/netlink/idiag/req.h new file mode 100644 index 0000000..fb75b96 --- /dev/null +++ b/libnl/include/netlink/idiag/req.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Sassano Systems LLC + */ + +#ifndef NETLINK_IDIAGNL_REQ_H_ +#define NETLINK_IDIAGNL_REQ_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +struct idiagnl_req; +extern struct nl_object_ops idiagnl_req_obj_ops; + +extern struct idiagnl_req * idiagnl_req_alloc(void); +extern void idiagnl_req_get(struct idiagnl_req *); +extern void idiagnl_req_put(struct idiagnl_req *); +extern uint8_t idiagnl_req_get_family(const struct idiagnl_req *); +extern void idiagnl_req_set_family(struct idiagnl_req *, + uint8_t); +extern uint8_t idiagnl_req_get_ext(const struct idiagnl_req *); +extern void idiagnl_req_set_ext(struct idiagnl_req *, uint8_t); +extern uint32_t idiagnl_req_get_ifindex(const struct idiagnl_req *); +extern void idiagnl_req_set_ifindex(struct idiagnl_req *, + uint32_t); +extern uint32_t idiagnl_req_get_states(const struct idiagnl_req *); +extern void idiagnl_req_set_states(struct idiagnl_req *, + uint32_t); +extern uint32_t idiagnl_req_get_dbs(const struct idiagnl_req *); +extern void idiagnl_req_set_dbs(struct idiagnl_req *, uint32_t); +extern struct nl_addr * idiagnl_req_get_src(const struct idiagnl_req *); +extern int idiagnl_req_set_src(struct idiagnl_req *, + struct nl_addr *); +extern struct nl_addr * idiagnl_req_get_dst(const struct idiagnl_req *); +extern int idiagnl_req_set_dst(struct idiagnl_req *, + struct nl_addr *); + +extern int idiagnl_req_parse(struct nlmsghdr *nlh, + struct idiagnl_req **result); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* NETLINK_IDIAGNL_REQ_H_ */ diff --git a/libnl/include/netlink/idiag/vegasinfo.h b/libnl/include/netlink/idiag/vegasinfo.h new file mode 100644 index 0000000..890c175 --- /dev/null +++ b/libnl/include/netlink/idiag/vegasinfo.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Sassano Systems LLC + */ + +#ifndef NETLINK_IDIAGNL_VEGASINFO_H_ +#define NETLINK_IDIAGNL_VEGASINFO_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern struct nl_object_ops idiagnl_vegasinfo_obj_ops; +extern struct idiagnl_vegasinfo * idiagnl_vegasinfo_alloc(void); +extern void idiagnl_vegasinfo_get(struct idiagnl_vegasinfo *); +extern void idiagnl_vegasinfo_put(struct idiagnl_vegasinfo *); + +extern uint32_t idiagnl_vegasinfo_get_enabled(const struct idiagnl_vegasinfo *); +extern uint32_t idiagnl_vegasinfo_get_rttcnt(const struct idiagnl_vegasinfo *); +extern uint32_t idiagnl_vegasinfo_get_rtt(const struct idiagnl_vegasinfo *); +extern uint32_t idiagnl_vegasinfo_get_minrtt(const struct idiagnl_vegasinfo *); + +extern void idiagnl_vegasinfo_set_enabled(struct idiagnl_vegasinfo *, + uint32_t); +extern void idiagnl_vegasinfo_set_rttcnt(struct idiagnl_vegasinfo *, + uint32_t); +extern void idiagnl_vegasinfo_set_rtt(struct idiagnl_vegasinfo *, uint32_t); +extern void idiagnl_vegasinfo_set_minrtt(struct idiagnl_vegasinfo *, + uint32_t); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* NETLINK_IDIAGNL_VEGASINFO_H_ */ diff --git a/libnl/include/netlink/list.h b/libnl/include/netlink/list.h new file mode 100644 index 0000000..7f300df --- /dev/null +++ b/libnl/include/netlink/list.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_LIST_H_ +#define NETLINK_LIST_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_list_head +{ + struct nl_list_head * next; + struct nl_list_head * prev; +}; + +static inline void NL_INIT_LIST_HEAD(struct nl_list_head *list) +{ + list->next = list; + list->prev = list; +} + +static inline void __nl_list_add(struct nl_list_head *obj, + struct nl_list_head *prev, + struct nl_list_head *next) +{ + prev->next = obj; + obj->prev = prev; + next->prev = obj; + obj->next = next; +} + +static inline void nl_list_add_tail(struct nl_list_head *obj, + struct nl_list_head *head) +{ + __nl_list_add(obj, head->prev, head); +} + +static inline void nl_list_add_head(struct nl_list_head *obj, + struct nl_list_head *head) +{ + __nl_list_add(obj, head, head->next); +} + +static inline void nl_list_del(struct nl_list_head *obj) +{ + obj->next->prev = obj->prev; + obj->prev->next = obj->next; +} + +static inline int nl_list_empty(struct nl_list_head *head) +{ + return head->next == head; +} + +#define nl_container_of(ptr, type, member) ({ \ + const __typeof__( ((type *)0)->member ) *__mptr = (ptr);\ + (type *)( (char *)__mptr - (offsetof(type, member)));}) + +#define nl_list_entry(ptr, type, member) \ + nl_container_of(ptr, type, member) + +#define nl_list_at_tail(pos, head, member) \ + ((pos)->member.next == (head)) + +#define nl_list_at_head(pos, head, member) \ + ((pos)->member.prev == (head)) + +#define NL_LIST_HEAD(name) \ + struct nl_list_head name = { &(name), &(name) } + +#define nl_list_first_entry(head, type, member) \ + nl_list_entry((head)->next, type, member) + +#define nl_list_for_each_entry(pos, head, member) \ + for (pos = nl_list_entry((head)->next, __typeof__(*pos), member); \ + &(pos)->member != (head); \ + (pos) = nl_list_entry((pos)->member.next, __typeof__(*(pos)), member)) + +#define nl_list_for_each_entry_safe(pos, n, head, member) \ + for (pos = nl_list_entry((head)->next, __typeof__(*pos), member), \ + n = nl_list_entry(pos->member.next, __typeof__(*pos), member); \ + &(pos)->member != (head); \ + pos = n, n = nl_list_entry(n->member.next, __typeof__(*n), member)) + +#define nl_init_list_head(head) \ + do { (head)->next = (head); (head)->prev = (head); } while (0) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/msg.h b/libnl/include/netlink/msg.h new file mode 100644 index 0000000..84aaa60 --- /dev/null +++ b/libnl/include/netlink/msg.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_MSG_H_ +#define NETLINK_MSG_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nlmsghdr; + +#define NL_DONTPAD 0 + +/** + * @ingroup msg + * @brief + * Will cause the netlink port to be set to the port assigned to + * the netlink icoket ust before sending the message off. + * + * @note Requires the use of nl_send_auto()! + */ +#define NL_AUTO_PORT 0 +#define NL_AUTO_PID NL_AUTO_PORT + +/** + * @ingroup msg + * @brief + * May be used to refer to a sequence number which should be + * automatically set just before sending the message off. + * + * @note Requires the use of nl_send_auto()! + */ +#define NL_AUTO_SEQ 0 + +struct nl_msg; +struct nl_tree; +struct ucred; + +extern int nlmsg_size(int); +extern int nlmsg_total_size(int); +extern int nlmsg_padlen(int); + +extern void * nlmsg_data(const struct nlmsghdr *); +extern int nlmsg_datalen(const struct nlmsghdr *); +extern void * nlmsg_tail(const struct nlmsghdr *); + +/* attribute access */ +extern struct nlattr * nlmsg_attrdata(const struct nlmsghdr *, int); +extern int nlmsg_attrlen(const struct nlmsghdr *, int); + +/* message parsing */ +extern int nlmsg_valid_hdr(const struct nlmsghdr *, int); +extern int nlmsg_ok(const struct nlmsghdr *, int); +extern struct nlmsghdr * nlmsg_next(struct nlmsghdr *, int *); +extern int nlmsg_parse(struct nlmsghdr *, int, struct nlattr **, + int, const struct nla_policy *); +extern struct nlattr * nlmsg_find_attr(struct nlmsghdr *, int, int); +extern int nlmsg_validate(struct nlmsghdr *, int, int, + const struct nla_policy *); + +extern struct nl_msg * nlmsg_alloc(void); +extern struct nl_msg * nlmsg_alloc_size(size_t); +extern struct nl_msg * nlmsg_alloc_simple(int, int); +extern void nlmsg_set_default_size(size_t); +extern struct nl_msg * nlmsg_inherit(struct nlmsghdr *); +extern struct nl_msg * nlmsg_convert(struct nlmsghdr *); +extern void * nlmsg_reserve(struct nl_msg *, size_t, int); +extern int nlmsg_append(struct nl_msg *, void *, size_t, int); +extern int nlmsg_expand(struct nl_msg *, size_t); + +extern struct nlmsghdr * nlmsg_put(struct nl_msg *, uint32_t, uint32_t, + int, int, int); +extern struct nlmsghdr * nlmsg_hdr(struct nl_msg *); +extern void nlmsg_get(struct nl_msg *); +extern void nlmsg_free(struct nl_msg *); + +/* attribute modification */ +extern void nlmsg_set_proto(struct nl_msg *, int); +extern int nlmsg_get_proto(struct nl_msg *); +extern size_t nlmsg_get_max_size(struct nl_msg *); +extern void nlmsg_set_src(struct nl_msg *, struct sockaddr_nl *); +extern struct sockaddr_nl *nlmsg_get_src(struct nl_msg *); +extern void nlmsg_set_dst(struct nl_msg *, struct sockaddr_nl *); +extern struct sockaddr_nl *nlmsg_get_dst(struct nl_msg *); +extern void nlmsg_set_creds(struct nl_msg *, struct ucred *); +extern struct ucred * nlmsg_get_creds(struct nl_msg *); + +extern char * nl_nlmsgtype2str(int, char *, size_t); +extern int nl_str2nlmsgtype(const char *); + +extern char * nl_nlmsg_flags2str(int, char *, size_t); + +extern int nl_msg_parse(struct nl_msg *, + void (*cb)(struct nl_object *, void *), + void *); + +extern void nl_msg_dump(struct nl_msg *, FILE *); + +/** + * @name Iterators + * @{ + */ + +/** + * @ingroup msg + * Iterate over a stream of attributes in a message + * @arg pos loop counter, set to current attribute + * @arg nlh netlink message header + * @arg hdrlen length of family header + * @arg rem initialized to len, holds bytes currently remaining in stream + */ +#define nlmsg_for_each_attr(pos, nlh, hdrlen, rem) \ + nla_for_each_attr(pos, nlmsg_attrdata(nlh, hdrlen), \ + nlmsg_attrlen(nlh, hdrlen), rem) + +/** + * Iterate over a stream of messages + * @arg pos loop counter, set to current message + * @arg head head of message stream + * @arg len length of message stream + */ +#define nlmsg_for_each(pos, head, len) \ + for (int rem = len, pos = head; \ + nlmsg_ok(pos, rem); \ + pos = nlmsg_next(pos, &rem)) + +#define nlmsg_for_each_msg(pos, head, len, rem) \ + nlmsg_for_each(pos, head, len) + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/netfilter/ct.h b/libnl/include/netlink/netfilter/ct.h new file mode 100644 index 0000000..ddf1373 --- /dev/null +++ b/libnl/include/netlink/netfilter/ct.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#ifndef NETLINK_CT_H_ +#define NETLINK_CT_H_ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nfnl_ct; + +struct nfnl_ct_timestamp { + uint64_t start; + uint64_t stop; +}; + +extern struct nl_object_ops ct_obj_ops; + +extern struct nfnl_ct * nfnl_ct_alloc(void); +extern int nfnl_ct_alloc_cache(struct nl_sock *, struct nl_cache **); + +extern int nfnlmsg_ct_group(struct nlmsghdr *); +extern int nfnlmsg_ct_parse(struct nlmsghdr *, struct nfnl_ct **); +extern int nfnlmsg_ct_parse_nested(struct nlattr *, struct nfnl_ct **); + +extern void nfnl_ct_get(struct nfnl_ct *); +extern void nfnl_ct_put(struct nfnl_ct *); + +extern int nfnl_ct_dump_request(struct nl_sock *); + +extern int nfnl_ct_build_add_request(const struct nfnl_ct *, int, + struct nl_msg **); +extern int nfnl_ct_add(struct nl_sock *, const struct nfnl_ct *, int); + +extern int nfnl_ct_build_delete_request(const struct nfnl_ct *, int, + struct nl_msg **); +extern int nfnl_ct_del(struct nl_sock *, const struct nfnl_ct *, int); + +extern int nfnl_ct_build_query_request(const struct nfnl_ct *, int, + struct nl_msg **); +extern int nfnl_ct_query(struct nl_sock *, const struct nfnl_ct *, int); + +extern void nfnl_ct_set_family(struct nfnl_ct *, uint8_t); +extern uint8_t nfnl_ct_get_family(const struct nfnl_ct *); + +extern void nfnl_ct_set_proto(struct nfnl_ct *, uint8_t); +extern int nfnl_ct_test_proto(const struct nfnl_ct *); +extern uint8_t nfnl_ct_get_proto(const struct nfnl_ct *); + +extern void nfnl_ct_set_tcp_state(struct nfnl_ct *, uint8_t); +extern int nfnl_ct_test_tcp_state(const struct nfnl_ct *); +extern uint8_t nfnl_ct_get_tcp_state(const struct nfnl_ct *); +extern char * nfnl_ct_tcp_state2str(uint8_t, char *, size_t); +extern int nfnl_ct_str2tcp_state(const char *name); + +extern void nfnl_ct_set_status(struct nfnl_ct *, uint32_t); +extern void nfnl_ct_unset_status(struct nfnl_ct *, uint32_t); +extern int nfnl_ct_test_status(const struct nfnl_ct *ct); +extern uint32_t nfnl_ct_get_status(const struct nfnl_ct *); +extern char * nfnl_ct_status2str(int, char *, size_t); +extern int nfnl_ct_str2status(const char *); + +extern void nfnl_ct_set_timeout(struct nfnl_ct *, uint32_t); +extern int nfnl_ct_test_timeout(const struct nfnl_ct *); +extern uint32_t nfnl_ct_get_timeout(const struct nfnl_ct *); + +extern void nfnl_ct_set_mark(struct nfnl_ct *, uint32_t); +extern int nfnl_ct_test_mark(const struct nfnl_ct *); +extern uint32_t nfnl_ct_get_mark(const struct nfnl_ct *); + +extern void nfnl_ct_set_use(struct nfnl_ct *, uint32_t); +extern int nfnl_ct_test_use(const struct nfnl_ct *); +extern uint32_t nfnl_ct_get_use(const struct nfnl_ct *); + +extern void nfnl_ct_set_id(struct nfnl_ct *, uint32_t); +extern int nfnl_ct_test_id(const struct nfnl_ct *); +extern uint32_t nfnl_ct_get_id(const struct nfnl_ct *); + +extern void nfnl_ct_set_zone(struct nfnl_ct *, uint16_t); +extern int nfnl_ct_test_zone(const struct nfnl_ct *); +extern uint16_t nfnl_ct_get_zone(const struct nfnl_ct *); + +extern int nfnl_ct_set_src(struct nfnl_ct *, int, struct nl_addr *); +extern struct nl_addr * nfnl_ct_get_src(const struct nfnl_ct *, int); + +extern int nfnl_ct_set_dst(struct nfnl_ct *, int, struct nl_addr *); +extern struct nl_addr * nfnl_ct_get_dst(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_src_port(struct nfnl_ct *, int, uint16_t); +extern int nfnl_ct_test_src_port(const struct nfnl_ct *, int); +extern uint16_t nfnl_ct_get_src_port(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_dst_port(struct nfnl_ct *, int, uint16_t); +extern int nfnl_ct_test_dst_port(const struct nfnl_ct *, int); +extern uint16_t nfnl_ct_get_dst_port(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_icmp_id(struct nfnl_ct *, int, uint16_t); +extern int nfnl_ct_test_icmp_id(const struct nfnl_ct *, int); +extern uint16_t nfnl_ct_get_icmp_id(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_icmp_type(struct nfnl_ct *, int, uint8_t); +extern int nfnl_ct_test_icmp_type(const struct nfnl_ct *, int); +extern uint8_t nfnl_ct_get_icmp_type(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_icmp_code(struct nfnl_ct *, int, uint8_t); +extern int nfnl_ct_test_icmp_code(const struct nfnl_ct *, int); +extern uint8_t nfnl_ct_get_icmp_code(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_packets(struct nfnl_ct *, int, uint64_t); +extern int nfnl_ct_test_packets(const struct nfnl_ct *, int); +extern uint64_t nfnl_ct_get_packets(const struct nfnl_ct *,int); + +extern void nfnl_ct_set_bytes(struct nfnl_ct *, int, uint64_t); +extern int nfnl_ct_test_bytes(const struct nfnl_ct *, int); +extern uint64_t nfnl_ct_get_bytes(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_timestamp(struct nfnl_ct *, uint64_t, uint64_t); +extern int nfnl_ct_test_timestamp(const struct nfnl_ct *); +extern const struct nfnl_ct_timestamp *nfnl_ct_get_timestamp(const struct nfnl_ct *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/netfilter/exp.h b/libnl/include/netlink/netfilter/exp.h new file mode 100644 index 0000000..bd80514 --- /dev/null +++ b/libnl/include/netlink/netfilter/exp.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2012 Rich Fought + */ + +#ifndef NETLINK_EXP_H_ +#define NETLINK_EXP_H_ + +#include +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nfnl_exp; + +enum nfnl_exp_tuples { + NFNL_EXP_TUPLE_EXPECT, + NFNL_EXP_TUPLE_MASTER, + NFNL_EXP_TUPLE_MASK, + NFNL_EXP_TUPLE_NAT, + NFNL_EXP_TUPLE_MAX +}; + +extern struct nl_object_ops exp_obj_ops; + +extern struct nfnl_exp * nfnl_exp_alloc(void); +extern int nfnl_exp_alloc_cache(struct nl_sock *, struct nl_cache **); + +extern int nfnlmsg_exp_group(struct nlmsghdr *); +extern int nfnlmsg_exp_parse(struct nlmsghdr *, struct nfnl_exp **); + +extern void nfnl_exp_get(struct nfnl_exp *); +extern void nfnl_exp_put(struct nfnl_exp *); + +extern int nfnl_exp_dump_request(struct nl_sock *); + +extern int nfnl_exp_build_add_request(const struct nfnl_exp *, int, + struct nl_msg **); +extern int nfnl_exp_add(struct nl_sock *, const struct nfnl_exp *, int); + +extern int nfnl_exp_build_delete_request(const struct nfnl_exp *, int, + struct nl_msg **); +extern int nfnl_exp_del(struct nl_sock *, const struct nfnl_exp *, int); + +extern int nfnl_exp_build_query_request(const struct nfnl_exp *, int, + struct nl_msg **); +extern int nfnl_exp_query(struct nl_sock *, const struct nfnl_exp *, int); + +extern void nfnl_exp_set_family(struct nfnl_exp *, uint8_t); +extern uint8_t nfnl_exp_get_family(const struct nfnl_exp *); + +extern void nfnl_exp_set_timeout(struct nfnl_exp *, uint32_t); +extern int nfnl_exp_test_timeout(const struct nfnl_exp *); +extern uint32_t nfnl_exp_get_timeout(const struct nfnl_exp *); + +extern void nfnl_exp_set_id(struct nfnl_exp *, uint32_t); +extern int nfnl_exp_test_id(const struct nfnl_exp *); +extern uint32_t nfnl_exp_get_id(const struct nfnl_exp *); + +extern int nfnl_exp_set_helper_name(struct nfnl_exp *, void *); +extern int nfnl_exp_test_helper_name(const struct nfnl_exp *); +extern const char * nfnl_exp_get_helper_name(const struct nfnl_exp *); + +extern void nfnl_exp_set_zone(struct nfnl_exp *, uint16_t); +extern int nfnl_exp_test_zone(const struct nfnl_exp *); +extern uint16_t nfnl_exp_get_zone(const struct nfnl_exp *); + +extern void nfnl_exp_set_flags(struct nfnl_exp *, uint32_t); +extern int nfnl_exp_test_flags(const struct nfnl_exp *); +extern void nfnl_exp_unset_flags(struct nfnl_exp *exp, uint32_t flags); +extern uint32_t nfnl_exp_get_flags(const struct nfnl_exp *); +extern char * nfnl_exp_flags2str(int flags, char *buf, size_t len); +int nfnl_exp_str2flags(const char *name); + +extern void nfnl_exp_set_class(struct nfnl_exp *, uint32_t); +extern int nfnl_exp_test_class(const struct nfnl_exp *); +extern uint32_t nfnl_exp_get_class(const struct nfnl_exp *); + +extern int nfnl_exp_set_fn(struct nfnl_exp *, void *); +extern int nfnl_exp_test_fn(const struct nfnl_exp *); +extern const char * nfnl_exp_get_fn(const struct nfnl_exp *); + +extern void nfnl_exp_set_nat_dir(struct nfnl_exp *, uint8_t); +extern int nfnl_exp_test_nat_dir(const struct nfnl_exp *); +extern uint8_t nfnl_exp_get_nat_dir(const struct nfnl_exp *); + +// The int argument specifies which nfnl_exp_dir (expect, master, mask or nat) +// Expectation objects only use orig, not reply + +extern int nfnl_exp_set_src(struct nfnl_exp *, int, struct nl_addr *); +extern int nfnl_exp_test_src(const struct nfnl_exp *, int); +extern struct nl_addr * nfnl_exp_get_src(const struct nfnl_exp *, int); + +extern int nfnl_exp_set_dst(struct nfnl_exp *, int, struct nl_addr *); +extern int nfnl_exp_test_dst(const struct nfnl_exp *, int); +extern struct nl_addr * nfnl_exp_get_dst(const struct nfnl_exp *, int); + +extern void nfnl_exp_set_l4protonum(struct nfnl_exp *, int, uint8_t); +extern int nfnl_exp_test_l4protonum(const struct nfnl_exp *, int); +extern uint8_t nfnl_exp_get_l4protonum(const struct nfnl_exp *, int); + +extern void nfnl_exp_set_ports(struct nfnl_exp *, int, uint16_t, uint16_t); +extern int nfnl_exp_test_ports(const struct nfnl_exp *, int); +extern uint16_t nfnl_exp_get_src_port(const struct nfnl_exp *, int); +extern uint16_t nfnl_exp_get_dst_port(const struct nfnl_exp *, int); + +extern void nfnl_exp_set_icmp(struct nfnl_exp *, int, uint16_t, uint8_t, uint8_t); +extern int nfnl_exp_test_icmp(const struct nfnl_exp *, int); +extern uint16_t nfnl_exp_get_icmp_id(const struct nfnl_exp *, int); +extern uint8_t nfnl_exp_get_icmp_type(const struct nfnl_exp *, int); +extern uint8_t nfnl_exp_get_icmp_code(const struct nfnl_exp *, int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/netfilter/log.h b/libnl/include/netlink/netfilter/log.h new file mode 100644 index 0000000..a8ca81d --- /dev/null +++ b/libnl/include/netlink/netfilter/log.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2008 Patrick McHardy + */ + +#ifndef NETLINK_LOG_H_ +#define NETLINK_LOG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_sock; +struct nlmsghdr; +struct nfnl_log; + +extern struct nl_object_ops log_obj_ops; + +enum nfnl_log_copy_mode { + NFNL_LOG_COPY_NONE, + NFNL_LOG_COPY_META, + NFNL_LOG_COPY_PACKET, +}; + +enum nfnl_log_flags { + NFNL_LOG_FLAG_SEQ = 0x1, + NFNL_LOG_FLAG_SEQ_GLOBAL = 0x2, + NFNL_LOG_FLAG_CONNTRACK = 0x4, +}; + +/* General */ +extern struct nfnl_log * nfnl_log_alloc(void); +extern int nfnlmsg_log_parse(struct nlmsghdr *, + struct nfnl_log **); + +extern void nfnl_log_get(struct nfnl_log *); +extern void nfnl_log_put(struct nfnl_log *); + +/* Attributes */ +extern void nfnl_log_set_group(struct nfnl_log *, uint16_t); +extern int nfnl_log_test_group(const struct nfnl_log *); +extern uint16_t nfnl_log_get_group(const struct nfnl_log *); + +extern void nfnl_log_set_copy_mode(struct nfnl_log *, + enum nfnl_log_copy_mode); +extern int nfnl_log_test_copy_mode(const struct nfnl_log *); +extern enum nfnl_log_copy_mode nfnl_log_get_copy_mode(const struct nfnl_log *); + +extern char * nfnl_log_copy_mode2str(enum nfnl_log_copy_mode, + char *, size_t); +extern int nfnl_log_str2copy_mode(const char *); + +extern void nfnl_log_set_copy_range(struct nfnl_log *, uint32_t); +extern int nfnl_log_test_copy_range(const struct nfnl_log *); +extern uint32_t nfnl_log_get_copy_range(const struct nfnl_log *); + +extern void nfnl_log_set_flush_timeout(struct nfnl_log *, uint32_t); +extern int nfnl_log_test_flush_timeout(const struct nfnl_log *); +extern uint32_t nfnl_log_get_flush_timeout(const struct nfnl_log *); + +extern void nfnl_log_set_alloc_size(struct nfnl_log *, uint32_t); +extern int nfnl_log_test_alloc_size(const struct nfnl_log *); +extern uint32_t nfnl_log_get_alloc_size(const struct nfnl_log *); + +extern void nfnl_log_set_queue_threshold(struct nfnl_log *, uint32_t); +extern int nfnl_log_test_queue_threshold(const struct nfnl_log *); +extern uint32_t nfnl_log_get_queue_threshold(const struct nfnl_log *); + +extern void nfnl_log_set_flags(struct nfnl_log *, unsigned int); +extern void nfnl_log_unset_flags(struct nfnl_log *, unsigned int); +extern unsigned int nfnl_log_get_flags(const struct nfnl_log *); + +extern char * nfnl_log_flags2str(unsigned int, char *, size_t); +extern unsigned int nfnl_log_str2flags(const char *); + +extern int nfnl_log_build_pf_bind(uint8_t, struct nl_msg **); +extern int nfnl_log_pf_bind(struct nl_sock *, uint8_t); + +extern int nfnl_log_build_pf_unbind(uint8_t, struct nl_msg **); +extern int nfnl_log_pf_unbind(struct nl_sock *, uint8_t); + +extern int nfnl_log_build_create_request(const struct nfnl_log *, + struct nl_msg **); +extern int nfnl_log_create(struct nl_sock *, const struct nfnl_log *); + +extern int nfnl_log_build_change_request(const struct nfnl_log *, + struct nl_msg **); +extern int nfnl_log_change(struct nl_sock *, const struct nfnl_log *); + +extern int nfnl_log_build_delete_request(const struct nfnl_log *, + struct nl_msg **); +extern int nfnl_log_delete(struct nl_sock *, const struct nfnl_log *); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libnl/include/netlink/netfilter/log_msg.h b/libnl/include/netlink/netfilter/log_msg.h new file mode 100644 index 0000000..b3672da --- /dev/null +++ b/libnl/include/netlink/netfilter/log_msg.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2008 Patrick McHardy + */ + +#ifndef NETLINK_LOG_MSG_H_ +#define NETLINK_LOG_MSG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nlmsghdr; +struct nfnl_log_msg; +struct nfnl_ct; + +extern struct nl_object_ops log_msg_obj_ops; + +/* General */ +extern struct nfnl_log_msg *nfnl_log_msg_alloc(void); +extern int nfnlmsg_log_msg_parse(struct nlmsghdr *, + struct nfnl_log_msg **); + +extern void nfnl_log_msg_get(struct nfnl_log_msg *); +extern void nfnl_log_msg_put(struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_family(struct nfnl_log_msg *, uint8_t); +extern uint8_t nfnl_log_msg_get_family(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_hwproto(struct nfnl_log_msg *, uint16_t); +extern int nfnl_log_msg_test_hwproto(const struct nfnl_log_msg *); +extern uint16_t nfnl_log_msg_get_hwproto(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_hook(struct nfnl_log_msg *, uint8_t); +extern int nfnl_log_msg_test_hook(const struct nfnl_log_msg *); +extern uint8_t nfnl_log_msg_get_hook(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_mark(struct nfnl_log_msg *, uint32_t); +extern int nfnl_log_msg_test_mark(const struct nfnl_log_msg *); +extern uint32_t nfnl_log_msg_get_mark(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_timestamp(struct nfnl_log_msg *, + struct timeval *); +extern const struct timeval *nfnl_log_msg_get_timestamp(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_indev(struct nfnl_log_msg *, uint32_t); +extern uint32_t nfnl_log_msg_get_indev(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_outdev(struct nfnl_log_msg *, uint32_t); +extern uint32_t nfnl_log_msg_get_outdev(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_physindev(struct nfnl_log_msg *, uint32_t); +extern uint32_t nfnl_log_msg_get_physindev(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_physoutdev(struct nfnl_log_msg *, uint32_t); +extern uint32_t nfnl_log_msg_get_physoutdev(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_hwaddr(struct nfnl_log_msg *, uint8_t *, int); +extern const uint8_t * nfnl_log_msg_get_hwaddr(const struct nfnl_log_msg *, int *); + +extern int nfnl_log_msg_set_payload(struct nfnl_log_msg *, uint8_t *, int); +extern const void * nfnl_log_msg_get_payload(const struct nfnl_log_msg *, int *); + +extern int nfnl_log_msg_set_prefix(struct nfnl_log_msg *, void *); +extern const char * nfnl_log_msg_get_prefix(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_uid(struct nfnl_log_msg *, uint32_t); +extern int nfnl_log_msg_test_uid(const struct nfnl_log_msg *); +extern uint32_t nfnl_log_msg_get_uid(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_gid(struct nfnl_log_msg *, uint32_t); +extern int nfnl_log_msg_test_gid(const struct nfnl_log_msg *); +extern uint32_t nfnl_log_msg_get_gid(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_seq(struct nfnl_log_msg *, uint32_t); +extern int nfnl_log_msg_test_seq(const struct nfnl_log_msg *); +extern uint32_t nfnl_log_msg_get_seq(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_seq_global(struct nfnl_log_msg *, uint32_t); +extern int nfnl_log_msg_test_seq_global(const struct nfnl_log_msg *); +extern uint32_t nfnl_log_msg_get_seq_global(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_hwtype(struct nfnl_log_msg *, uint16_t); +extern int nfnl_log_msg_test_hwtype(const struct nfnl_log_msg *); +extern uint16_t nfnl_log_msg_get_hwtype(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_hwlen(struct nfnl_log_msg *, uint16_t); +extern int nfnl_log_msg_test_hwlen(const struct nfnl_log_msg *); +extern uint16_t nfnl_log_msg_get_hwlen(const struct nfnl_log_msg *); + +extern int nfnl_log_msg_set_hwheader(struct nfnl_log_msg *, void *, int); +extern int nfnl_log_msg_test_hwheader(const struct nfnl_log_msg *); +extern const void * nfnl_log_msg_get_hwheader(const struct nfnl_log_msg *, int *); + +extern void nfnl_log_msg_set_vlan_proto(struct nfnl_log_msg *, uint16_t); +extern int nfnl_log_msg_test_vlan_proto(const struct nfnl_log_msg *); +extern uint16_t nfnl_log_msg_get_vlan_proto(const struct nfnl_log_msg *); +extern void nfnl_log_msg_set_vlan_tag(struct nfnl_log_msg *, uint16_t); +extern int nfnl_log_msg_test_vlan_tag(const struct nfnl_log_msg *); +extern uint16_t nfnl_log_msg_get_vlan_tag(const struct nfnl_log_msg *); +extern uint16_t nfnl_log_msg_get_vlan_id(const struct nfnl_log_msg *); +extern uint16_t nfnl_log_msg_get_vlan_cfi(const struct nfnl_log_msg *); +extern uint16_t nfnl_log_msg_get_vlan_prio(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_ct_info(struct nfnl_log_msg *, uint32_t); +extern int nfnl_log_msg_test_ct_info(const struct nfnl_log_msg *); +extern uint32_t nfnl_log_msg_get_ct_info(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_ct(struct nfnl_log_msg *, struct nfnl_ct *); +extern int nfnl_log_msg_test_ct(const struct nfnl_log_msg *); +extern struct nfnl_ct * nfnl_log_msg_get_ct(const struct nfnl_log_msg *); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libnl/include/netlink/netfilter/netfilter.h b/libnl/include/netlink/netfilter/netfilter.h new file mode 100644 index 0000000..f365a21 --- /dev/null +++ b/libnl/include/netlink/netfilter/netfilter.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008 Patrick McHardy + */ + +#ifndef NETLINK_NETFILTER_H_ +#define NETLINK_NETFILTER_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern char * nfnl_verdict2str(unsigned int, char *, size_t); +extern unsigned int nfnl_str2verdict(const char *); + +extern char * nfnl_inet_hook2str(unsigned int, char *, size_t); +extern unsigned int nfnl_str2inet_hook(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/netfilter/nfnl.h b/libnl/include/netlink/netfilter/nfnl.h new file mode 100644 index 0000000..6e349ad --- /dev/null +++ b/libnl/include/netlink/netfilter/nfnl.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#ifndef NETLINK_NFNL_H_ +#define NETLINK_NFNL_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NFNL_HDRLEN NLMSG_ALIGN(sizeof(struct nfgenmsg)) +#define NFNLMSG_TYPE(subsys, subtype) (((subsys) << 8) | (subtype)) + +extern int nfnl_connect(struct nl_sock *); + +extern uint8_t nfnlmsg_subsys(struct nlmsghdr *); +extern uint8_t nfnlmsg_subtype(struct nlmsghdr *); +extern uint8_t nfnlmsg_family(struct nlmsghdr *); +extern uint16_t nfnlmsg_res_id(struct nlmsghdr *); + +extern int nfnl_send_simple(struct nl_sock *, uint8_t, uint8_t, + int, uint8_t, uint16_t); +extern struct nl_msg * nfnlmsg_alloc_simple(uint8_t, uint8_t, int, + uint8_t, uint16_t); +extern int nfnlmsg_put(struct nl_msg *, uint32_t, uint32_t, + uint8_t, uint8_t, int, uint8_t, uint16_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/netfilter/queue.h b/libnl/include/netlink/netfilter/queue.h new file mode 100644 index 0000000..f393f4e --- /dev/null +++ b/libnl/include/netlink/netfilter/queue.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2007, 2008 Patrick McHardy + */ + +#ifndef NETLINK_QUEUE_H_ +#define NETLINK_QUEUE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_sock; +struct nlmsghdr; +struct nfnl_queue; + +extern struct nl_object_ops queue_obj_ops; + +enum nfnl_queue_copy_mode { + NFNL_QUEUE_COPY_NONE, + NFNL_QUEUE_COPY_META, + NFNL_QUEUE_COPY_PACKET, +}; + +/* General */ +extern struct nl_sock * nfnl_queue_socket_alloc(void); + +extern struct nfnl_queue * nfnl_queue_alloc(void); + +extern void nfnl_queue_get(struct nfnl_queue *); +extern void nfnl_queue_put(struct nfnl_queue *); + +/* Attributes */ +extern void nfnl_queue_set_group(struct nfnl_queue *, uint16_t); +extern int nfnl_queue_test_group(const struct nfnl_queue *); +extern uint16_t nfnl_queue_get_group(const struct nfnl_queue *); + +extern void nfnl_queue_set_maxlen(struct nfnl_queue *, uint32_t); +extern int nfnl_queue_test_maxlen(const struct nfnl_queue *); +extern uint32_t nfnl_queue_get_maxlen(const struct nfnl_queue *); + +extern void nfnl_queue_set_copy_mode(struct nfnl_queue *, + enum nfnl_queue_copy_mode); +extern int nfnl_queue_test_copy_mode(const struct nfnl_queue *); +extern enum nfnl_queue_copy_mode nfnl_queue_get_copy_mode(const struct nfnl_queue *); + +extern char * nfnl_queue_copy_mode2str(enum nfnl_queue_copy_mode, + char *, size_t); +extern int nfnl_queue_str2copy_mode(const char *); + +extern void nfnl_queue_set_copy_range(struct nfnl_queue *, + uint32_t); +extern int nfnl_queue_test_copy_range(const struct nfnl_queue *); +extern uint32_t nfnl_queue_get_copy_range(const struct nfnl_queue *); + +extern int nfnl_queue_build_pf_bind(uint8_t, struct nl_msg **); +extern int nfnl_queue_pf_bind(struct nl_sock *, uint8_t); + +extern int nfnl_queue_build_pf_unbind(uint8_t, struct nl_msg **); +extern int nfnl_queue_pf_unbind(struct nl_sock *, uint8_t); + +extern int nfnl_queue_build_create_request(const struct nfnl_queue *, + struct nl_msg **); +extern int nfnl_queue_create(struct nl_sock *, + const struct nfnl_queue *); + +extern int nfnl_queue_build_change_request(const struct nfnl_queue *, + struct nl_msg **); +extern int nfnl_queue_change(struct nl_sock *, + const struct nfnl_queue *); + +extern int nfnl_queue_build_delete_request(const struct nfnl_queue *, + struct nl_msg **); +extern int nfnl_queue_delete(struct nl_sock *, + const struct nfnl_queue *); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libnl/include/netlink/netfilter/queue_msg.h b/libnl/include/netlink/netfilter/queue_msg.h new file mode 100644 index 0000000..dc333e0 --- /dev/null +++ b/libnl/include/netlink/netfilter/queue_msg.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2007, 2008 Patrick McHardy + */ + +#ifndef NETLINK_QUEUE_MSG_H_ +#define NETLINK_QUEUE_MSG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_sock; +struct nlmsghdr; +struct nfnl_queue_msg; + +extern struct nl_object_ops queue_msg_obj_ops; + +/* General */ +extern struct nfnl_queue_msg * nfnl_queue_msg_alloc(void); +extern int nfnlmsg_queue_msg_parse(struct nlmsghdr *, + struct nfnl_queue_msg **); + +extern void nfnl_queue_msg_get(struct nfnl_queue_msg *); +extern void nfnl_queue_msg_put(struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_group(struct nfnl_queue_msg *, uint16_t); +extern int nfnl_queue_msg_test_group(const struct nfnl_queue_msg *); +extern uint16_t nfnl_queue_msg_get_group(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_family(struct nfnl_queue_msg *, uint8_t); +extern int nfnl_queue_msg_test_family(const struct nfnl_queue_msg *); +extern uint8_t nfnl_queue_msg_get_family(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_packetid(struct nfnl_queue_msg *, uint32_t); +extern int nfnl_queue_msg_test_packetid(const struct nfnl_queue_msg *); +extern uint32_t nfnl_queue_msg_get_packetid(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_hwproto(struct nfnl_queue_msg *, uint16_t); +extern int nfnl_queue_msg_test_hwproto(const struct nfnl_queue_msg *); +extern uint16_t nfnl_queue_msg_get_hwproto(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_hook(struct nfnl_queue_msg *, uint8_t); +extern int nfnl_queue_msg_test_hook(const struct nfnl_queue_msg *); +extern uint8_t nfnl_queue_msg_get_hook(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_mark(struct nfnl_queue_msg *, uint32_t); +extern int nfnl_queue_msg_test_mark(const struct nfnl_queue_msg *); +extern uint32_t nfnl_queue_msg_get_mark(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_timestamp(struct nfnl_queue_msg *, + struct timeval *); +extern int nfnl_queue_msg_test_timestamp(const struct nfnl_queue_msg *); +extern const struct timeval * nfnl_queue_msg_get_timestamp(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_indev(struct nfnl_queue_msg *, uint32_t); +extern int nfnl_queue_msg_test_indev(const struct nfnl_queue_msg *); +extern uint32_t nfnl_queue_msg_get_indev(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_outdev(struct nfnl_queue_msg *, uint32_t); +extern int nfnl_queue_msg_test_outdev(const struct nfnl_queue_msg *); +extern uint32_t nfnl_queue_msg_get_outdev(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_physindev(struct nfnl_queue_msg *, uint32_t); +extern int nfnl_queue_msg_test_physindev(const struct nfnl_queue_msg *); +extern uint32_t nfnl_queue_msg_get_physindev(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_physoutdev(struct nfnl_queue_msg *, uint32_t); +extern int nfnl_queue_msg_test_physoutdev(const struct nfnl_queue_msg *); +extern uint32_t nfnl_queue_msg_get_physoutdev(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_hwaddr(struct nfnl_queue_msg *, uint8_t *, int); +extern int nfnl_queue_msg_test_hwaddr(const struct nfnl_queue_msg *); +extern const uint8_t * nfnl_queue_msg_get_hwaddr(const struct nfnl_queue_msg *, int *); + +extern int nfnl_queue_msg_set_payload(struct nfnl_queue_msg *, uint8_t *, int); +extern int nfnl_queue_msg_test_payload(const struct nfnl_queue_msg *); +extern const void * nfnl_queue_msg_get_payload(const struct nfnl_queue_msg *, int *); + +extern void nfnl_queue_msg_set_verdict(struct nfnl_queue_msg *, + unsigned int); +extern int nfnl_queue_msg_test_verdict(const struct nfnl_queue_msg *); +extern unsigned int nfnl_queue_msg_get_verdict(const struct nfnl_queue_msg *); + +extern struct nl_msg * nfnl_queue_msg_build_verdict(const struct nfnl_queue_msg *); +extern int nfnl_queue_msg_send_verdict(struct nl_sock *, + const struct nfnl_queue_msg *); + +extern struct nl_msg * nfnl_queue_msg_build_verdict_batch(const struct nfnl_queue_msg *msg); +extern int nfnl_queue_msg_send_verdict_batch(struct nl_sock *, + const struct nfnl_queue_msg *); +extern int nfnl_queue_msg_send_verdict_payload(struct nl_sock *, + const struct nfnl_queue_msg *, + const void *, unsigned ); +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libnl/include/netlink/netlink-compat.h b/libnl/include/netlink/netlink-compat.h new file mode 100644 index 0000000..76b4595 --- /dev/null +++ b/libnl/include/netlink/netlink-compat.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_COMPAT_H_ +#define NETLINK_COMPAT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined _LINUX_SOCKET_H && !defined _BITS_SOCKADDR_H +typedef unsigned short sa_family_t; +#endif + +#ifndef IFNAMSIZ +/** Maximum length of a interface name */ +#define IFNAMSIZ 16 +#endif + +/* patch 2.4.x if_arp */ +#ifndef ARPHRD_INFINIBAND +#define ARPHRD_INFINIBAND 32 +#endif + +/* patch 2.4.x eth header file */ +#ifndef ETH_P_MPLS_UC +#define ETH_P_MPLS_UC 0x8847 +#endif + +#ifndef ETH_P_MPLS_MC +#define ETH_P_MPLS_MC 0x8848 +#endif + +#ifndef ETH_P_EDP2 +#define ETH_P_EDP2 0x88A2 +#endif + +#ifndef ETH_P_HDLC +#define ETH_P_HDLC 0x0019 +#endif + +#ifndef AF_LLC +#define AF_LLC 26 +#endif + +#ifndef AF_MPLS +#define AF_MPLS 28 +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/netlink-kernel.h b/libnl/include/netlink/netlink-kernel.h new file mode 100644 index 0000000..f09051d --- /dev/null +++ b/libnl/include/netlink/netlink-kernel.h @@ -0,0 +1,293 @@ +#ifndef __NETLINK_KERNEL_H_ +#define __NETLINK_KERNEL_H_ + +#if 0 + +/* + * FIXME: Goal is to preseve the documentation but make it simple + * to keep linux/netlink.h in sync. Maybe use named documentation + * sections. + */ + +/** + * Netlink socket address + * @ingroup nl + */ +struct sockaddr_nl +{ + /** socket family (AF_NETLINK) */ + sa_family_t nl_family; + + /** Padding (unused) */ + unsigned short nl_pad; + + /** Unique process ID */ + uint32_t nl_pid; + + /** Multicast group subscriptions */ + uint32_t nl_groups; +}; + +/** + * @addtogroup msg + * @{ + */ + + +/** + * Netlink message header + */ +struct nlmsghdr +{ + /** Length of message including header and padding. */ + uint32_t nlmsg_len; + + /** Message type (content type) */ + uint16_t nlmsg_type; + + /** Message flags */ + uint16_t nlmsg_flags; + + /** Sequence number of message \see core_sk_seq_num. */ + uint32_t nlmsg_seq; + + /** Netlink port */ + uint32_t nlmsg_pid; +}; + +/** + * @name Standard message flags + * @{ + */ + +/** + * Must be set on all request messages (typically from user space to + * kernel space). + */ +#define NLM_F_REQUEST 1 + +/** + * Indicates the message is part of a multipart message terminated + * by NLMSG_DONE. + */ +#define NLM_F_MULTI 2 + +/** + * Request for an acknowledgment on success. + */ +#define NLM_F_ACK 4 + +/** + * Echo this request + */ +#define NLM_F_ECHO 8 + +/** @} */ + +/** + * @name Additional message flags for GET requests + * @{ + */ + +/** + * Return the complete table instead of a single entry. + */ +#define NLM_F_ROOT 0x100 + +/** + * Return all entries matching criteria passed in message content. + */ +#define NLM_F_MATCH 0x200 + +/** + * Return an atomic snapshot of the table being referenced. This + * may require special privileges because it has the potential to + * interrupt service in the FE for a longer time. + */ +#define NLM_F_ATOMIC 0x400 + +/** + * Dump all entries + */ +#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) + +/** @} */ + +/** + * @name Additional messsage flags for NEW requests + * @{ + */ + +/** + * Replace existing matching config object with this request. + */ +#define NLM_F_REPLACE 0x100 + +/** + * Don't replace the config object if it already exists. + */ +#define NLM_F_EXCL 0x200 + +/** + * Create config object if it doesn't already exist. + */ +#define NLM_F_CREATE 0x400 + +/** + * Add to the end of the object list. + */ +#define NLM_F_APPEND 0x800 + +/** @} */ + +/** + * @name Standard Message types + * @{ + */ + +/** + * No operation, message must be ignored + */ +#define NLMSG_NOOP 0x1 + +/** + * The message signals an error and the payload contains a nlmsgerr + * structure. This can be looked at as a NACK and typically it is + * from FEC to CPC. + */ +#define NLMSG_ERROR 0x2 + +/** + * Message terminates a multipart message. + */ +#define NLMSG_DONE 0x3 + +/** + * The message signals that data got lost + */ +#define NLMSG_OVERRUN 0x4 + +/** + * Lower limit of reserved message types + */ +#define NLMSG_MIN_TYPE 0x10 + +/** @} */ + +/** + * Netlink error message header + */ +struct nlmsgerr +{ + /** Error code (errno number) */ + int error; + + /** Original netlink message causing the error */ + struct nlmsghdr msg; +}; + +struct nl_pktinfo +{ + __u32 group; +}; + +/** + * Netlink alignment constant, all boundries within messages must be align to this. + * + * See \ref core_msg_fmt_align for more information on message alignment. + */ +#define NLMSG_ALIGNTO 4 + +/** + * Returns \p len properly aligned to NLMSG_ALIGNTO. + * + * See \ref core_msg_fmt_align for more information on message alignment. + */ +#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) + +/** + * Length of a netlink message header including padding. + * + * See \ref core_msg_fmt_align for more information on message alignment. + */ +#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) + +/** @} */ + +/** + * @addtogroup attr + * @{ + */ + +/* + */ + +/** + * Netlink attribute structure + * + * @code + * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)--> + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * | Header | Pad | Payload | Pad | + * | (struct nlattr) | ing | | ing | + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * <-------------- nlattr->nla_len --------------> + * @endcode + */ +struct nlattr { + /** + * Attribute length in bytes including header + */ + __u16 nla_len; + + /** + * Netlink attribute type + */ + __u16 nla_type; +}; + +/** + * @name Attribute Type Flags + * + * @code + * nla_type (16 bits) + * +---+---+-------------------------------+ + * | N | O | Attribute Type | + * +---+---+-------------------------------+ + * N := Carries nested attributes + * O := Payload stored in network byte order + * @endcode + * + * @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 + +/** + * Returns \p len properly aligned to NLA_ALIGNTO. + * + * See \ref core_msg_fmt_align for more information on message alignment. + */ +#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) + +/** + * Length of a netlink attribute header including padding. + * + * See \ref core_msg_fmt_align for more information on message alignment. + */ +#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) + +/** @} */ + +#endif +#endif /* __LINUX_NETLINK_H */ diff --git a/libnl/include/netlink/netlink.h b/libnl/include/netlink/netlink.h new file mode 100644 index 0000000..c5ec350 --- /dev/null +++ b/libnl/include/netlink/netlink.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_NETLINK_H_ +#define NETLINK_NETLINK_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nlmsghdr; +struct ucred; +struct nl_cache_ops; +struct nl_parser_param; +struct nl_object; +struct nl_sock; + +extern int nl_debug; +extern struct nl_dump_params nl_debug_dp; + +/* Connection Management */ +extern int nl_connect(struct nl_sock *, int); +extern void nl_close(struct nl_sock *); + +/* Send */ +extern int nl_sendto(struct nl_sock *, void *, size_t); +extern int nl_sendmsg(struct nl_sock *, struct nl_msg *, + struct msghdr *); +extern int nl_send(struct nl_sock *, struct nl_msg *); +extern int nl_send_iovec(struct nl_sock *, struct nl_msg *, + struct iovec *, unsigned); +extern void nl_complete_msg(struct nl_sock *, + struct nl_msg *); +extern void nl_auto_complete(struct nl_sock *, + struct nl_msg *); +extern int nl_send_auto(struct nl_sock *, struct nl_msg *); +extern int nl_send_auto_complete(struct nl_sock *, + struct nl_msg *); +extern int nl_send_sync(struct nl_sock *, struct nl_msg *); +extern int nl_send_simple(struct nl_sock *, int, int, + void *, size_t); + +/* Receive */ +extern int nl_recv(struct nl_sock *, + struct sockaddr_nl *, unsigned char **, + struct ucred **); + +extern int nl_recvmsgs(struct nl_sock *, struct nl_cb *); +extern int nl_recvmsgs_report(struct nl_sock *, struct nl_cb *); + +extern int nl_recvmsgs_default(struct nl_sock *); + +extern int nl_wait_for_ack(struct nl_sock *); + +extern int nl_pickup(struct nl_sock *, + int (*parser)(struct nl_cache_ops *, + struct sockaddr_nl *, + struct nlmsghdr *, + struct nl_parser_param *), + struct nl_object **); +extern int nl_pickup_keep_syserr(struct nl_sock *sk, + int (*parser)(struct nl_cache_ops *, struct sockaddr_nl *, + struct nlmsghdr *, struct nl_parser_param *), + struct nl_object **result, + int *syserror); +/* Netlink Family Translations */ +extern char * nl_nlfamily2str(int, char *, size_t); +extern int nl_str2nlfamily(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/object-api.h b/libnl/include/netlink/object-api.h new file mode 100644 index 0000000..d08bafa --- /dev/null +++ b/libnl/include/netlink/object-api.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Thomas Graf + */ + +#ifndef NETLINK_DUMMY_OBJECT_API_H_ +#define NETLINK_DUMMY_OBJECT_API_H_ + +#include +#include +#include + +#endif diff --git a/libnl/include/netlink/object.h b/libnl/include/netlink/object.h new file mode 100644 index 0000000..7448df6 --- /dev/null +++ b/libnl/include/netlink/object.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef NETLINK_OBJECT_H_ +#define NETLINK_OBJECT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_cache; +struct nl_object; +struct nl_object_ops; + +#define OBJ_CAST(ptr) ((struct nl_object *) (ptr)) + +/* General */ +extern struct nl_object * nl_object_alloc(struct nl_object_ops *); +extern int nl_object_alloc_name(const char *, + struct nl_object **); +extern void nl_object_free(struct nl_object *); +extern struct nl_object * nl_object_clone(struct nl_object *obj); +extern int nl_object_update(struct nl_object *dst, + struct nl_object *src); +extern void nl_object_get(struct nl_object *); +extern void nl_object_put(struct nl_object *); +extern int nl_object_shared(struct nl_object *); +extern void nl_object_dump(struct nl_object *, + struct nl_dump_params *); +extern void nl_object_dump_buf(struct nl_object *, char *, size_t); +extern int nl_object_identical(struct nl_object *, + struct nl_object *); +extern uint32_t nl_object_diff(struct nl_object *, + struct nl_object *); +extern uint64_t nl_object_diff64(struct nl_object *, + struct nl_object *); +extern int nl_object_match_filter(struct nl_object *, + struct nl_object *); +extern char * nl_object_attrs2str(struct nl_object *, + uint32_t attrs, char *buf, + size_t); +extern char * nl_object_attr_list(struct nl_object *, + char *, size_t); +extern void nl_object_keygen(struct nl_object *, + uint32_t *, uint32_t); + +/* Marks */ +extern void nl_object_mark(struct nl_object *); +extern void nl_object_unmark(struct nl_object *); +extern int nl_object_is_marked(struct nl_object *); + +/* Access Functions */ +extern int nl_object_get_refcnt(struct nl_object *); +extern struct nl_cache * nl_object_get_cache(struct nl_object *); +extern const char * nl_object_get_type(const struct nl_object *); +extern int nl_object_get_msgtype(const struct nl_object *); +struct nl_object_ops * nl_object_get_ops(const struct nl_object *); +uint32_t nl_object_get_id_attrs(struct nl_object *obj); + + +static inline void * nl_object_priv(struct nl_object *obj) +{ + return obj; +} + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/act/gact.h b/libnl/include/netlink/route/act/gact.h new file mode 100644 index 0000000..b3a57f7 --- /dev/null +++ b/libnl/include/netlink/route/act/gact.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Sushma Sitaram + */ + +#ifndef NETLINK_GACT_H_ +#define NETLINK_GACT_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_gact_set_action(struct rtnl_act *act, int action); +extern int rtnl_gact_get_action(struct rtnl_act *act); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/act/mirred.h b/libnl/include/netlink/route/act/mirred.h new file mode 100644 index 0000000..14fe988 --- /dev/null +++ b/libnl/include/netlink/route/act/mirred.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +#ifndef NETLINK_MIRRED_H_ +#define NETLINK_MIRRED_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_mirred_set_action(struct rtnl_act *, int); +extern int rtnl_mirred_get_action(struct rtnl_act *); +extern int rtnl_mirred_set_ifindex(struct rtnl_act *, uint32_t); +extern uint32_t rtnl_mirred_get_ifindex(struct rtnl_act *); +extern int rtnl_mirred_set_policy(struct rtnl_act *, int); +extern int rtnl_mirred_get_policy(struct rtnl_act *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/act/nat.h b/libnl/include/netlink/route/act/nat.h new file mode 100644 index 0000000..41295ce --- /dev/null +++ b/libnl/include/netlink/route/act/nat.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Magnus Öberg + */ + +#ifndef NETLINK_NAT_H_ +#define NETLINK_NAT_H_ + +#include +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_nat_set_old_addr(struct rtnl_act *act, in_addr_t addr); +extern int rtnl_nat_get_old_addr(struct rtnl_act *act, in_addr_t *addr); +extern int rtnl_nat_set_new_addr(struct rtnl_act *act, in_addr_t addr); +extern int rtnl_nat_get_new_addr(struct rtnl_act *act, in_addr_t *addr); +extern int rtnl_nat_set_mask(struct rtnl_act *act, in_addr_t bitmask); +extern int rtnl_nat_get_mask(struct rtnl_act *act, in_addr_t *bitmask); +extern int rtnl_nat_set_flags(struct rtnl_act *act, uint32_t flags); +extern int rtnl_nat_get_flags(struct rtnl_act *act, uint32_t *flags); +extern int rtnl_nat_set_action(struct rtnl_act *act, int action); +extern int rtnl_nat_get_action(struct rtnl_act *act, int *action); + +#ifdef __cplusplus +} +#endif + +#endif /* NETLINK_NAT_H */ diff --git a/libnl/include/netlink/route/act/skbedit.h b/libnl/include/netlink/route/act/skbedit.h new file mode 100644 index 0000000..3e662ad --- /dev/null +++ b/libnl/include/netlink/route/act/skbedit.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2015 Cong Wang + */ + +#ifndef NETLINK_SKBEDIT_H_ +#define NETLINK_SKBEDIT_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_skbedit_set_action(struct rtnl_act *act, int action); +extern int rtnl_skbedit_get_action(struct rtnl_act *act); +extern int rtnl_skbedit_set_queue_mapping(struct rtnl_act *act, uint16_t index); +extern int rtnl_skbedit_get_queue_mapping(struct rtnl_act *act, uint16_t *index); +extern int rtnl_skbedit_set_mark(struct rtnl_act *act, uint32_t mark); +extern int rtnl_skbedit_get_mark(struct rtnl_act *act, uint32_t *mark); +extern int rtnl_skbedit_set_priority(struct rtnl_act *act, uint32_t prio); +extern int rtnl_skbedit_get_priority(struct rtnl_act *act, uint32_t *prio); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/act/vlan.h b/libnl/include/netlink/route/act/vlan.h new file mode 100644 index 0000000..f5001ba --- /dev/null +++ b/libnl/include/netlink/route/act/vlan.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2018 Volodymyr Bendiuga + */ + +#ifndef NETLINK_VLAN_H_ +#define NETLINK_VLAN_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_vlan_set_mode(struct rtnl_act *act, int mode); +extern int rtnl_vlan_get_mode(struct rtnl_act *act, int *out_mode); +extern int rtnl_vlan_set_action(struct rtnl_act *act, int action); +extern int rtnl_vlan_get_action(struct rtnl_act *act, int *out_action); +extern int rtnl_vlan_set_protocol(struct rtnl_act *act, uint16_t protocol); +extern int rtnl_vlan_get_protocol(struct rtnl_act *act, uint16_t *out_protocol); +extern int rtnl_vlan_set_vlan_id(struct rtnl_act *act, uint16_t vid); +extern int rtnl_vlan_get_vlan_id(struct rtnl_act *act, uint16_t *out_vid); +extern int rtnl_vlan_set_vlan_prio(struct rtnl_act *act, uint8_t prio); +extern int rtnl_vlan_get_vlan_prio(struct rtnl_act *act, uint8_t *out_prio); + +#ifdef __cplusplus +} +#endif + +#endif /* NETLINK_VLAN_H_ */ diff --git a/libnl/include/netlink/route/action.h b/libnl/include/netlink/route/action.h new file mode 100644 index 0000000..fed491f --- /dev/null +++ b/libnl/include/netlink/route/action.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +#ifndef NETLINK_ACTION_H_ +#define NETLINK_ACTION_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_act *rtnl_act_alloc(void); +extern struct rtnl_act *rtnl_act_next(struct rtnl_act *); +extern void rtnl_act_get(struct rtnl_act *); +extern void rtnl_act_put(struct rtnl_act *); +extern int rtnl_act_build_add_request(struct rtnl_act *, int, + struct nl_msg **); +extern int rtnl_act_add(struct nl_sock *, struct rtnl_act *, int); +extern int rtnl_act_change(struct nl_sock *, struct rtnl_act *, int); + +extern int rtnl_act_build_change_request(struct rtnl_act *, int, + struct nl_msg **); +extern int rtnl_act_build_delete_request(struct rtnl_act *, int, + struct nl_msg **); +extern int rtnl_act_delete(struct nl_sock *, struct rtnl_act *, + int); +extern int rtnl_act_append(struct rtnl_act **, struct rtnl_act *); +extern int rtnl_act_remove(struct rtnl_act **, struct rtnl_act *); +extern int rtnl_act_fill(struct nl_msg *, int, struct rtnl_act *); +extern void rtnl_act_put_all(struct rtnl_act **); +extern int rtnl_act_parse(struct rtnl_act **, struct nlattr *); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/addr.h b/libnl/include/netlink/route/addr.h new file mode 100644 index 0000000..240ae48 --- /dev/null +++ b/libnl/include/netlink/route/addr.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + * Copyright (c) 2003-2006 Baruch Even + * Copyright (c) 2003-2006 Mediatrix Telecom, inc. + */ + +#ifndef NETADDR_ADDR_H_ +#define NETADDR_ADDR_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_addr; + +/* General */ +extern struct rtnl_addr *rtnl_addr_alloc(void); +extern void rtnl_addr_put(struct rtnl_addr *); + +extern int rtnl_addr_alloc_cache(struct nl_sock *, struct nl_cache **); +extern struct rtnl_addr * + rtnl_addr_get(struct nl_cache *, int, struct nl_addr *); + +extern int rtnl_addr_build_add_request(struct rtnl_addr *, int, + struct nl_msg **); +extern int rtnl_addr_add(struct nl_sock *, struct rtnl_addr *, int); + +extern int rtnl_addr_build_delete_request(struct rtnl_addr *, int, + struct nl_msg **); +extern int rtnl_addr_delete(struct nl_sock *, + struct rtnl_addr *, int); + +extern char * rtnl_addr_flags2str(int, char *, size_t); +extern int rtnl_addr_str2flags(const char *); + +extern int rtnl_addr_set_label(struct rtnl_addr *, const char *); +extern char * rtnl_addr_get_label(struct rtnl_addr *); + +extern void rtnl_addr_set_ifindex(struct rtnl_addr *, int); +extern int rtnl_addr_get_ifindex(struct rtnl_addr *); + +extern void rtnl_addr_set_link(struct rtnl_addr *, struct rtnl_link *); +extern struct rtnl_link * + rtnl_addr_get_link(struct rtnl_addr *); + +extern void rtnl_addr_set_family(struct rtnl_addr *, int); +extern int rtnl_addr_get_family(struct rtnl_addr *); + +extern void rtnl_addr_set_prefixlen(struct rtnl_addr *, int); +extern int rtnl_addr_get_prefixlen(struct rtnl_addr *); + +extern void rtnl_addr_set_scope(struct rtnl_addr *, int); +extern int rtnl_addr_get_scope(struct rtnl_addr *); + +extern void rtnl_addr_set_flags(struct rtnl_addr *, unsigned int); +extern void rtnl_addr_unset_flags(struct rtnl_addr *, unsigned int); +extern unsigned int rtnl_addr_get_flags(struct rtnl_addr *); + +extern int rtnl_addr_set_local(struct rtnl_addr *, + struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_local(struct rtnl_addr *); + +extern int rtnl_addr_set_peer(struct rtnl_addr *, struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_peer(struct rtnl_addr *); + +extern int rtnl_addr_set_broadcast(struct rtnl_addr *, struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_broadcast(struct rtnl_addr *); + +extern int rtnl_addr_set_multicast(struct rtnl_addr *, struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_multicast(struct rtnl_addr *); + +extern int rtnl_addr_set_anycast(struct rtnl_addr *, struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_anycast(struct rtnl_addr *); + +extern uint32_t rtnl_addr_get_valid_lifetime(struct rtnl_addr *); +extern void rtnl_addr_set_valid_lifetime(struct rtnl_addr *, uint32_t); +extern uint32_t rtnl_addr_get_preferred_lifetime(struct rtnl_addr *); +extern void rtnl_addr_set_preferred_lifetime(struct rtnl_addr *, uint32_t); +extern uint32_t rtnl_addr_get_create_time(struct rtnl_addr *); +extern uint32_t rtnl_addr_get_last_update_time(struct rtnl_addr *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/class.h b/libnl/include/netlink/route/class.h new file mode 100644 index 0000000..91252f1 --- /dev/null +++ b/libnl/include/netlink/route/class.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +#ifndef NETLINK_CLASS_H_ +#define NETLINK_CLASS_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_class; + +extern struct rtnl_class * + rtnl_class_alloc(void); +extern void rtnl_class_put(struct rtnl_class *); + +extern int rtnl_class_alloc_cache(struct nl_sock *, int, + struct nl_cache **); +extern struct rtnl_class * + rtnl_class_get(struct nl_cache *, int, uint32_t); + +extern struct rtnl_class * + rtnl_class_get_by_parent(struct nl_cache *, int, uint32_t); + +extern struct rtnl_qdisc * + rtnl_class_leaf_qdisc(struct rtnl_class *, + struct nl_cache *); + +extern int rtnl_class_build_add_request(struct rtnl_class *, int, + struct nl_msg **); +extern int rtnl_class_add(struct nl_sock *, struct rtnl_class *, + int); + +extern int rtnl_class_build_delete_request(struct rtnl_class *, + struct nl_msg **); +extern int rtnl_class_delete(struct nl_sock *, + struct rtnl_class *); + +/* deprecated functions */ +extern void rtnl_class_foreach_child(struct rtnl_class *, + struct nl_cache *, + void (*cb)(struct nl_object *, + void *), + void *) + __attribute__((deprecated)); +extern void rtnl_class_foreach_cls(struct rtnl_class *, + struct nl_cache *, + void (*cb)(struct nl_object *, + void *), + void *) + __attribute__((deprecated)); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/classifier.h b/libnl/include/netlink/route/classifier.h new file mode 100644 index 0000000..d92652b --- /dev/null +++ b/libnl/include/netlink/route/classifier.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +#ifndef NETLINK_CLASSIFIER_H_ +#define NETLINK_CLASSIFIER_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_cls *rtnl_cls_alloc(void); +extern void rtnl_cls_put(struct rtnl_cls *); + +extern int rtnl_cls_alloc_cache(struct nl_sock *, int, uint32_t, + struct nl_cache **); +extern struct rtnl_cls *rtnl_cls_find_by_handle(struct nl_cache *cache, int ifindex, + uint32_t parent, uint32_t handle); +extern struct rtnl_cls *rtnl_cls_find_by_prio(struct nl_cache *cache, int ifindex, + uint32_t parent, uint16_t prio); + +extern void rtnl_cls_cache_set_tc_params(struct nl_cache *, int, uint32_t); + +extern int rtnl_cls_build_add_request(struct rtnl_cls *, int, + struct nl_msg **); +extern int rtnl_cls_add(struct nl_sock *, struct rtnl_cls *, int); +extern int rtnl_cls_change(struct nl_sock *, struct rtnl_cls *, int); + +extern int rtnl_cls_build_change_request(struct rtnl_cls *, int, + struct nl_msg **); +extern int rtnl_cls_build_delete_request(struct rtnl_cls *, int, + struct nl_msg **); +extern int rtnl_cls_delete(struct nl_sock *, struct rtnl_cls *, + int); + +extern void rtnl_cls_set_prio(struct rtnl_cls *, uint16_t); +extern uint16_t rtnl_cls_get_prio(struct rtnl_cls *); + +extern void rtnl_cls_set_protocol(struct rtnl_cls *, uint16_t); +extern uint16_t rtnl_cls_get_protocol(struct rtnl_cls *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/basic.h b/libnl/include/netlink/route/cls/basic.h new file mode 100644 index 0000000..98bbe67 --- /dev/null +++ b/libnl/include/netlink/route/cls/basic.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2010 Thomas Graf + */ + +#ifndef NETLINK_BASIC_H_ +#define NETLINK_BASIC_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_basic_set_target(struct rtnl_cls *, uint32_t); +extern uint32_t rtnl_basic_get_target(struct rtnl_cls *); +extern void rtnl_basic_set_ematch(struct rtnl_cls *, + struct rtnl_ematch_tree *); +extern struct rtnl_ematch_tree *rtnl_basic_get_ematch(struct rtnl_cls *); +extern int rtnl_basic_add_action(struct rtnl_cls *, struct rtnl_act *); +extern int rtnl_basic_del_action(struct rtnl_cls *, struct rtnl_act *); +extern struct rtnl_act* rtnl_basic_get_action(struct rtnl_cls *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/cgroup.h b/libnl/include/netlink/route/cls/cgroup.h new file mode 100644 index 0000000..8452f6c --- /dev/null +++ b/libnl/include/netlink/route/cls/cgroup.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2009-2010 Thomas Graf + */ + +#ifndef NETLINK_CLS_CGROUP_H_ +#define NETLINK_CLS_CGROUP_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_cgroup_set_ematch(struct rtnl_cls *, + struct rtnl_ematch_tree *); +struct rtnl_ematch_tree * rtnl_cgroup_get_ematch(struct rtnl_cls *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/ematch.h b/libnl/include/netlink/route/cls/ematch.h new file mode 100644 index 0000000..4362423 --- /dev/null +++ b/libnl/include/netlink/route/cls/ematch.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2010 Thomas Graf + */ + +#ifndef NETLINK_CLS_EMATCH_H_ +#define NETLINK_CLS_EMATCH_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* FIXME: Should be moved to the kernel header at some point */ +#define RTNL_EMATCH_PROGID 2 + +struct rtnl_ematch; +struct rtnl_ematch_tree; + +/** + * Extended Match Operations + */ +struct rtnl_ematch_ops +{ + int eo_kind; + const char * eo_name; + size_t eo_minlen; + size_t eo_datalen; + + int (*eo_parse)(struct rtnl_ematch *, void *, size_t); + void (*eo_dump)(struct rtnl_ematch *, + struct nl_dump_params *); + int (*eo_fill)(struct rtnl_ematch *, struct nl_msg *); + void (*eo_free)(struct rtnl_ematch *); + struct nl_list_head eo_list; +}; + +extern int rtnl_ematch_register(struct rtnl_ematch_ops *); +extern struct rtnl_ematch_ops * rtnl_ematch_lookup_ops(int); +extern struct rtnl_ematch_ops * rtnl_ematch_lookup_ops_by_name(const char *); + +extern struct rtnl_ematch * rtnl_ematch_alloc(void); +extern int rtnl_ematch_add_child(struct rtnl_ematch *, + struct rtnl_ematch *); +extern void rtnl_ematch_unlink(struct rtnl_ematch *); +extern void rtnl_ematch_free(struct rtnl_ematch *); + +extern void * rtnl_ematch_data(struct rtnl_ematch *); +extern void rtnl_ematch_set_flags(struct rtnl_ematch *, + uint16_t); +extern void rtnl_ematch_unset_flags(struct rtnl_ematch *, + uint16_t); +extern uint16_t rtnl_ematch_get_flags(struct rtnl_ematch *); +extern int rtnl_ematch_set_ops(struct rtnl_ematch *, + struct rtnl_ematch_ops *); +extern int rtnl_ematch_set_kind(struct rtnl_ematch *, + uint16_t); +extern int rtnl_ematch_set_name(struct rtnl_ematch *, + const char *); + +extern struct rtnl_ematch_tree *rtnl_ematch_tree_alloc(uint16_t); +extern void rtnl_ematch_tree_free(struct rtnl_ematch_tree *); +extern void rtnl_ematch_tree_add(struct rtnl_ematch_tree *, + struct rtnl_ematch *); + +extern struct rtnl_ematch_tree *rtnl_ematch_tree_clone(struct rtnl_ematch_tree *); + +extern int rtnl_ematch_parse_attr(struct nlattr *, + struct rtnl_ematch_tree **); +extern int rtnl_ematch_fill_attr(struct nl_msg *, int, + struct rtnl_ematch_tree *); +extern void rtnl_ematch_tree_dump(struct rtnl_ematch_tree *, + struct nl_dump_params *); + + +extern int rtnl_ematch_parse_expr(const char *, char **, + struct rtnl_ematch_tree **); + +extern char * rtnl_ematch_offset2txt(uint8_t, uint16_t, + char *, size_t); +extern char * rtnl_ematch_opnd2txt(uint8_t, char *, size_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/ematch/cmp.h b/libnl/include/netlink/route/cls/ematch/cmp.h new file mode 100644 index 0000000..8aacb51 --- /dev/null +++ b/libnl/include/netlink/route/cls/ematch/cmp.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2010 Thomas Graf + */ + +#ifndef NETLINK_CLS_EMATCH_CMP_H_ +#define NETLINK_CLS_EMATCH_CMP_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct tcf_em_cmp; + +extern void rtnl_ematch_cmp_set(struct rtnl_ematch *, + struct tcf_em_cmp *); +extern struct tcf_em_cmp * + rtnl_ematch_cmp_get(struct rtnl_ematch *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/ematch/meta.h b/libnl/include/netlink/route/cls/ematch/meta.h new file mode 100644 index 0000000..b197246 --- /dev/null +++ b/libnl/include/netlink/route/cls/ematch/meta.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#ifndef NETLINK_CLS_EMATCH_META_H_ +#define NETLINK_CLS_EMATCH_META_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_meta_value; + +extern struct rtnl_meta_value * rtnl_meta_value_alloc_int(uint64_t); +extern struct rtnl_meta_value * rtnl_meta_value_alloc_var(void *, size_t); +extern struct rtnl_meta_value * rtnl_meta_value_alloc_id(uint8_t, uint16_t, + uint8_t, uint64_t); +extern void rtnl_meta_value_put(struct rtnl_meta_value *); + +extern void rtnl_ematch_meta_set_lvalue(struct rtnl_ematch *, + struct rtnl_meta_value *); +void rtnl_ematch_meta_set_rvalue(struct rtnl_ematch *, + struct rtnl_meta_value *); +extern void rtnl_ematch_meta_set_operand(struct rtnl_ematch *, uint8_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/ematch/nbyte.h b/libnl/include/netlink/route/cls/ematch/nbyte.h new file mode 100644 index 0000000..6bcf7f2 --- /dev/null +++ b/libnl/include/netlink/route/cls/ematch/nbyte.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#ifndef NETLINK_CLS_EMATCH_NBYTE_H_ +#define NETLINK_CLS_EMATCH_NBYTE_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_ematch_nbyte_set_offset(struct rtnl_ematch *, + uint8_t, uint16_t); +extern uint16_t rtnl_ematch_nbyte_get_offset(struct rtnl_ematch *); +extern uint8_t rtnl_ematch_nbyte_get_layer(struct rtnl_ematch *); +extern void rtnl_ematch_nbyte_set_pattern(struct rtnl_ematch *, + uint8_t *, size_t); +extern uint8_t * rtnl_ematch_nbyte_get_pattern(struct rtnl_ematch *); +extern size_t rtnl_ematch_nbyte_get_len(struct rtnl_ematch *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/ematch/text.h b/libnl/include/netlink/route/cls/ematch/text.h new file mode 100644 index 0000000..1493b09 --- /dev/null +++ b/libnl/include/netlink/route/cls/ematch/text.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#ifndef NETLINK_CLS_EMATCH_TEXT_H_ +#define NETLINK_CLS_EMATCH_TEXT_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_ematch_text_set_from(struct rtnl_ematch *, + uint8_t, uint16_t); +extern uint16_t rtnl_ematch_text_get_from_offset(struct rtnl_ematch *); +extern uint8_t rtnl_ematch_text_get_from_layer(struct rtnl_ematch *); +extern void rtnl_ematch_text_set_to(struct rtnl_ematch *, + uint8_t, uint16_t); +extern uint16_t rtnl_ematch_text_get_to_offset(struct rtnl_ematch *); +extern uint8_t rtnl_ematch_text_get_to_layer(struct rtnl_ematch *); +extern void rtnl_ematch_text_set_pattern(struct rtnl_ematch *, + char *, size_t); +extern char * rtnl_ematch_text_get_pattern(struct rtnl_ematch *); +extern size_t rtnl_ematch_text_get_len(struct rtnl_ematch *); +extern void rtnl_ematch_text_set_algo(struct rtnl_ematch *, const char *); +extern char * rtnl_ematch_text_get_algo(struct rtnl_ematch *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/flower.h b/libnl/include/netlink/route/cls/flower.h new file mode 100644 index 0000000..1a772a8 --- /dev/null +++ b/libnl/include/netlink/route/cls/flower.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2018 Volodymyr Bendiuga + */ + +#ifndef NETLINK_FLOWER_H_ +#define NETLINK_FLOWER_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_flower_set_proto(struct rtnl_cls *cls, uint16_t); +extern int rtnl_flower_get_proto(struct rtnl_cls *cls, uint16_t *); + +extern int rtnl_flower_set_vlan_id(struct rtnl_cls *, uint16_t); +extern int rtnl_flower_get_vlan_id(struct rtnl_cls *, uint16_t *); + +extern int rtnl_flower_set_vlan_prio(struct rtnl_cls *, uint8_t); +extern int rtnl_flower_get_vlan_prio(struct rtnl_cls *, uint8_t *); + +extern int rtnl_flower_set_vlan_ethtype(struct rtnl_cls *, uint16_t); + +extern int rtnl_flower_set_dst_mac(struct rtnl_cls *, unsigned char *, + unsigned char *); +extern int rtnl_flower_get_dst_mac(struct rtnl_cls *, unsigned char *, + unsigned char *); + +extern int rtnl_flower_set_src_mac(struct rtnl_cls *, unsigned char *, + unsigned char *); +extern int rtnl_flower_get_src_mac(struct rtnl_cls *, unsigned char *, + unsigned char *); + +extern int rtnl_flower_set_ip_dscp(struct rtnl_cls *, uint8_t, uint8_t); +extern int rtnl_flower_get_ip_dscp(struct rtnl_cls *, uint8_t *, uint8_t *); + +extern int rtnl_flower_set_ipv4_src(struct rtnl_cls *, in_addr_t, in_addr_t); +extern int rtnl_flower_get_ipv4_src(struct rtnl_cls *, in_addr_t *, + in_addr_t *); +extern int rtnl_flower_set_ipv4_dst(struct rtnl_cls *, in_addr_t, in_addr_t); +extern int rtnl_flower_get_ipv4_dst(struct rtnl_cls *, in_addr_t *, + in_addr_t *); + +extern int rtnl_flower_set_flags(struct rtnl_cls *, int); + +extern int rtnl_flower_append_action(struct rtnl_cls *, struct rtnl_act *); +extern int rtnl_flower_del_action(struct rtnl_cls *, struct rtnl_act *); +extern struct rtnl_act* rtnl_flower_get_action(struct rtnl_cls *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/fw.h b/libnl/include/netlink/route/cls/fw.h new file mode 100644 index 0000000..57d7c3f --- /dev/null +++ b/libnl/include/netlink/route/cls/fw.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2006 Petr Gotthard + * Copyright (c) 2006 Siemens AG Oesterreich + */ + +#ifndef NETLINK_FW_H_ +#define NETLINK_FW_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_fw_set_classid(struct rtnl_cls *, uint32_t); +extern int rtnl_fw_set_mask(struct rtnl_cls *, uint32_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/matchall.h b/libnl/include/netlink/route/cls/matchall.h new file mode 100644 index 0000000..bfe1e7c --- /dev/null +++ b/libnl/include/netlink/route/cls/matchall.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2017 Volodymyr Bendiuga + */ + +#ifndef NETLINK_MATCHALL_H_ +#define NETLINK_MATCHALL_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_mall_set_classid(struct rtnl_cls *, uint32_t); +extern int rtnl_mall_get_classid(struct rtnl_cls *, uint32_t *); +extern int rtnl_mall_set_flags(struct rtnl_cls *, uint32_t); +extern int rtnl_mall_get_flags(struct rtnl_cls *, uint32_t *); +extern int rtnl_mall_append_action(struct rtnl_cls *, struct rtnl_act *); +extern struct rtnl_act *rtnl_mall_get_first_action(struct rtnl_cls *); +extern int rtnl_mall_del_action(struct rtnl_cls *, struct rtnl_act *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/police.h b/libnl/include/netlink/route/cls/police.h new file mode 100644 index 0000000..9c18b87 --- /dev/null +++ b/libnl/include/netlink/route/cls/police.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_CLS_POLICE_H_ +#define NETLINK_CLS_POLICE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern char * nl_police2str(int, char *, size_t); +extern int nl_str2police(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/u32.h b/libnl/include/netlink/route/cls/u32.h new file mode 100644 index 0000000..b8f0223 --- /dev/null +++ b/libnl/include/netlink/route/cls/u32.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_U32_H_ +#define NETLINK_U32_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_u32_set_handle(struct rtnl_cls *, int, int, int); +extern int rtnl_u32_set_classid(struct rtnl_cls *, uint32_t); +extern int rtnl_u32_get_classid(struct rtnl_cls *, uint32_t *); +extern int rtnl_u32_set_divisor(struct rtnl_cls *, uint32_t); +extern int rtnl_u32_set_link(struct rtnl_cls *, uint32_t); +extern int rtnl_u32_set_hashtable(struct rtnl_cls *, uint32_t); +extern int rtnl_u32_set_hashmask(struct rtnl_cls *, uint32_t, uint32_t); +extern int rtnl_u32_set_selector(struct rtnl_cls *, int, uint32_t, char, uint16_t, char); +extern int rtnl_u32_set_cls_terminal(struct rtnl_cls *); + +extern int rtnl_u32_set_flags(struct rtnl_cls *, int); +extern int rtnl_u32_add_mark(struct rtnl_cls *, uint32_t, uint32_t); +extern int rtnl_u32_del_mark(struct rtnl_cls *); +extern int rtnl_u32_add_key(struct rtnl_cls *, uint32_t, uint32_t, + int, int); +extern int rtnl_u32_get_key(struct rtnl_cls *, uint8_t, uint32_t *, uint32_t *, + int *, int *); +extern int rtnl_u32_add_key_uint8(struct rtnl_cls *, uint8_t, uint8_t, + int, int); +extern int rtnl_u32_add_key_uint16(struct rtnl_cls *, uint16_t, uint16_t, + int, int); +extern int rtnl_u32_add_key_uint32(struct rtnl_cls *, uint32_t, uint32_t, + int, int); +extern int rtnl_u32_add_key_in_addr(struct rtnl_cls *, const struct in_addr *, + uint8_t, int, int); +extern int rtnl_u32_add_key_in6_addr(struct rtnl_cls *, const struct in6_addr *, + uint8_t, int, int); +extern int rtnl_u32_add_action(struct rtnl_cls *, struct rtnl_act *); +extern int rtnl_u32_del_action(struct rtnl_cls *, struct rtnl_act *); +extern struct rtnl_act* rtnl_u32_get_action(struct rtnl_cls *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link.h b/libnl/include/netlink/route/link.h new file mode 100644 index 0000000..3717bba --- /dev/null +++ b/libnl/include/netlink/route/link.h @@ -0,0 +1,274 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef NETLINK_LINK_H_ +#define NETLINK_LINK_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @struct rtnl_link link.h "netlink/route/link.h" + * @brief Link object + * @implements nl_object + * @ingroup link + * + * @copydoc private_struct + */ +struct rtnl_link; + +/** + * @ingroup link + */ +typedef enum { + RTNL_LINK_RX_PACKETS, /*!< Packets received */ + RTNL_LINK_TX_PACKETS, /*!< Packets sent */ + RTNL_LINK_RX_BYTES, /*!< Bytes received */ + RTNL_LINK_TX_BYTES, /*!< Bytes sent */ + RTNL_LINK_RX_ERRORS, /*!< Receive errors */ + RTNL_LINK_TX_ERRORS, /*!< Send errors */ + RTNL_LINK_RX_DROPPED, /*!< Received packets dropped */ + RTNL_LINK_TX_DROPPED, /*!< Packets dropped during transmit */ + RTNL_LINK_RX_COMPRESSED, /*!< Compressed packets received */ + RTNL_LINK_TX_COMPRESSED, /*!< Compressed packets sent */ + RTNL_LINK_RX_FIFO_ERR, /*!< Receive FIFO errors */ + RTNL_LINK_TX_FIFO_ERR, /*!< Send FIFO errors */ + RTNL_LINK_RX_LEN_ERR, /*!< Length errors */ + RTNL_LINK_RX_OVER_ERR, /*!< Over errors */ + RTNL_LINK_RX_CRC_ERR, /*!< CRC errors */ + RTNL_LINK_RX_FRAME_ERR, /*!< Frame errors */ + RTNL_LINK_RX_MISSED_ERR, /*!< Missed errors */ + RTNL_LINK_TX_ABORT_ERR, /*!< Aborted errors */ + RTNL_LINK_TX_CARRIER_ERR, /*!< Carrier errors */ + RTNL_LINK_TX_HBEAT_ERR, /*!< Heartbeat errors */ + RTNL_LINK_TX_WIN_ERR, /*!< Window errors */ + RTNL_LINK_COLLISIONS, /*!< Send collisions */ + RTNL_LINK_MULTICAST, /*!< Multicast */ + RTNL_LINK_IP6_INPKTS, /*!< IPv6 SNMP InReceives */ + RTNL_LINK_IP6_INHDRERRORS, /*!< IPv6 SNMP InHdrErrors */ + RTNL_LINK_IP6_INTOOBIGERRORS, /*!< IPv6 SNMP InTooBigErrors */ + RTNL_LINK_IP6_INNOROUTES, /*!< IPv6 SNMP InNoRoutes */ + RTNL_LINK_IP6_INADDRERRORS, /*!< IPv6 SNMP InAddrErrors */ + RTNL_LINK_IP6_INUNKNOWNPROTOS, /*!< IPv6 SNMP InUnknownProtos */ + RTNL_LINK_IP6_INTRUNCATEDPKTS, /*!< IPv6 SNMP InTruncatedPkts */ + RTNL_LINK_IP6_INDISCARDS, /*!< IPv6 SNMP InDiscards */ + RTNL_LINK_IP6_INDELIVERS, /*!< IPv6 SNMP InDelivers */ + RTNL_LINK_IP6_OUTFORWDATAGRAMS, /*!< IPv6 SNMP OutForwDatagrams */ + RTNL_LINK_IP6_OUTPKTS, /*!< IPv6 SNMP OutRequests */ + RTNL_LINK_IP6_OUTDISCARDS, /*!< IPv6 SNMP OutDiscards */ + RTNL_LINK_IP6_OUTNOROUTES, /*!< IPv6 SNMP OutNoRoutes */ + RTNL_LINK_IP6_REASMTIMEOUT, /*!< IPv6 SNMP ReasmTimeout */ + RTNL_LINK_IP6_REASMREQDS, /*!< IPv6 SNMP ReasmReqds */ + RTNL_LINK_IP6_REASMOKS, /*!< IPv6 SNMP ReasmOKs */ + RTNL_LINK_IP6_REASMFAILS, /*!< IPv6 SNMP ReasmFails */ + RTNL_LINK_IP6_FRAGOKS, /*!< IPv6 SNMP FragOKs */ + RTNL_LINK_IP6_FRAGFAILS, /*!< IPv6 SNMP FragFails */ + RTNL_LINK_IP6_FRAGCREATES, /*!< IPv6 SNMP FragCreates */ + RTNL_LINK_IP6_INMCASTPKTS, /*!< IPv6 SNMP InMcastPkts */ + RTNL_LINK_IP6_OUTMCASTPKTS, /*!< IPv6 SNMP OutMcastPkts */ + RTNL_LINK_IP6_INBCASTPKTS, /*!< IPv6 SNMP InBcastPkts */ + RTNL_LINK_IP6_OUTBCASTPKTS, /*!< IPv6 SNMP OutBcastPkts */ + RTNL_LINK_IP6_INOCTETS, /*!< IPv6 SNMP InOctets */ + RTNL_LINK_IP6_OUTOCTETS, /*!< IPv6 SNMP OutOctets */ + RTNL_LINK_IP6_INMCASTOCTETS, /*!< IPv6 SNMP InMcastOctets */ + RTNL_LINK_IP6_OUTMCASTOCTETS, /*!< IPv6 SNMP OutMcastOctets */ + RTNL_LINK_IP6_INBCASTOCTETS, /*!< IPv6 SNMP InBcastOctets */ + RTNL_LINK_IP6_OUTBCASTOCTETS, /*!< IPv6 SNMP OutBcastOctets */ + RTNL_LINK_ICMP6_INMSGS, /*!< ICMPv6 SNMP InMsgs */ + RTNL_LINK_ICMP6_INERRORS, /*!< ICMPv6 SNMP InErrors */ + RTNL_LINK_ICMP6_OUTMSGS, /*!< ICMPv6 SNMP OutMsgs */ + RTNL_LINK_ICMP6_OUTERRORS, /*!< ICMPv6 SNMP OutErrors */ + RTNL_LINK_ICMP6_CSUMERRORS, /*!< ICMPv6 SNMP InCsumErrors */ + RTNL_LINK_IP6_CSUMERRORS, /*!< IPv6 SNMP InCsumErrors */ + RTNL_LINK_IP6_NOECTPKTS, /*!< IPv6 SNMP InNoECTPkts */ + RTNL_LINK_IP6_ECT1PKTS, /*!< IPv6 SNMP InECT1Pkts */ + RTNL_LINK_IP6_ECT0PKTS, /*!< IPv6 SNMP InECT0Pkts */ + RTNL_LINK_IP6_CEPKTS, /*!< IPv6 SNMP InCEPkts */ + RTNL_LINK_RX_NOHANDLER, /*!< Received packets dropped on inactive device */ + RTNL_LINK_REASM_OVERLAPS, /*!< SNMP ReasmOverlaps */ + __RTNL_LINK_STATS_MAX, +} rtnl_link_stat_id_t; + +#define RTNL_LINK_STATS_MAX (__RTNL_LINK_STATS_MAX - 1) + +extern struct nla_policy rtln_link_policy[]; + +extern struct rtnl_link *rtnl_link_alloc(void); +extern void rtnl_link_put(struct rtnl_link *); + +extern int rtnl_link_alloc_cache(struct nl_sock *, int, struct nl_cache **); +extern int rtnl_link_alloc_cache_flags(struct nl_sock *, int, + struct nl_cache **, + unsigned int flags); +extern struct rtnl_link *rtnl_link_get(struct nl_cache *, int); +extern struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *, const char *); + + +extern int rtnl_link_build_add_request(struct rtnl_link *, int, + struct nl_msg **); +extern int rtnl_link_add(struct nl_sock *, struct rtnl_link *, int); +extern int rtnl_link_build_change_request(struct rtnl_link *, + struct rtnl_link *, int, + struct nl_msg **); +extern int rtnl_link_change(struct nl_sock *, struct rtnl_link *, + struct rtnl_link *, int); + +extern int rtnl_link_build_delete_request(const struct rtnl_link *, + struct nl_msg **); +extern int rtnl_link_delete(struct nl_sock *, const struct rtnl_link *); +extern int rtnl_link_build_get_request(int, const char *, + struct nl_msg **); +extern int rtnl_link_get_kernel(struct nl_sock *, int, const char *, + struct rtnl_link **); + +/* Name <-> Index Translations */ +extern char * rtnl_link_i2name(struct nl_cache *, int, char *, size_t); +extern int rtnl_link_name2i(struct nl_cache *, const char *); + +/* Name <-> Statistic Translations */ +extern char * rtnl_link_stat2str(int, char *, size_t); +extern int rtnl_link_str2stat(const char *); + +/* Link Flags Translations */ +extern char * rtnl_link_flags2str(int, char *, size_t); +extern int rtnl_link_str2flags(const char *); + +extern char * rtnl_link_operstate2str(uint8_t, char *, size_t); +extern int rtnl_link_str2operstate(const char *); + +extern char * rtnl_link_mode2str(uint8_t, char *, size_t); +extern int rtnl_link_str2mode(const char *); + +/* Carrier State Translations */ +extern char * rtnl_link_carrier2str(uint8_t, char *, size_t); +extern int rtnl_link_str2carrier(const char *); + +/* Access Functions */ +extern void rtnl_link_set_qdisc(struct rtnl_link *, const char *); +extern char * rtnl_link_get_qdisc(struct rtnl_link *); + +extern void rtnl_link_set_name(struct rtnl_link *, const char *); +extern char * rtnl_link_get_name(struct rtnl_link *); + +extern void rtnl_link_set_group(struct rtnl_link *, uint32_t); +extern uint32_t rtnl_link_get_group(struct rtnl_link *); + +extern void rtnl_link_set_flags(struct rtnl_link *, unsigned int); +extern void rtnl_link_unset_flags(struct rtnl_link *, unsigned int); +extern unsigned int rtnl_link_get_flags(struct rtnl_link *); + +extern void rtnl_link_set_mtu(struct rtnl_link *, unsigned int); +extern unsigned int rtnl_link_get_mtu(struct rtnl_link *); + +extern void rtnl_link_set_txqlen(struct rtnl_link *, unsigned int); +extern unsigned int rtnl_link_get_txqlen(struct rtnl_link *); + +extern void rtnl_link_set_ifindex(struct rtnl_link *, int); +extern int rtnl_link_get_ifindex(struct rtnl_link *); + +extern void rtnl_link_set_family(struct rtnl_link *, int); +extern int rtnl_link_get_family(struct rtnl_link *); + +extern void rtnl_link_set_arptype(struct rtnl_link *, unsigned int); +extern unsigned int rtnl_link_get_arptype(struct rtnl_link *); + +extern void rtnl_link_set_addr(struct rtnl_link *, struct nl_addr *); +extern struct nl_addr *rtnl_link_get_addr(struct rtnl_link *); + +extern struct nl_addr *rtnl_link_get_perm_addr(struct rtnl_link *); + +extern void rtnl_link_set_broadcast(struct rtnl_link *, struct nl_addr *); +extern struct nl_addr *rtnl_link_get_broadcast(struct rtnl_link *); + +extern void rtnl_link_set_link(struct rtnl_link *, int); +extern int rtnl_link_get_link(struct rtnl_link *); + +extern void rtnl_link_set_master(struct rtnl_link *, int); +extern int rtnl_link_get_master(struct rtnl_link *); + +extern void rtnl_link_set_carrier(struct rtnl_link *, uint8_t); +extern uint8_t rtnl_link_get_carrier(struct rtnl_link *); + +extern int rtnl_link_get_carrier_changes(struct rtnl_link *, uint32_t *); + +extern void rtnl_link_set_operstate(struct rtnl_link *, uint8_t); +extern uint8_t rtnl_link_get_operstate(struct rtnl_link *); + +extern void rtnl_link_set_linkmode(struct rtnl_link *, uint8_t); +extern uint8_t rtnl_link_get_linkmode(struct rtnl_link *); + +int rtnl_link_set_link_netnsid(struct rtnl_link *link, int32_t link_netnsid); +int rtnl_link_get_link_netnsid(const struct rtnl_link *link, int32_t *out_link_netnsid); + +extern const char * rtnl_link_get_ifalias(struct rtnl_link *); +extern void rtnl_link_set_ifalias(struct rtnl_link *, const char *); + +extern int rtnl_link_get_num_vf(struct rtnl_link *, uint32_t *); + +extern uint64_t rtnl_link_get_stat(struct rtnl_link *, rtnl_link_stat_id_t); +extern int rtnl_link_set_stat(struct rtnl_link *, rtnl_link_stat_id_t, + const uint64_t); + +extern int rtnl_link_set_type(struct rtnl_link *, const char *); +extern char * rtnl_link_get_type(struct rtnl_link *); + +extern int rtnl_link_set_slave_type(struct rtnl_link *, const char *); +extern const char * rtnl_link_get_slave_type(const struct rtnl_link *); + +extern void rtnl_link_set_promiscuity(struct rtnl_link *, uint32_t); +extern uint32_t rtnl_link_get_promiscuity(struct rtnl_link *); + +extern void rtnl_link_set_num_tx_queues(struct rtnl_link *, uint32_t); +extern uint32_t rtnl_link_get_num_tx_queues(struct rtnl_link *); + +extern void rtnl_link_set_num_rx_queues(struct rtnl_link *, uint32_t); +extern uint32_t rtnl_link_get_num_rx_queues(struct rtnl_link *); + +extern int rtnl_link_get_gso_max_segs(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_get_gso_max_size(struct rtnl_link *, uint32_t *); + +extern struct nl_data * rtnl_link_get_phys_port_id(struct rtnl_link *); + +extern char* rtnl_link_get_phys_port_name(struct rtnl_link *); + +extern struct nl_data * rtnl_link_get_phys_switch_id(struct rtnl_link *); + +extern void rtnl_link_set_ns_fd(struct rtnl_link *, int); +extern int rtnl_link_get_ns_fd(struct rtnl_link *); +extern void rtnl_link_set_ns_pid(struct rtnl_link *, pid_t); +extern pid_t rtnl_link_get_ns_pid(struct rtnl_link *); + +extern int rtnl_link_enslave_ifindex(struct nl_sock *, int, int); +extern int rtnl_link_enslave(struct nl_sock *, struct rtnl_link *, + struct rtnl_link *); +extern int rtnl_link_release_ifindex(struct nl_sock *, int); +extern int rtnl_link_release(struct nl_sock *, struct rtnl_link *); +extern int rtnl_link_fill_info(struct nl_msg *, struct rtnl_link *); +extern int rtnl_link_info_parse(struct rtnl_link *, struct nlattr **); + +extern int rtnl_link_has_vf_list(struct rtnl_link *); +extern void rtnl_link_set_vf_list(struct rtnl_link *); +extern void rtnl_link_unset_vf_list(struct rtnl_link *); + + +/* deprecated */ +extern int rtnl_link_set_info_type(struct rtnl_link *, const char *) __attribute__((deprecated)); +extern char * rtnl_link_get_info_type(struct rtnl_link *) __attribute__((deprecated)); +extern void rtnl_link_set_weight(struct rtnl_link *, unsigned int) __attribute__((deprecated)); +extern unsigned int rtnl_link_get_weight(struct rtnl_link *) __attribute__((deprecated)); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/api.h b/libnl/include/netlink/route/link/api.h new file mode 100644 index 0000000..ef11d2f --- /dev/null +++ b/libnl/include/netlink/route/link/api.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Thomas Graf + */ + +#ifndef NETLINK_DUMMY_LINK_API_H_ +#define NETLINK_DUMMY_LINK_API_H_ + +#include +#include + +#ifndef _NL_NO_WARN_DEPRECATED_HEADER +#warning "You are including a deprecated header file, include ." +#endif + +#endif diff --git a/libnl/include/netlink/route/link/bonding.h b/libnl/include/netlink/route/link/bonding.h new file mode 100644 index 0000000..a8dce73 --- /dev/null +++ b/libnl/include/netlink/route/link/bonding.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2011-2013 Thomas Graf + */ + +#ifndef NETLINK_LINK_BONDING_H_ +#define NETLINK_LINK_BONDING_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_link *rtnl_link_bond_alloc(void); + +extern int rtnl_link_bond_add(struct nl_sock *, const char *, + struct rtnl_link *); + +extern int rtnl_link_bond_enslave_ifindex(struct nl_sock *, int, int); +extern int rtnl_link_bond_enslave(struct nl_sock *, struct rtnl_link *, + struct rtnl_link *); + +extern int rtnl_link_bond_release_ifindex(struct nl_sock *, int); +extern int rtnl_link_bond_release(struct nl_sock *, struct rtnl_link *); + +extern void rtnl_link_bond_set_mode(struct rtnl_link *link, uint8_t mode); +extern int rtnl_link_bond_get_mode(struct rtnl_link *link, uint8_t *mode); + +extern void rtnl_link_bond_set_activeslave(struct rtnl_link *link, int active_slave); +extern int rtnl_link_bond_get_activeslave(struct rtnl_link *link, int *active_slave); + +extern void rtnl_link_bond_set_hashing_type(struct rtnl_link *link, uint8_t type); +extern int rtnl_link_bond_get_hashing_type(struct rtnl_link *link, uint8_t *type); + +extern void rtnl_link_bond_set_miimon(struct rtnl_link *link, uint32_t miimon); +extern int rtnl_link_bond_get_miimon(struct rtnl_link *link, uint32_t *miimon); + +extern void rtnl_link_bond_set_min_links(struct rtnl_link *link, uint32_t min_links); +extern int rtnl_link_bond_get_min_links(struct rtnl_link *link, uint32_t *min_links); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libnl/include/netlink/route/link/bridge.h b/libnl/include/netlink/route/link/bridge.h new file mode 100644 index 0000000..f51bdca --- /dev/null +++ b/libnl/include/netlink/route/link/bridge.h @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Thomas Graf + */ + +#ifndef NETLINK_LINK_BRIDGE_H_ +#define NETLINK_LINK_BRIDGE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define RTNL_LINK_BRIDGE_VLAN_BITMAP_MAX 4096 +#define RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN (RTNL_LINK_BRIDGE_VLAN_BITMAP_MAX / 32) + +struct rtnl_link_bridge_vlan +{ + uint16_t pvid; + uint32_t vlan_bitmap[RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN]; + uint32_t untagged_bitmap[RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN]; +}; + +/** + * Bridge flags + * @ingroup bridge + */ +enum rtnl_link_bridge_flags { + RTNL_BRIDGE_HAIRPIN_MODE = 1ul << 0, + RTNL_BRIDGE_BPDU_GUARD = 1ul << 1, + RTNL_BRIDGE_ROOT_BLOCK = 1ul << 2, + RTNL_BRIDGE_FAST_LEAVE = 1ul << 3, + RTNL_BRIDGE_UNICAST_FLOOD = 1ul << 4, + RTNL_BRIDGE_LEARNING = 1ul << 5, + RTNL_BRIDGE_LEARNING_SYNC = 1ul << 6, + RTNL_BRIDGE_PROXYARP = 1ul << 7, + RTNL_BRIDGE_PROXYARP_WIFI = 1ul << 8, + RTNL_BRIDGE_MCAST_FLOOD = 1ul << 9, + RTNL_BRIDGE_MCAST_TO_UCAST = 1ul << 10, + RTNL_BRIDGE_VLAN_TUNNEL = 1ul << 11, + RTNL_BRIDGE_BCAST_FLOOD = 1ul << 12, + RTNL_BRIDGE_NEIGH_SUPPRESS = 1ul << 13, + RTNL_BRIDGE_ISOLATED = 1ul << 14, + RTNL_BRIDGE_LOCKED = 1ul << 15, + RTNL_BRIDGE_MAB = 1ul << 16, + RTNL_BRIDGE_NEIGH_VLAN_SUPPRESS = 1ul << 17, +}; + +#define RTNL_BRIDGE_HWMODE_VEB BRIDGE_MODE_VEB +#define RTNL_BRIDGE_HWMODE_VEPA BRIDGE_MODE_VEPA +#define RTNL_BRIDGE_HWMODE_MAX BRIDGE_MODE_VEPA +#define RTNL_BRIDGE_HWMODE_UNDEF BRIDGE_MODE_UNDEF + +extern struct rtnl_link *rtnl_link_bridge_alloc(void); + +extern int rtnl_link_is_bridge(struct rtnl_link *); +extern int rtnl_link_bridge_has_ext_info(struct rtnl_link *); + +extern int rtnl_link_bridge_set_port_state(struct rtnl_link *, uint8_t ); +extern int rtnl_link_bridge_get_port_state(struct rtnl_link *); + +extern int rtnl_link_bridge_set_priority(struct rtnl_link *, uint16_t); +extern int rtnl_link_bridge_get_priority(struct rtnl_link *); + +extern int rtnl_link_bridge_set_cost(struct rtnl_link *, uint32_t); +extern int rtnl_link_bridge_get_cost(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_bridge_unset_flags(struct rtnl_link *, unsigned int); +extern int rtnl_link_bridge_set_flags(struct rtnl_link *, unsigned int); +extern int rtnl_link_bridge_get_flags(struct rtnl_link *); + +extern int rtnl_link_bridge_set_self(struct rtnl_link *); +extern int rtnl_link_bridge_set_master(struct rtnl_link *); + +extern int rtnl_link_bridge_get_hwmode(struct rtnl_link *, uint16_t *); +extern int rtnl_link_bridge_set_hwmode(struct rtnl_link *, uint16_t); + +extern char * rtnl_link_bridge_flags2str(int, char *, size_t); +extern int rtnl_link_bridge_str2flags(const char *); + +extern char * rtnl_link_bridge_portstate2str(int, char *, size_t); +extern int rtnl_link_bridge_str2portstate(const char *); + +extern char * rtnl_link_bridge_hwmode2str(uint16_t, char *, size_t); +extern uint16_t rtnl_link_bridge_str2hwmode(const char *); + +extern int rtnl_link_bridge_add(struct nl_sock *sk, const char *name); + +extern int rtnl_link_bridge_enable_vlan(struct rtnl_link *link); +extern int rtnl_link_bridge_set_port_vlan_map_range (struct rtnl_link *link, uint16_t start, uint16_t end, int untagged); +extern int rtnl_link_bridge_unset_port_vlan_map_range (struct rtnl_link *link, uint16_t start, uint16_t end); +extern int rtnl_link_bridge_set_port_vlan_pvid (struct rtnl_link *link, uint16_t pvid); +extern int rtnl_link_bridge_pvid(struct rtnl_link *link); +extern int rtnl_link_bridge_has_vlan(struct rtnl_link *link); + +extern struct rtnl_link_bridge_vlan *rtnl_link_bridge_get_port_vlan(struct rtnl_link *link); + +extern int rtnl_link_bridge_set_mst_port_state(struct rtnl_link *link, uint16_t instance, uint8_t state); +extern int rtnl_link_bridge_get_mst_port_state(struct rtnl_link *link, uint16_t instance); +extern int rtnl_link_bridge_del_mst_port_state(struct rtnl_link *link, uint16_t instance); +extern int rtnl_link_bridge_clear_mst_port_state_info(struct rtnl_link *link); +extern int rtnl_link_bridge_foreach_mst_entry(struct rtnl_link *link, void (*cb)(uint16_t instance, uint8_t state, void *arg), void *arg); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libnl/include/netlink/route/link/bridge_info.h b/libnl/include/netlink/route/link/bridge_info.h new file mode 100644 index 0000000..560a7c0 --- /dev/null +++ b/libnl/include/netlink/route/link/bridge_info.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2022 MaxLinear, Inc. + */ + +#ifndef NETLINK_LINK_BRIDGE_INFO_H_ +#define NETLINK_LINK_BRIDGE_INFO_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_link_bridge_set_ageing_time(struct rtnl_link *link, + uint32_t ageing_time); +extern int rtnl_link_bridge_get_ageing_time(struct rtnl_link *link, + uint32_t *ageing_time); + +extern void rtnl_link_bridge_set_vlan_filtering(struct rtnl_link *link, + uint8_t vlan_filtering); +extern int rtnl_link_bridge_get_vlan_filtering(struct rtnl_link *link, + uint8_t *vlan_filtering); + +extern void rtnl_link_bridge_set_vlan_protocol(struct rtnl_link *link, + uint16_t vlan_protocol); +extern int rtnl_link_bridge_get_vlan_protocol(struct rtnl_link *link, + uint16_t *vlan_protocol); + +extern void rtnl_link_bridge_set_vlan_default_pvid(struct rtnl_link *link, + uint16_t default_pvid); +extern int rtnl_link_bridge_get_vlan_default_pvid(struct rtnl_link *link, + uint16_t *default_pvid); + +extern void rtnl_link_bridge_set_vlan_stats_enabled(struct rtnl_link *link, + uint8_t vlan_stats_enabled); +extern int rtnl_link_bridge_get_vlan_stats_enabled(struct rtnl_link *link, + uint8_t *vlan_stats_enabled); + +extern void rtnl_link_bridge_set_nf_call_iptables(struct rtnl_link *link, + uint8_t call_enabled); +extern int rtnl_link_bridge_get_nf_call_iptables(struct rtnl_link *link, + uint8_t *call_enabled); + +extern void rtnl_link_bridge_set_nf_call_ip6tables(struct rtnl_link *link, + uint8_t call_enabled); +extern int rtnl_link_bridge_get_nf_call_ip6tables(struct rtnl_link *link, + uint8_t *call_enabled); + +extern void rtnl_link_bridge_set_nf_call_arptables(struct rtnl_link *link, + uint8_t call_enabled); +extern int rtnl_link_bridge_get_nf_call_arptables(struct rtnl_link *link, + uint8_t *call_enabled); + +extern void rtnl_link_bridge_set_stp_state(struct rtnl_link *link, + uint32_t stp_state); +extern int rtnl_link_bridge_get_stp_state(struct rtnl_link *link, + uint32_t *stp_state); + +extern void rtnl_link_bridge_set_mcast_router(struct rtnl_link *link, + uint8_t type); +extern int rtnl_link_bridge_get_mcast_router(struct rtnl_link *link, + uint8_t *type); + +extern void rtnl_link_bridge_set_mcast_snooping(struct rtnl_link *link, + uint8_t value); +extern int rtnl_link_bridge_get_mcast_snooping(struct rtnl_link *link, + uint8_t *value); + +extern int rtnl_link_bridge_set_boolopt(struct rtnl_link *link, int opt, + int value); + +extern int rtnl_link_bridge_get_boolopt(struct rtnl_link *link, int opt); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/can.h b/libnl/include/netlink/route/link/can.h new file mode 100644 index 0000000..1c23660 --- /dev/null +++ b/libnl/include/netlink/route/link/can.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2012 Benedikt Spranger + */ + +#ifndef NETLINK_LINK_CAN_H_ +#define NETLINK_LINK_CAN_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct can_bittiming_const; +struct can_bittiming; +struct can_berr_counter; + +extern int rtnl_link_is_can(struct rtnl_link *link); + +extern char *rtnl_link_can_ctrlmode2str(int, char *, size_t); +extern int rtnl_link_can_str2ctrlmode(const char *); + +extern int rtnl_link_can_restart(struct rtnl_link *); +extern int rtnl_link_can_freq(struct rtnl_link *, uint32_t *); +extern int rtnl_link_can_state(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_can_berr_rx(struct rtnl_link *); +extern int rtnl_link_can_berr_tx(struct rtnl_link *); +extern int rtnl_link_can_berr(struct rtnl_link *, struct can_berr_counter *); + +extern int rtnl_link_can_get_bt_const(struct rtnl_link *, + struct can_bittiming_const *); +extern int rtnl_link_can_get_bittiming(struct rtnl_link *, + struct can_bittiming *); +extern int rtnl_link_can_set_bittiming(struct rtnl_link *, + const struct can_bittiming *); + +extern int rtnl_link_can_get_bitrate(struct rtnl_link *, uint32_t *); +extern int rtnl_link_can_set_bitrate(struct rtnl_link *, uint32_t); + +extern int rtnl_link_can_get_sample_point(struct rtnl_link *, uint32_t *); +extern int rtnl_link_can_set_sample_point(struct rtnl_link *, uint32_t); + +extern int rtnl_link_can_get_restart_ms(struct rtnl_link *, uint32_t *); +extern int rtnl_link_can_set_restart_ms(struct rtnl_link *, uint32_t); + +extern int rtnl_link_can_get_ctrlmode(struct rtnl_link *, uint32_t *); +extern int rtnl_link_can_set_ctrlmode(struct rtnl_link *, uint32_t); +extern int rtnl_link_can_unset_ctrlmode(struct rtnl_link *, uint32_t); + +extern int rtnl_link_can_get_data_bittiming_const(struct rtnl_link *, + struct can_bittiming_const *); +extern int rtnl_link_can_set_data_bittiming_const(struct rtnl_link *, + const struct can_bittiming_const *); +extern int rtnl_link_can_get_data_bittiming(struct rtnl_link *, + struct can_bittiming *); +extern int rtnl_link_can_set_data_bittiming(struct rtnl_link *, + const struct can_bittiming *); + +extern int rtnl_link_can_get_device_stats(struct rtnl_link *, + struct can_device_stats *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/geneve.h b/libnl/include/netlink/route/link/geneve.h new file mode 100644 index 0000000..cf37c4e --- /dev/null +++ b/libnl/include/netlink/route/link/geneve.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#ifndef NETLINK_LINK_GENEVE_H_ +#define NETLINK_LINK_GENEVE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define RTNL_GENEVE_ID_MAX 16777215 + +#define RTNL_LINK_GENEVE_F_COLLECT_METADATA (1<<0) + +extern struct rtnl_link *rtnl_link_geneve_alloc(void); +extern int rtnl_link_is_geneve(struct rtnl_link *); + +extern int rtnl_link_geneve_set_id(struct rtnl_link *, uint32_t); +extern int rtnl_link_geneve_get_id(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_geneve_set_remote(struct rtnl_link *, struct nl_addr *); +extern int rtnl_link_geneve_get_remote(struct rtnl_link *, struct nl_addr **); + +extern int rtnl_link_geneve_set_ttl(struct rtnl_link *, uint8_t); +extern int rtnl_link_geneve_get_ttl(struct rtnl_link *); + +extern int rtnl_link_geneve_set_tos(struct rtnl_link *, uint8_t); +extern int rtnl_link_geneve_get_tos(struct rtnl_link *); + +extern int rtnl_link_geneve_set_port(struct rtnl_link *, uint32_t); +extern int rtnl_link_geneve_get_port(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_geneve_set_label(struct rtnl_link *, uint32_t); +extern int rtnl_link_geneve_get_label(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_geneve_set_udp_csum(struct rtnl_link *, uint8_t); +extern int rtnl_link_geneve_get_udp_csum(struct rtnl_link *); + +extern int rtnl_link_geneve_set_udp_zero_csum6_tx(struct rtnl_link *, uint8_t); +extern int rtnl_link_geneve_get_udp_zero_csum6_tx(struct rtnl_link *); + +extern int rtnl_link_geneve_set_udp_zero_csum6_rx(struct rtnl_link *, uint8_t); +extern int rtnl_link_geneve_get_udp_zero_csum6_rx(struct rtnl_link *); + +extern int rtnl_link_geneve_set_flags(struct rtnl_link *, uint8_t flags, int enable); +extern int rtnl_link_geneve_get_flags(struct rtnl_link *, uint8_t *flags); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/inet.h b/libnl/include/netlink/route/link/inet.h new file mode 100644 index 0000000..b1318d1 --- /dev/null +++ b/libnl/include/netlink/route/link/inet.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#ifndef NETLINK_LINK_INET_H_ +#define NETLINK_LINK_INET_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const char * rtnl_link_inet_devconf2str(int, char *, size_t); +extern int rtnl_link_inet_str2devconf(const char *); + +extern int rtnl_link_inet_get_conf(struct rtnl_link *, + const unsigned int, uint32_t *); +extern int rtnl_link_inet_set_conf(struct rtnl_link *, + const unsigned int, uint32_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/inet6.h b/libnl/include/netlink/route/link/inet6.h new file mode 100644 index 0000000..8939427 --- /dev/null +++ b/libnl/include/netlink/route/link/inet6.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Dan Williams + */ + +#ifndef NETLINK_LINK_INET6_H_ +#define NETLINK_LINK_INET6_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +const char * rtnl_link_inet6_addrgenmode2str (uint8_t mode, + char *buf, + size_t len); + +uint8_t rtnl_link_inet6_str2addrgenmode (const char *mode); + +extern int rtnl_link_inet6_get_token(struct rtnl_link *, + struct nl_addr **); + +extern int rtnl_link_inet6_set_token(struct rtnl_link *, + struct nl_addr *); + +extern int rtnl_link_inet6_get_addr_gen_mode(struct rtnl_link *, + uint8_t *); + +extern int rtnl_link_inet6_set_addr_gen_mode(struct rtnl_link *, + uint8_t); + +extern int rtnl_link_inet6_get_flags(struct rtnl_link *, + uint32_t *); + +extern int rtnl_link_inet6_set_flags(struct rtnl_link *, + uint32_t); + +extern int rtnl_link_inet6_get_conf(struct rtnl_link *, + unsigned int, + uint32_t *); + +/* Link Flags Translations */ +extern char * rtnl_link_inet6_flags2str(int, char *, size_t); +extern int rtnl_link_inet6_str2flags(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/info-api.h b/libnl/include/netlink/route/link/info-api.h new file mode 100644 index 0000000..88461bb --- /dev/null +++ b/libnl/include/netlink/route/link/info-api.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Thomas Graf + */ + +#ifndef NETLINK_DUMMY_LINK_INFO_API_H_ +#define NETLINK_DUMMY_LINK_INFO_API_H_ + +#include +#include + +#ifndef _NL_NO_WARN_DEPRECATED_HEADER +#warning "You are including a deprecated header file, include ." +#endif + +#endif diff --git a/libnl/include/netlink/route/link/ip6gre.h b/libnl/include/netlink/route/link/ip6gre.h new file mode 100644 index 0000000..2838592 --- /dev/null +++ b/libnl/include/netlink/route/link/ip6gre.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#ifndef NETLINK_LINK_IP6GRE_H_ +#define NETLINK_LINK_IP6GRE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + extern int rtnl_link_is_ip6gre(struct rtnl_link *link); + + extern struct rtnl_link *rtnl_link_ip6gre_alloc(void); + extern int rtnl_link_ip6gre_add(struct nl_sock *sk, const char *name); + + extern int rtnl_link_ip6gre_set_link(struct rtnl_link *link, uint32_t index); + extern int rtnl_link_ip6gre_get_link(struct rtnl_link *link, uint32_t *index); + + extern int rtnl_link_ip6gre_set_iflags(struct rtnl_link *link, uint16_t iflags); + extern int rtnl_link_ip6gre_get_iflags(struct rtnl_link *link, uint16_t *iflags); + + extern int rtnl_link_ip6gre_set_oflags(struct rtnl_link *link, uint16_t oflags); + extern int rtnl_link_ip6gre_get_oflags(struct rtnl_link *link, uint16_t *oflags); + + extern int rtnl_link_ip6gre_set_ikey(struct rtnl_link *link, uint32_t ikey); + extern int rtnl_link_ip6gre_get_ikey(struct rtnl_link *link, uint32_t *ikey); + + extern int rtnl_link_ip6gre_set_okey(struct rtnl_link *link, uint32_t okey); + extern int rtnl_link_ip6gre_get_okey(struct rtnl_link *link, uint32_t *okey); + + extern int rtnl_link_ip6gre_set_local(struct rtnl_link *link, struct in6_addr *local); + extern int rtnl_link_ip6gre_get_local(struct rtnl_link *link, struct in6_addr *local); + + extern int rtnl_link_ip6gre_set_remote(struct rtnl_link *link, struct in6_addr *remote); + extern int rtnl_link_ip6gre_get_remote(struct rtnl_link *link, struct in6_addr *remote); + + extern int rtnl_link_ip6gre_set_ttl(struct rtnl_link *link, uint8_t ttl); + extern int rtnl_link_ip6gre_get_ttl(struct rtnl_link *link, uint8_t *ttl); + + extern int rtnl_link_ip6gre_set_encaplimit(struct rtnl_link *link, uint8_t encaplimit); + extern int rtnl_link_ip6gre_get_encaplimit(struct rtnl_link *link, uint8_t *encaplimit); + + extern int rtnl_link_ip6gre_set_flowinfo(struct rtnl_link *link, uint32_t flowinfo); + extern int rtnl_link_ip6gre_get_flowinfo(struct rtnl_link *link, uint32_t *flowinfo); + + extern int rtnl_link_ip6gre_set_flags(struct rtnl_link *link, uint32_t flags); + extern int rtnl_link_ip6gre_get_flags(struct rtnl_link *link, uint32_t *flags); + + extern int rtnl_link_ip6gre_set_fwmark(struct rtnl_link *link, uint32_t fwmark); + extern int rtnl_link_ip6gre_get_fwmark(struct rtnl_link *link, uint32_t *fwmark); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/ip6tnl.h b/libnl/include/netlink/route/link/ip6tnl.h new file mode 100644 index 0000000..865d973 --- /dev/null +++ b/libnl/include/netlink/route/link/ip6tnl.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +#ifndef NETLINK_LINK_IP6TNL_H_ +#define NETLINK_LINK_IP6TNL_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + extern struct rtnl_link *rtnl_link_ip6_tnl_alloc(void); + extern int rtnl_link_ip6_tnl_add(struct nl_sock *sk, const char *name); + + extern int rtnl_link_is_ip6_tnl(struct rtnl_link *link); + + extern int rtnl_link_ip6_tnl_set_link(struct rtnl_link *link, uint32_t index); + extern uint32_t rtnl_link_ip6_tnl_get_link(struct rtnl_link *link); + + extern int rtnl_link_ip6_tnl_set_local(struct rtnl_link *link, struct in6_addr *); + extern int rtnl_link_ip6_tnl_get_local(struct rtnl_link *link, struct in6_addr *addr); + + extern int rtnl_link_ip6_tnl_set_remote(struct rtnl_link *link, struct in6_addr *); + extern int rtnl_link_ip6_tnl_get_remote(struct rtnl_link *link, struct in6_addr *); + + extern int rtnl_link_ip6_tnl_set_ttl(struct rtnl_link *link, uint8_t ttl); + extern uint8_t rtnl_link_ip6_tnl_get_ttl(struct rtnl_link *link); + + extern int rtnl_link_ip6_tnl_set_tos(struct rtnl_link *link, uint8_t tos); + extern uint8_t rtnl_link_ip6_tnl_get_tos(struct rtnl_link *link); + + extern int rtnl_link_ip6_tnl_set_encaplimit(struct rtnl_link *link, uint8_t encap_limit); + extern uint8_t rtnl_link_ip6_tnl_get_encaplimit(struct rtnl_link *link); + + extern int rtnl_link_ip6_tnl_set_flags(struct rtnl_link *link, uint32_t flags); + extern uint32_t rtnl_link_ip6_tnl_get_flags(struct rtnl_link *link); + + extern uint32_t rtnl_link_ip6_tnl_get_flowinfo(struct rtnl_link *link); + extern int rtnl_link_ip6_tnl_set_flowinfo(struct rtnl_link *link, uint32_t flowinfo); + + extern int rtnl_link_ip6_tnl_set_proto(struct rtnl_link *link, uint8_t proto); + extern uint8_t rtnl_link_ip6_tnl_get_proto(struct rtnl_link *link); + + extern int rtnl_link_ip6_tnl_set_fwmark(struct rtnl_link *link, uint32_t fwmark); + extern int rtnl_link_ip6_tnl_get_fwmark(struct rtnl_link *link, uint32_t *fwmark); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/ip6vti.h b/libnl/include/netlink/route/link/ip6vti.h new file mode 100644 index 0000000..bfe33d3 --- /dev/null +++ b/libnl/include/netlink/route/link/ip6vti.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#ifndef NETLINK_LINK_IP6VTI_H_ +#define NETLINK_LINK_IP6VTI_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + int rtnl_link_is_ip6vti(struct rtnl_link *link); + + extern struct rtnl_link *rtnl_link_ip6vti_alloc(void); + extern int rtnl_link_ip6vti_add(struct nl_sock *sk, const char *name); + + extern int rtnl_link_ip6vti_set_link(struct rtnl_link *link, uint32_t index); + extern int rtnl_link_ip6vti_get_link(struct rtnl_link *link, uint32_t *index); + + extern int rtnl_link_ip6vti_set_ikey(struct rtnl_link *link, uint32_t ikey); + extern int rtnl_link_ip6vti_get_ikey(struct rtnl_link *link, uint32_t *ikey); + + extern int rtnl_link_ip6vti_set_okey(struct rtnl_link *link, uint32_t okey); + extern int rtnl_link_ip6vti_get_okey(struct rtnl_link *link, uint32_t *okey); + + extern int rtnl_link_ip6vti_set_local(struct rtnl_link *link, struct in6_addr *local); + extern int rtnl_link_ip6vti_get_local(struct rtnl_link *link, struct in6_addr *remote); + + extern int rtnl_link_ip6vti_set_remote(struct rtnl_link *link, struct in6_addr *remote); + extern int rtnl_link_ip6vti_get_remote(struct rtnl_link *link, struct in6_addr *remote); + + extern int rtnl_link_ip6vti_set_fwmark(struct rtnl_link *link, uint32_t fwmark); + extern int rtnl_link_ip6vti_get_fwmark(struct rtnl_link *link, uint32_t *fwmark); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/ipgre.h b/libnl/include/netlink/route/link/ipgre.h new file mode 100644 index 0000000..09a4957 --- /dev/null +++ b/libnl/include/netlink/route/link/ipgre.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +#ifndef NETLINK_LINK_IPGRE_H_ +#define NETLINK_LINK_IPGRE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + extern int rtnl_link_is_ipgre(struct rtnl_link *link); + extern int rtnl_link_is_ipgretap(struct rtnl_link *link); + + extern struct rtnl_link *rtnl_link_ipgre_alloc(void); + extern struct rtnl_link *rtnl_link_ipgretap_alloc(void); + extern int rtnl_link_ipgre_add(struct nl_sock *sk, const char *name); + extern int rtnl_link_ipgretap_add(struct nl_sock *sk, const char *name); + + extern int rtnl_link_ipgre_set_link(struct rtnl_link *link, uint32_t index); + extern uint32_t rtnl_link_ipgre_get_link(struct rtnl_link *link); + + extern int rtnl_link_ipgre_set_iflags(struct rtnl_link *link, uint16_t iflags); + extern uint16_t rtnl_link_ipgre_get_iflags(struct rtnl_link *link); + + extern int rtnl_link_ipgre_set_oflags(struct rtnl_link *link, uint16_t oflags); + extern uint16_t rtnl_link_ipgre_get_oflags(struct rtnl_link *link); + + extern int rtnl_link_ipgre_set_ikey(struct rtnl_link *link, uint32_t ikey); + extern uint32_t rtnl_link_ipgre_get_ikey(struct rtnl_link *link); + + extern int rtnl_link_ipgre_set_okey(struct rtnl_link *link, uint32_t okey); + extern uint32_t rtnl_link_ipgre_get_okey(struct rtnl_link *link); + + extern int rtnl_link_ipgre_set_local(struct rtnl_link *link, uint32_t addr); + extern uint32_t rtnl_link_ipgre_get_local(struct rtnl_link *link); + + extern int rtnl_link_ipgre_set_remote(struct rtnl_link *link, uint32_t addr); + extern uint32_t rtnl_link_ipgre_get_remote(struct rtnl_link *link); + + extern int rtnl_link_ipgre_set_ttl(struct rtnl_link *link, uint8_t ttl); + extern uint8_t rtnl_link_ipgre_get_ttl(struct rtnl_link *link); + + extern int rtnl_link_ipgre_set_tos(struct rtnl_link *link, uint8_t tos); + extern uint8_t rtnl_link_ipgre_get_tos(struct rtnl_link *link); + + extern int rtnl_link_ipgre_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc); + extern uint8_t rtnl_link_ipgre_get_pmtudisc(struct rtnl_link *link); + + extern int rtnl_link_ipgre_set_fwmark(struct rtnl_link *link, uint32_t fwmark); + extern int rtnl_link_ipgre_get_fwmark(struct rtnl_link *link, uint32_t *fwmark); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/ipip.h b/libnl/include/netlink/route/link/ipip.h new file mode 100644 index 0000000..65d9f18 --- /dev/null +++ b/libnl/include/netlink/route/link/ipip.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +#ifndef NETLINK_LINK_IPIP_H_ +#define NETLINK_LINK_IPIP_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + extern struct rtnl_link *rtnl_link_ipip_alloc(void); + extern int rtnl_link_ipip_add(struct nl_sock *sk, const char *name); + + extern int rtnl_link_is_ipip(struct rtnl_link *link); + + extern uint32_t rtnl_link_ipip_get_link(struct rtnl_link *link); + extern int rtnl_link_ipip_set_link(struct rtnl_link *link, uint32_t index); + + extern int rtnl_link_ipip_set_local(struct rtnl_link *link, uint32_t addr); + extern uint32_t rtnl_link_ipip_get_local(struct rtnl_link *link); + + extern int rtnl_link_ipip_set_remote(struct rtnl_link *link, uint32_t addr); + extern uint32_t rtnl_link_ipip_get_remote(struct rtnl_link *link); + + extern int rtnl_link_ipip_set_ttl(struct rtnl_link *link, uint8_t ttl); + extern uint8_t rtnl_link_ipip_get_ttl(struct rtnl_link *link); + + extern int rtnl_link_ipip_set_tos(struct rtnl_link *link, uint8_t tos); + extern uint8_t rtnl_link_ipip_get_tos(struct rtnl_link *link); + + extern int rtnl_link_ipip_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc); + extern uint8_t rtnl_link_ipip_get_pmtudisc(struct rtnl_link *link); + + extern int rtnl_link_ipip_set_fwmark(struct rtnl_link *link, uint32_t fwmark); + extern int rtnl_link_ipip_get_fwmark(struct rtnl_link *link, uint32_t *fwmark); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/ipvlan.h b/libnl/include/netlink/route/link/ipvlan.h new file mode 100644 index 0000000..09b1d2d --- /dev/null +++ b/libnl/include/netlink/route/link/ipvlan.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2015 Cong Wang + */ + +#ifndef NETLINK_LINK_IPVLAN_H_ +#define NETLINK_LINK_IPVLAN_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_link *rtnl_link_ipvlan_alloc(void); + +extern int rtnl_link_is_ipvlan(struct rtnl_link *); + +extern char * rtnl_link_ipvlan_mode2str(int, char *, size_t); +extern int rtnl_link_ipvlan_str2mode(const char *); + +extern int rtnl_link_ipvlan_set_mode(struct rtnl_link *, + uint16_t); +extern int rtnl_link_ipvlan_get_mode(struct rtnl_link *, uint16_t *out_mode); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/ipvti.h b/libnl/include/netlink/route/link/ipvti.h new file mode 100644 index 0000000..8e31dab --- /dev/null +++ b/libnl/include/netlink/route/link/ipvti.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +#ifndef NETLINK_LINK_IPVTI_H_ +#define NETLINK_LINK_IPVTI_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + extern struct rtnl_link *rtnl_link_ipvti_alloc(void); + extern int rtnl_link_ipvti_add(struct nl_sock *sk, const char *name); + + extern int rtnl_link_is_ipvti(struct rtnl_link *link); + + extern int rtnl_link_ipvti_set_link(struct rtnl_link *link, uint32_t index); + extern uint32_t rtnl_link_ipvti_get_link(struct rtnl_link *link); + + extern int rtnl_link_ipvti_set_ikey(struct rtnl_link *link, uint32_t ikey); + extern uint32_t rtnl_link_ipvti_get_ikey(struct rtnl_link *link); + + extern int rtnl_link_ipvti_set_okey(struct rtnl_link *link, uint32_t okey); + extern uint32_t rtnl_link_ipvti_get_okey(struct rtnl_link *link); + + extern int rtnl_link_ipvti_set_local(struct rtnl_link *link, uint32_t addr); + extern uint32_t rtnl_link_ipvti_get_local(struct rtnl_link *link); + + extern int rtnl_link_ipvti_set_remote(struct rtnl_link *link, uint32_t addr); + extern uint32_t rtnl_link_ipvti_get_remote(struct rtnl_link *link); + + extern int rtnl_link_ipvti_set_fwmark(struct rtnl_link *link, uint32_t fwmark); + extern int rtnl_link_ipvti_get_fwmark(struct rtnl_link *link, uint32_t *fwmark); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/macsec.h b/libnl/include/netlink/route/link/macsec.h new file mode 100644 index 0000000..3139b47 --- /dev/null +++ b/libnl/include/netlink/route/link/macsec.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Sabrina Dubroca + */ + +#ifndef NETLINK_LINK_MACSEC_H_ +#define NETLINK_LINK_MACSEC_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum macsec_validation_type; + +struct rtnl_link *rtnl_link_macsec_alloc(void); + +int rtnl_link_macsec_set_sci(struct rtnl_link *, uint64_t); +int rtnl_link_macsec_get_sci(struct rtnl_link *, uint64_t *); + +int rtnl_link_macsec_set_port(struct rtnl_link *, uint16_t); +int rtnl_link_macsec_get_port(struct rtnl_link *, uint16_t *); + +int rtnl_link_macsec_set_cipher_suite(struct rtnl_link *, uint64_t); +int rtnl_link_macsec_get_cipher_suite(struct rtnl_link *, uint64_t *); + +int rtnl_link_macsec_set_icv_len(struct rtnl_link *, uint16_t); +int rtnl_link_macsec_get_icv_len(struct rtnl_link *, uint16_t *); + +int rtnl_link_macsec_set_protect(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_protect(struct rtnl_link *, uint8_t *); + +int rtnl_link_macsec_set_encrypt(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_encrypt(struct rtnl_link *, uint8_t *); + +int rtnl_link_macsec_set_offload(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_offload(struct rtnl_link *, uint8_t *); + +int rtnl_link_macsec_set_encoding_sa(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_encoding_sa(struct rtnl_link *, uint8_t *); + +int rtnl_link_macsec_set_validation_type(struct rtnl_link *, + enum macsec_validation_type); +int rtnl_link_macsec_get_validation_type(struct rtnl_link *, + enum macsec_validation_type *); + +int rtnl_link_macsec_set_replay_protect(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_replay_protect(struct rtnl_link *, uint8_t *); + +int rtnl_link_macsec_set_window(struct rtnl_link *, uint32_t); +int rtnl_link_macsec_get_window(struct rtnl_link *, uint32_t *); + +int rtnl_link_macsec_set_send_sci(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_send_sci(struct rtnl_link *, uint8_t *); + +int rtnl_link_macsec_set_end_station(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_end_station(struct rtnl_link *, uint8_t *); + +int rtnl_link_macsec_set_scb(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_scb(struct rtnl_link *, uint8_t *); + + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/macvlan.h b/libnl/include/netlink/route/link/macvlan.h new file mode 100644 index 0000000..7c133cb --- /dev/null +++ b/libnl/include/netlink/route/link/macvlan.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Michael Braun + */ + +#ifndef NETLINK_LINK_MACVLAN_H_ +#define NETLINK_LINK_MACVLAN_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_link *rtnl_link_macvlan_alloc(void); + +extern int rtnl_link_is_macvlan(struct rtnl_link *); + +extern char * rtnl_link_macvlan_mode2str(int, char *, size_t); +extern int rtnl_link_macvlan_str2mode(const char *); + +extern char * rtnl_link_macvlan_flags2str(int, char *, size_t); +extern int rtnl_link_macvlan_str2flags(const char *); + +extern char * rtnl_link_macvlan_macmode2str(int, char *, size_t); +extern int rtnl_link_macvlan_str2macmode(const char *); + +extern int rtnl_link_macvlan_set_mode(struct rtnl_link *, + uint32_t); +extern uint32_t rtnl_link_macvlan_get_mode(struct rtnl_link *); + +extern int rtnl_link_macvlan_set_flags(struct rtnl_link *, + uint16_t); +extern int rtnl_link_macvlan_unset_flags(struct rtnl_link *, + uint16_t); +extern uint16_t rtnl_link_macvlan_get_flags(struct rtnl_link *); + +extern int rtnl_link_macvlan_set_macmode(struct rtnl_link *, + uint32_t); +extern int rtnl_link_macvlan_get_macmode(struct rtnl_link *link, + uint32_t *out_macmode); + +extern int rtnl_link_macvlan_count_macaddr(struct rtnl_link *link, + uint32_t *out_count); +extern int rtnl_link_macvlan_get_macaddr(struct rtnl_link *link, + uint32_t idx, + const struct nl_addr **addr); +extern int rtnl_link_macvlan_add_macaddr(struct rtnl_link *link, + struct nl_addr *addr); +extern int rtnl_link_macvlan_del_macaddr(struct rtnl_link *link, + struct nl_addr *addr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/macvtap.h b/libnl/include/netlink/route/link/macvtap.h new file mode 100644 index 0000000..6fff30d --- /dev/null +++ b/libnl/include/netlink/route/link/macvtap.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2015 Beniamino Galvani + */ + +#ifndef NETLINK_LINK_MACVTAP_H_ +#define NETLINK_LINK_MACVTAP_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_link *rtnl_link_macvtap_alloc(void); + +extern int rtnl_link_is_macvtap(struct rtnl_link *); + +extern char * rtnl_link_macvtap_mode2str(int, char *, size_t); +extern int rtnl_link_macvtap_str2mode(const char *); + +extern char * rtnl_link_macvtap_flags2str(int, char *, size_t); +extern int rtnl_link_macvtap_str2flags(const char *); + +extern int rtnl_link_macvtap_set_mode(struct rtnl_link *, + uint32_t); +extern uint32_t rtnl_link_macvtap_get_mode(struct rtnl_link *); + +extern int rtnl_link_macvtap_set_flags(struct rtnl_link *, + uint16_t); +extern int rtnl_link_macvtap_unset_flags(struct rtnl_link *, + uint16_t); +extern uint16_t rtnl_link_macvtap_get_flags(struct rtnl_link *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/ppp.h b/libnl/include/netlink/route/link/ppp.h new file mode 100644 index 0000000..d65582a --- /dev/null +++ b/libnl/include/netlink/route/link/ppp.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Jonas Johansson + */ + +#ifndef NETLINK_LINK_PPP_H_ +#define NETLINK_LINK_PPP_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_link *rtnl_link_ppp_alloc(void); +extern int rtnl_link_ppp_set_fd(struct rtnl_link *, int32_t); +extern int rtnl_link_ppp_get_fd(struct rtnl_link *, int32_t *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/sit.h b/libnl/include/netlink/route/link/sit.h new file mode 100644 index 0000000..f41e101 --- /dev/null +++ b/libnl/include/netlink/route/link/sit.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +#ifndef NETLINK_LINK_SIT_H_ +#define NETLINK_LINK_SIT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + extern struct rtnl_link *rtnl_link_sit_alloc(void); + extern int rtnl_link_sit_add(struct nl_sock *sk, const char *name); + + extern int rtnl_link_is_sit(struct rtnl_link *link); + + extern int rtnl_link_sit_set_link(struct rtnl_link *link, uint32_t index); + extern uint32_t rtnl_link_sit_get_link(struct rtnl_link *link); + + extern int rtnl_link_sit_set_local(struct rtnl_link *link, uint32_t addr); + extern uint32_t rtnl_link_sit_get_local(struct rtnl_link *link); + + extern int rtnl_link_sit_set_remote(struct rtnl_link *link, uint32_t addr); + extern uint32_t rtnl_link_sit_get_remote(struct rtnl_link *link); + + extern int rtnl_link_sit_set_ttl(struct rtnl_link *link, uint8_t ttl); + extern uint8_t rtnl_link_sit_get_ttl(struct rtnl_link *link); + + extern int rtnl_link_sit_set_tos(struct rtnl_link *link, uint8_t tos); + extern uint8_t rtnl_link_sit_get_tos(struct rtnl_link *link); + + extern int rtnl_link_sit_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc); + extern uint8_t rtnl_link_sit_get_pmtudisc(struct rtnl_link *link); + + extern int rtnl_link_sit_set_flags(struct rtnl_link *link, uint16_t flags); + extern uint16_t rtnl_link_sit_get_flags(struct rtnl_link *link); + + int rtnl_link_sit_set_proto(struct rtnl_link *link, uint8_t proto); + uint8_t rtnl_link_sit_get_proto(struct rtnl_link *link); + + int rtnl_link_sit_set_ip6rd_prefix(struct rtnl_link *link, const struct in6_addr *prefix); + int rtnl_link_sit_get_ip6rd_prefix(const struct rtnl_link *link, struct in6_addr *prefix); + + int rtnl_link_sit_set_ip6rd_prefixlen(struct rtnl_link *link, uint16_t prefixlen); + int rtnl_link_sit_get_ip6rd_prefixlen(struct rtnl_link *link, uint16_t *prefixlen); + + int rtnl_link_sit_set_ip6rd_relay_prefix(struct rtnl_link *link, uint32_t prefix); + int rtnl_link_sit_get_ip6rd_relay_prefix(const struct rtnl_link *link, uint32_t *prefix); + + int rtnl_link_sit_set_ip6rd_relay_prefixlen(struct rtnl_link *link, uint16_t prefix); + int rtnl_link_sit_get_ip6rd_relay_prefixlen(struct rtnl_link *link, uint16_t *prefix); + + extern int rtnl_link_sit_set_fwmark(struct rtnl_link *link, uint32_t fwmark); + extern int rtnl_link_sit_get_fwmark(struct rtnl_link *link, uint32_t *fwmark); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/sriov.h b/libnl/include/netlink/route/link/sriov.h new file mode 100644 index 0000000..36d66bf --- /dev/null +++ b/libnl/include/netlink/route/link/sriov.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Intel Corp. All rights reserved. + * Copyright (c) 2016 Jef Oliver + */ + +#ifndef NETLINK_LINK_SRIOV_H_ +#define NETLINK_LINK_SRIOV_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define RTNL_VF_GUID_STR_LEN 23 + +/** + * @ingroup sriov + */ +typedef enum { + RTNL_LINK_VF_RATE_API_UNSPEC, /*!< Unspecified API type */ + RTNL_LINK_VF_RATE_API_OLD, /*!< Old Rate setting API */ + RTNL_LINK_VF_RATE_API_NEW, /*!< New Rate setting API */ + __RTNL_LINK_VF_RATE_API_MAX, +} rtnl_link_rate_api_t; + +#define RTNL_LINK_VF_RATE_API_MAX (__RTNL_LINK_VF_RATE_API_MAX - 1) + +/** + * @ingroup sriov + */ +typedef enum { + RTNL_LINK_VF_STATS_RX_PACKETS, /*!< Packets Received */ + RTNL_LINK_VF_STATS_TX_PACKETS, /*!< Packets Sent */ + RTNL_LINK_VF_STATS_RX_BYTES, /*!< Bytes Recieved */ + RTNL_LINK_VF_STATS_TX_BYTES, /*!< Bytes Sent */ + RTNL_LINK_VF_STATS_BROADCAST, /*!< Broadcast packets received */ + RTNL_LINK_VF_STATS_MULTICAST, /*!< Multicast packets received */ + __RTNL_LINK_VF_STATS_MAX, +} rtnl_link_vf_stats_t; + +#define RTNL_LINK_VF_STATS_MAX (__RTNL_LINK_VF_STATS_MAX - 1) + +/** + * @struct rtnl_link_vf sriov.h "netlink/route/link/sriov.h" + * @brief SRIOV VF object + * @ingroup sriov + * + * @copydoc private_struct + */ +struct rtnl_link_vf; + +/** + * @brief SRIOV VF VFLAN settings + * @ingroup sriov + */ +typedef struct nl_vf_vlan_info { + uint32_t vf_vlan; /*!< VLAN number */ + uint32_t vf_vlan_qos; /*!< VLAN QOS value */ + uint16_t vf_vlan_proto; /*!< VLAN protocol */ +} nl_vf_vlan_info_t; + +/** + * @brief SRIOV VF VLANs information + * @ingroup sriov + */ +typedef struct nl_vf_vlans { + int ce_refcnt; /*!< Reference counter. Don't change this value */ + int size; /*!< Number of VLANs on the SRIOV VF */ + nl_vf_vlan_info_t * vlans; /*!< nl_vf_vlan_info_t array of SRIOV VF VLANs */ +} nl_vf_vlans_t; + +/** + * @brief VF Rate information structure + * @ingroup sriov + */ +struct nl_vf_rate { + int api; /*!< rtnl_link_rate_api_t API Version to use */ + uint32_t rate; /*!< Old API Max Rate in Mbps */ + uint32_t max_tx_rate; /*!< New API Max Rate in Mbps */ + uint32_t min_tx_rate; /*!< New API Mix Rate in Mbps */ +}; + +extern int rtnl_link_vf_add(struct rtnl_link *, struct rtnl_link_vf *); +extern struct rtnl_link_vf *rtnl_link_vf_alloc(void); +extern void rtnl_link_vf_free(struct rtnl_link_vf *); +extern struct rtnl_link_vf *rtnl_link_vf_get(struct rtnl_link *, uint32_t); +extern void rtnl_link_vf_put(struct rtnl_link_vf *); + +extern int rtnl_link_vf_get_addr(struct rtnl_link_vf *, struct nl_addr **); +extern void rtnl_link_vf_set_addr(struct rtnl_link_vf *, struct nl_addr *); + +extern void rtnl_link_vf_set_ib_node_guid(struct rtnl_link_vf *, uint64_t); +extern void rtnl_link_vf_set_ib_port_guid(struct rtnl_link_vf *, uint64_t); + +extern int rtnl_link_vf_get_index(struct rtnl_link_vf *, uint32_t *); +extern void rtnl_link_vf_set_index(struct rtnl_link_vf *, uint32_t); + +extern int rtnl_link_vf_get_linkstate(struct rtnl_link_vf *, uint32_t *); +extern void rtnl_link_vf_set_linkstate(struct rtnl_link_vf *, uint32_t); + +extern int rtnl_link_vf_get_rate(struct rtnl_link_vf *, struct nl_vf_rate *); +extern void rtnl_link_vf_set_rate(struct rtnl_link_vf *, struct nl_vf_rate *); + +extern int rtnl_link_vf_get_rss_query_en(struct rtnl_link_vf *, uint32_t *); +extern void rtnl_link_vf_set_rss_query_en(struct rtnl_link_vf *, uint32_t); + +extern int rtnl_link_vf_get_spoofchk(struct rtnl_link_vf *, uint32_t *); +extern void rtnl_link_vf_set_spoofchk(struct rtnl_link_vf *, uint32_t); + +extern int rtnl_link_vf_get_stat(struct rtnl_link_vf *, rtnl_link_vf_stats_t, + uint64_t *); + +extern int rtnl_link_vf_get_trust(struct rtnl_link_vf *, uint32_t *); +extern void rtnl_link_vf_set_trust(struct rtnl_link_vf *, uint32_t); + +extern int rtnl_link_vf_get_vlans(struct rtnl_link_vf *, nl_vf_vlans_t **); +extern void rtnl_link_vf_set_vlans(struct rtnl_link_vf *, nl_vf_vlans_t *); + +extern int rtnl_link_vf_vlan_alloc(nl_vf_vlans_t **, int); +extern void rtnl_link_vf_vlan_free(nl_vf_vlans_t *vf_vlans); +extern void rtnl_link_vf_vlan_put(nl_vf_vlans_t *); + +/* Utility functions */ +extern char *rtnl_link_vf_linkstate2str(uint32_t, char *, size_t); +extern int rtnl_link_vf_str2linkstate(const char *); + +extern char *rtnl_link_vf_vlanproto2str(uint16_t, char *, size_t); +extern int rtnl_link_vf_str2vlanproto(const char *); + +extern int rtnl_link_vf_str2guid(uint64_t *, const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/team.h b/libnl/include/netlink/route/link/team.h new file mode 100644 index 0000000..a55cdf9 --- /dev/null +++ b/libnl/include/netlink/route/link/team.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2015 Jonas Johansson + */ + +#ifndef NETLINK_LINK_TEAM_H_ +#define NETLINK_LINK_TEAM_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_link *rtnl_link_team_alloc(void); + +extern int rtnl_link_team_add(struct nl_sock *, const char *, + struct rtnl_link *); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libnl/include/netlink/route/link/veth.h b/libnl/include/netlink/route/link/veth.h new file mode 100644 index 0000000..66a0d93 --- /dev/null +++ b/libnl/include/netlink/route/link/veth.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +#ifndef NETLINK_LINK_VETH_H_ +#define NETLINK_LINK_VETH_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_link *rtnl_link_veth_alloc(void); +extern void rtnl_link_veth_release(struct rtnl_link *); + +extern int rtnl_link_is_veth(struct rtnl_link *); + +extern struct rtnl_link *rtnl_link_veth_get_peer(struct rtnl_link *); +extern int rtnl_link_veth_add(struct nl_sock *sock, const char *name, + const char *peer, pid_t pid); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/vlan.h b/libnl/include/netlink/route/link/vlan.h new file mode 100644 index 0000000..2729d6e --- /dev/null +++ b/libnl/include/netlink/route/link/vlan.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_LINK_VLAN_H_ +#define NETLINK_LINK_VLAN_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct vlan_map +{ + uint32_t vm_from; + uint32_t vm_to; +}; + +#define VLAN_PRIO_MAX 7 + +extern struct rtnl_link *rtnl_link_vlan_alloc(void); + +extern int rtnl_link_is_vlan(struct rtnl_link *); + +extern char * rtnl_link_vlan_flags2str(int, char *, size_t); +extern int rtnl_link_vlan_str2flags(const char *); + +extern int rtnl_link_vlan_set_protocol(struct rtnl_link *link, uint16_t); +extern int rtnl_link_vlan_get_protocol(struct rtnl_link *link); + +extern int rtnl_link_vlan_set_id(struct rtnl_link *, uint16_t); +extern int rtnl_link_vlan_get_id(struct rtnl_link *); + +extern int rtnl_link_vlan_set_flags(struct rtnl_link *, + unsigned int); +extern int rtnl_link_vlan_unset_flags(struct rtnl_link *, + unsigned int); +extern int rtnl_link_vlan_get_flags(struct rtnl_link *); + +extern int rtnl_link_vlan_set_ingress_map(struct rtnl_link *, + int, uint32_t); +extern uint32_t * rtnl_link_vlan_get_ingress_map(struct rtnl_link *); + +extern int rtnl_link_vlan_set_egress_map(struct rtnl_link *, + uint32_t, int); +extern struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *, + int *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/vrf.h b/libnl/include/netlink/route/link/vrf.h new file mode 100644 index 0000000..5d49437 --- /dev/null +++ b/libnl/include/netlink/route/link/vrf.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2015 Cumulus Networks. All rights reserved. + * Copyright (c) 2015 David Ahern + */ + +#ifndef NETLINK_LINK_VRF_H_ +#define NETLINK_LINK_VRF_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_link *rtnl_link_vrf_alloc(void); +extern int rtnl_link_is_vrf(struct rtnl_link *link); +extern int rtnl_link_vrf_get_tableid(struct rtnl_link *link, uint32_t *id); +extern int rtnl_link_vrf_set_tableid(struct rtnl_link *link, uint32_t id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/vxlan.h b/libnl/include/netlink/route/link/vxlan.h new file mode 100644 index 0000000..6321b09 --- /dev/null +++ b/libnl/include/netlink/route/link/vxlan.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Yasunobu Chiba + */ + +#ifndef NETLINK_LINK_VXLAN_H_ +#define NETLINK_LINK_VXLAN_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ifla_vxlan_port_range; + +#define VXLAN_ID_MAX 16777215 + +enum { + RTNL_LINK_VXLAN_F_GBP = 1 << 0, +#define RTNL_LINK_VXLAN_F_GBP RTNL_LINK_VXLAN_F_GBP + RTNL_LINK_VXLAN_F_GPE = 1 << 1, +#define RTNL_LINK_VXLAN_F_GPE RTNL_LINK_VXLAN_F_GPE + RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL = 1 << 2, +#define RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL +}; + +extern struct rtnl_link *rtnl_link_vxlan_alloc(void); + +extern int rtnl_link_is_vxlan(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_id(struct rtnl_link *, uint32_t); +extern int rtnl_link_vxlan_get_id(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_vxlan_set_group(struct rtnl_link *, struct nl_addr *); +extern int rtnl_link_vxlan_get_group(struct rtnl_link *, struct nl_addr **); + +extern int rtnl_link_vxlan_set_link(struct rtnl_link *, uint32_t); +extern int rtnl_link_vxlan_get_link(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_vxlan_set_local(struct rtnl_link *, struct nl_addr *); +extern int rtnl_link_vxlan_get_local(struct rtnl_link *, struct nl_addr **); + +extern int rtnl_link_vxlan_set_ttl(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_ttl(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_tos(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_tos(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_learning(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_learning(struct rtnl_link *); +extern int rtnl_link_vxlan_enable_learning(struct rtnl_link *); +extern int rtnl_link_vxlan_disable_learning(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_ageing(struct rtnl_link *, uint32_t); +extern int rtnl_link_vxlan_get_ageing(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_vxlan_set_limit(struct rtnl_link *, uint32_t); +extern int rtnl_link_vxlan_get_limit(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_vxlan_set_port_range(struct rtnl_link *, + struct ifla_vxlan_port_range *); +extern int rtnl_link_vxlan_get_port_range(struct rtnl_link *, + struct ifla_vxlan_port_range *); + +extern int rtnl_link_vxlan_set_proxy(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_proxy(struct rtnl_link *); +extern int rtnl_link_vxlan_enable_proxy(struct rtnl_link *); +extern int rtnl_link_vxlan_disable_proxy(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_rsc(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_rsc(struct rtnl_link *); +extern int rtnl_link_vxlan_enable_rsc(struct rtnl_link *); +extern int rtnl_link_vxlan_disable_rsc(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_l2miss(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_l2miss(struct rtnl_link *); +extern int rtnl_link_vxlan_enable_l2miss(struct rtnl_link *); +extern int rtnl_link_vxlan_disable_l2miss(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_l3miss(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_l3miss(struct rtnl_link *); +extern int rtnl_link_vxlan_enable_l3miss(struct rtnl_link *); +extern int rtnl_link_vxlan_disable_l3miss(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_port(struct rtnl_link *, uint32_t); +extern int rtnl_link_vxlan_get_port(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_vxlan_set_udp_csum(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_udp_csum(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_udp_zero_csum6_tx(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_udp_zero_csum6_tx(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_udp_zero_csum6_rx(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_udp_zero_csum6_rx(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_remcsum_tx(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_remcsum_tx(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_remcsum_rx(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_remcsum_rx(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_flags(struct rtnl_link *, uint32_t flags, int enable); +extern int rtnl_link_vxlan_get_flags(struct rtnl_link *, uint32_t *out_flags); + +extern int rtnl_link_vxlan_set_collect_metadata(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_collect_metadata(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_label(struct rtnl_link *, uint32_t); +extern int rtnl_link_vxlan_get_label(struct rtnl_link *, uint32_t *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/xfrmi.h b/libnl/include/netlink/route/link/xfrmi.h new file mode 100644 index 0000000..094ea11 --- /dev/null +++ b/libnl/include/netlink/route/link/xfrmi.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2019 Eyal Birger + * + * Based on netlink/route/link/ipvti.h + */ + +#ifndef NETLINK_LINK_XFRMI_H_ +#define NETLINK_LINK_XFRMI_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + extern struct rtnl_link *rtnl_link_xfrmi_alloc(void); + + extern int rtnl_link_is_xfrmi(struct rtnl_link *link); + + extern int rtnl_link_xfrmi_set_link(struct rtnl_link *link, uint32_t index); + extern int rtnl_link_xfrmi_get_link(struct rtnl_link *link, uint32_t *out_link); + + extern int rtnl_link_xfrmi_set_if_id(struct rtnl_link *link, uint32_t if_id); + extern int rtnl_link_xfrmi_get_if_id(struct rtnl_link *link, uint32_t *out_if_id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/mdb.h b/libnl/include/netlink/route/mdb.h new file mode 100644 index 0000000..a65ea84 --- /dev/null +++ b/libnl/include/netlink/route/mdb.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#ifndef NETLINK_MDB_H_ +#define NETLINK_MDB_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_mdb; +struct rtnl_mdb_entry; + +struct rtnl_mdb *rtnl_mdb_alloc(void); +void rtnl_mdb_put(struct rtnl_mdb *mdb); + +int rtnl_mdb_alloc_cache(struct nl_sock *sk, struct nl_cache **result); +int rtnl_mdb_alloc_cache_flags(struct nl_sock *sock, + struct nl_cache **result, + unsigned int flags); + +uint32_t rtnl_mdb_get_ifindex(struct rtnl_mdb *mdb); +void rtnl_mdb_add_entry(struct rtnl_mdb *mdb, + struct rtnl_mdb_entry *_entry); + +void rtnl_mdb_foreach_entry(struct rtnl_mdb *mdb, + void (*cb)(struct rtnl_mdb_entry *, void *), + void *arg); + +int rtnl_mdb_entry_get_ifindex(struct rtnl_mdb_entry *mdb_entry); +int rtnl_mdb_entry_get_vid(struct rtnl_mdb_entry *mdb_entry); +int rtnl_mdb_entry_get_state(struct rtnl_mdb_entry *mdb_entry); +struct nl_addr *rtnl_mdb_entry_get_addr(struct rtnl_mdb_entry + *mdb_entry); +uint16_t rtnl_mdb_entry_get_proto(struct rtnl_mdb_entry *mdb_entry); +#ifdef __cplusplus +} +#endif +#endif diff --git a/libnl/include/netlink/route/neighbour.h b/libnl/include/netlink/route/neighbour.h new file mode 100644 index 0000000..09a2c37 --- /dev/null +++ b/libnl/include/netlink/route/neighbour.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +#ifndef NETLINK_NEIGHBOUR_H_ +#define NETLINK_NEIGHBOUR_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_neigh; + +extern struct rtnl_neigh *rtnl_neigh_alloc(void); +extern void rtnl_neigh_put(struct rtnl_neigh *); + +extern int rtnl_neigh_alloc_cache(struct nl_sock *, struct nl_cache **); +extern int rtnl_neigh_alloc_cache_flags(struct nl_sock *, + struct nl_cache **, + unsigned int); +extern struct rtnl_neigh *rtnl_neigh_get(struct nl_cache *, int, + struct nl_addr *); +extern struct rtnl_neigh *rtnl_neigh_get_by_vlan(struct nl_cache *, int, + struct nl_addr *, int); + +extern int rtnl_neigh_parse(struct nlmsghdr *, struct rtnl_neigh **); + +extern char * rtnl_neigh_state2str(int, char *, size_t); +extern int rtnl_neigh_str2state(const char *); + +extern char * rtnl_neigh_flags2str(int, char *, size_t); +extern int rtnl_neigh_str2flag(const char *); + +extern char * rtnl_neigh_extflags2str(uint32_t, char *, size_t); +extern uint32_t rtnl_neigh_str2extflag(const char *); + +extern int rtnl_neigh_add(struct nl_sock *, struct rtnl_neigh *, int); +extern int rtnl_neigh_build_add_request(struct rtnl_neigh *, int, + struct nl_msg **); + +extern int rtnl_neigh_delete(struct nl_sock *, struct rtnl_neigh *, int); +extern int rtnl_neigh_build_delete_request(struct rtnl_neigh *, int, + struct nl_msg **); + +extern void rtnl_neigh_set_state(struct rtnl_neigh *, int); +extern int rtnl_neigh_get_state(struct rtnl_neigh *); +extern void rtnl_neigh_unset_state(struct rtnl_neigh *, + int); + +extern void rtnl_neigh_set_flags(struct rtnl_neigh *, + unsigned int); +extern void rtnl_neigh_unset_flags(struct rtnl_neigh *, + unsigned int); +extern unsigned int rtnl_neigh_get_flags(struct rtnl_neigh *); + +extern void rtnl_neigh_set_ext_flags(struct rtnl_neigh *, + uint32_t); +extern void rtnl_neigh_unset_ext_flags(struct rtnl_neigh *, + uint32_t); +extern int rtnl_neigh_get_ext_flags(struct rtnl_neigh *, + uint32_t *); + +extern void rtnl_neigh_set_ifindex(struct rtnl_neigh *, + int); +extern int rtnl_neigh_get_ifindex(struct rtnl_neigh *); + +extern void rtnl_neigh_set_lladdr(struct rtnl_neigh *, + struct nl_addr *); +extern struct nl_addr * rtnl_neigh_get_lladdr(struct rtnl_neigh *); + +extern int rtnl_neigh_set_dst(struct rtnl_neigh *, + struct nl_addr *); +extern struct nl_addr * rtnl_neigh_get_dst(struct rtnl_neigh *); + +extern void rtnl_neigh_set_type(struct rtnl_neigh *, int); +extern int rtnl_neigh_get_type(struct rtnl_neigh *); + +extern void rtnl_neigh_set_family(struct rtnl_neigh *, int); +extern int rtnl_neigh_get_family(struct rtnl_neigh *); + +extern void rtnl_neigh_set_vlan(struct rtnl_neigh *, int); +extern int rtnl_neigh_get_vlan(struct rtnl_neigh *); + +extern void rtnl_neigh_set_master(struct rtnl_neigh *, int); +extern int rtnl_neigh_get_master(struct rtnl_neigh *); + +extern void rtnl_neigh_set_nhid(struct rtnl_neigh *, uint32_t); +extern int rtnl_neigh_get_nhid(struct rtnl_neigh *, uint32_t *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/neightbl.h b/libnl/include/netlink/route/neightbl.h new file mode 100644 index 0000000..8bcdab2 --- /dev/null +++ b/libnl/include/netlink/route/neightbl.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +#ifndef NETLINK_NEIGHTBL_H_ +#define NETLINK_NEIGHTBL_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_neightbl; + +extern struct rtnl_neightbl *rtnl_neightbl_alloc(void); +extern void rtnl_neightbl_put(struct rtnl_neightbl *); +extern void rtnl_neightbl_free(struct rtnl_neightbl *); +extern int rtnl_neightbl_alloc_cache(struct nl_sock *, struct nl_cache **); +extern struct rtnl_neightbl *rtnl_neightbl_get(struct nl_cache *, + const char *, int); +extern void rtnl_neightbl_dump(struct rtnl_neightbl *, FILE *, + struct nl_dump_params *); + +extern int rtnl_neightbl_build_change_request(struct rtnl_neightbl *, + struct rtnl_neightbl *, + struct nl_msg **); +extern int rtnl_neightbl_change(struct nl_sock *, struct rtnl_neightbl *, + struct rtnl_neightbl *); + +extern void rtnl_neightbl_set_family(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_gc_tresh1(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_gc_tresh2(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_gc_tresh3(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_gc_interval(struct rtnl_neightbl *, uint64_t); +extern void rtnl_neightbl_set_name(struct rtnl_neightbl *, const char *); +extern void rtnl_neightbl_set_dev(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_queue_len(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_proxy_queue_len(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_app_probes(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_ucast_probes(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_mcast_probes(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_base_reachable_time(struct rtnl_neightbl *, + uint64_t); +extern void rtnl_neightbl_set_retrans_time(struct rtnl_neightbl *, uint64_t); +extern void rtnl_neightbl_set_gc_stale_time(struct rtnl_neightbl *, uint64_t); +extern void rtnl_neightbl_set_delay_probe_time(struct rtnl_neightbl *, + uint64_t); +extern void rtnl_neightbl_set_anycast_delay(struct rtnl_neightbl *, uint64_t); +extern void rtnl_neightbl_set_proxy_delay(struct rtnl_neightbl *, uint64_t); +extern void rtnl_neightbl_set_locktime(struct rtnl_neightbl *, uint64_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/netconf.h b/libnl/include/netlink/route/netconf.h new file mode 100644 index 0000000..a6b1f89 --- /dev/null +++ b/libnl/include/netlink/route/netconf.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2017 David Ahern + */ + +#ifndef NETCONF_H_ +#define NETCONF_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_sock; +struct nl_cache; +struct rtnl_netconf; + +int rtnl_netconf_alloc_cache(struct nl_sock *sk, struct nl_cache **result); + +struct rtnl_netconf *rtnl_netconf_get_by_idx(struct nl_cache *cache, int family, + int ifindex); +struct rtnl_netconf *rtnl_netconf_get_all(struct nl_cache *cache, + int family); +struct rtnl_netconf *rtnl_netconf_get_default(struct nl_cache *cache, + int family); +void rtnl_netconf_put(struct rtnl_netconf *nc); + +int rtnl_netconf_get_family(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_ifindex(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_forwarding(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_mc_forwarding(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_rp_filter(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_proxy_neigh(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_ignore_routes_linkdown(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_input(struct rtnl_netconf *nc, int *val); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/nexthop.h b/libnl/include/netlink/route/nexthop.h new file mode 100644 index 0000000..1beb9fa --- /dev/null +++ b/libnl/include/netlink/route/nexthop.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +#ifndef NETLINK_ROUTE_NEXTHOP_H_ +#define NETLINK_ROUTE_NEXTHOP_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_nexthop; + +enum { + NH_DUMP_FROM_ONELINE = -2, + NH_DUMP_FROM_DETAILS = -1, + NH_DUMP_FROM_ENV = 0, + /* > 0 reserved for nexthop index */ +}; + +extern struct rtnl_nexthop * rtnl_route_nh_alloc(void); +extern struct rtnl_nexthop * rtnl_route_nh_clone(struct rtnl_nexthop *); +extern void rtnl_route_nh_free(struct rtnl_nexthop *); + +extern int rtnl_route_nh_compare(struct rtnl_nexthop *, + struct rtnl_nexthop *, + uint32_t, int); + +extern int rtnl_route_nh_identical(struct rtnl_nexthop *, + struct rtnl_nexthop *); + +extern void rtnl_route_nh_dump(struct rtnl_nexthop *, + struct nl_dump_params *); + +extern void rtnl_route_nh_set_weight(struct rtnl_nexthop *, uint8_t); +extern uint8_t rtnl_route_nh_get_weight(struct rtnl_nexthop *); +extern void rtnl_route_nh_set_ifindex(struct rtnl_nexthop *, int); +extern int rtnl_route_nh_get_ifindex(struct rtnl_nexthop *); +extern void rtnl_route_nh_set_gateway(struct rtnl_nexthop *, + struct nl_addr *); +extern struct nl_addr * rtnl_route_nh_get_gateway(struct rtnl_nexthop *); +extern void rtnl_route_nh_set_flags(struct rtnl_nexthop *, + unsigned int); +extern void rtnl_route_nh_unset_flags(struct rtnl_nexthop *, + unsigned int); +extern unsigned int rtnl_route_nh_get_flags(struct rtnl_nexthop *); +extern void rtnl_route_nh_set_realms(struct rtnl_nexthop *, + uint32_t); +extern uint32_t rtnl_route_nh_get_realms(struct rtnl_nexthop *); + +extern int rtnl_route_nh_set_newdst(struct rtnl_nexthop *, + struct nl_addr *); +extern struct nl_addr * rtnl_route_nh_get_newdst(struct rtnl_nexthop *); +extern int rtnl_route_nh_set_via(struct rtnl_nexthop *, + struct nl_addr *); +extern struct nl_addr * rtnl_route_nh_get_via(struct rtnl_nexthop *); +extern char * rtnl_route_nh_flags2str(int, char *, size_t); +extern int rtnl_route_nh_str2flags(const char *); + +/* + * nexthop encapsulations + */ +extern int rtnl_route_nh_encap_mpls(struct rtnl_nexthop *nh, + struct nl_addr *addr, + uint8_t ttl); +extern struct nl_addr * rtnl_route_nh_get_encap_mpls_dst(struct rtnl_nexthop *); +extern uint8_t rtnl_route_nh_get_encap_mpls_ttl(struct rtnl_nexthop *); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/nh.h b/libnl/include/netlink/route/nh.h new file mode 100644 index 0000000..3edbad7 --- /dev/null +++ b/libnl/include/netlink/route/nh.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2022 Stanislav Zaikin + */ + +#ifndef NETLINK_ROUTE_NEXTHOP2_H_ +#define NETLINK_ROUTE_NEXTHOP2_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_nh; + +typedef struct nl_nh_group_info { + uint32_t nh_id; /*!< nexthop id */ + uint8_t weight; /*!< weight in nexthop group */ +} nl_nh_group_info_t; + +typedef struct nl_nh_group { + int ce_refcnt; + unsigned size; + nl_nh_group_info_t *entries; +} nl_nh_group_t; + +extern int rtnl_nh_alloc_cache(struct nl_sock *sk, int family, + struct nl_cache **result); +extern struct rtnl_nh *rtnl_nh_alloc(void); +extern void rtnl_nh_put(struct rtnl_nh *); + +extern struct rtnl_nh *rtnl_nh_get(struct nl_cache *cache, int nhid); + +extern int rtnl_nh_set_gateway(struct rtnl_nh *, struct nl_addr *); +extern struct nl_addr *rtnl_nh_get_gateway(struct rtnl_nh *); + +extern int rtnl_nh_set_fdb(struct rtnl_nh *, int value); +extern int rtnl_nh_get_fdb(struct rtnl_nh *); + +extern int rtnl_nh_get_group_entry(struct rtnl_nh *, int n); +extern int rtnl_nh_get_group_size(struct rtnl_nh *); + +extern int rtnl_nh_get_id(struct rtnl_nh *); +extern int rtnl_nh_get_oif(struct rtnl_nh *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/pktloc.h b/libnl/include/netlink/route/pktloc.h new file mode 100644 index 0000000..ab83821 --- /dev/null +++ b/libnl/include/netlink/route/pktloc.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#ifndef NETLINK_PKTLOC_H_ +#define NETLINK_PKTLOC_H_ + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_pktloc +{ + char * name; + uint8_t layer; + uint8_t shift; + uint16_t offset; + uint16_t align; + uint32_t mask; + uint32_t refcnt; + + struct nl_list_head list; +}; + +extern int rtnl_pktloc_lookup(const char *, struct rtnl_pktloc **); +extern struct rtnl_pktloc *rtnl_pktloc_alloc(void); +extern void rtnl_pktloc_put(struct rtnl_pktloc *); +extern int rtnl_pktloc_add(struct rtnl_pktloc *); +extern void rtnl_pktloc_foreach(void (*cb)(struct rtnl_pktloc *, void *), + void *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc.h b/libnl/include/netlink/route/qdisc.h new file mode 100644 index 0000000..7d963a5 --- /dev/null +++ b/libnl/include/netlink/route/qdisc.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +#ifndef NETLINK_QDISC_H_ +#define NETLINK_QDISC_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_qdisc; + +extern struct rtnl_qdisc * + rtnl_qdisc_alloc(void); +extern void rtnl_qdisc_put(struct rtnl_qdisc *); + +extern int rtnl_qdisc_alloc_cache(struct nl_sock *, struct nl_cache **); + +extern struct rtnl_qdisc * + rtnl_qdisc_get(struct nl_cache *, int, uint32_t); + +extern struct rtnl_qdisc * + rtnl_qdisc_get_by_parent(struct nl_cache *, int, uint32_t); +extern struct rtnl_qdisc *rtnl_qdisc_get_by_kind(struct nl_cache *cache, + int ifindex, char *kind); + +extern int rtnl_qdisc_build_add_request(struct rtnl_qdisc *, int, + struct nl_msg **); +extern int rtnl_qdisc_add(struct nl_sock *, struct rtnl_qdisc *, int); + +extern int rtnl_qdisc_build_update_request(struct rtnl_qdisc *, + struct rtnl_qdisc *, + int, struct nl_msg **); + +extern int rtnl_qdisc_update(struct nl_sock *, struct rtnl_qdisc *, + struct rtnl_qdisc *, int); + +extern int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *, + struct nl_msg **); +extern int rtnl_qdisc_delete(struct nl_sock *, struct rtnl_qdisc *); + +/* Deprecated functions */ +extern void rtnl_qdisc_foreach_child(struct rtnl_qdisc *, struct nl_cache *, + void (*cb)(struct nl_object *, void *), + void *) __attribute__ ((deprecated)); + +extern void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *, struct nl_cache *, + void (*cb)(struct nl_object *, void *), + void *) __attribute__ ((deprecated)); + +extern int rtnl_qdisc_build_change_request(struct rtnl_qdisc *, + struct rtnl_qdisc *, + struct nl_msg **) + __attribute__ ((deprecated)); + +extern int rtnl_qdisc_change(struct nl_sock *, struct rtnl_qdisc *, + struct rtnl_qdisc *) __attribute__ ((deprecated)); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/cbq.h b/libnl/include/netlink/route/qdisc/cbq.h new file mode 100644 index 0000000..3af6d88 --- /dev/null +++ b/libnl/include/netlink/route/qdisc/cbq.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_CBQ_H_ +#define NETLINK_CBQ_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern char * nl_ovl_strategy2str(int, char *, size_t); +extern int nl_str2ovl_strategy(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/dsmark.h b/libnl/include/netlink/route/qdisc/dsmark.h new file mode 100644 index 0000000..b8c358f --- /dev/null +++ b/libnl/include/netlink/route/qdisc/dsmark.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +#ifndef NETLINK_DSMARK_H_ +#define NETLINK_DSMARK_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_class_dsmark_set_bitmask(struct rtnl_class *, uint8_t); +extern int rtnl_class_dsmark_get_bitmask(struct rtnl_class *); + +extern int rtnl_class_dsmark_set_value(struct rtnl_class *, uint8_t); +extern int rtnl_class_dsmark_get_value(struct rtnl_class *); + +extern int rtnl_qdisc_dsmark_set_indices(struct rtnl_qdisc *, uint16_t); +extern int rtnl_qdisc_dsmark_get_indices(struct rtnl_qdisc *); + +extern int rtnl_qdisc_dsmark_set_default_index(struct rtnl_qdisc *, + uint16_t); +extern int rtnl_qdisc_dsmark_get_default_index(struct rtnl_qdisc *); + +extern int rtnl_qdisc_dsmark_set_set_tc_index(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_dsmark_get_set_tc_index(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/fifo.h b/libnl/include/netlink/route/qdisc/fifo.h new file mode 100644 index 0000000..291aac5 --- /dev/null +++ b/libnl/include/netlink/route/qdisc/fifo.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_FIFO_H_ +#define NETLINK_FIFO_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_qdisc_fifo_set_limit(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_fifo_get_limit(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/fq_codel.h b/libnl/include/netlink/route/qdisc/fq_codel.h new file mode 100644 index 0000000..1a69cd8 --- /dev/null +++ b/libnl/include/netlink/route/qdisc/fq_codel.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +#ifndef NETLINK_FQ_CODEL_H_ +#define NETLINK_FQ_CODEL_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_qdisc_fq_codel_set_limit(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_fq_codel_get_limit(struct rtnl_qdisc *); + +extern int rtnl_qdisc_fq_codel_set_target(struct rtnl_qdisc *, uint32_t); +extern uint32_t rtnl_qdisc_fq_codel_get_target(struct rtnl_qdisc *); + +extern int rtnl_qdisc_fq_codel_set_interval(struct rtnl_qdisc *, uint32_t); +extern uint32_t rtnl_qdisc_fq_codel_get_interval(struct rtnl_qdisc *); + +extern int rtnl_qdisc_fq_codel_set_quantum(struct rtnl_qdisc *, uint32_t); +extern uint32_t rtnl_qdisc_fq_codel_get_quantum(struct rtnl_qdisc *); + +extern int rtnl_qdisc_fq_codel_set_flows(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_fq_codel_get_flows(struct rtnl_qdisc *); + +extern int rtnl_qdisc_fq_codel_set_ecn(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_fq_codel_get_ecn(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/hfsc.h b/libnl/include/netlink/route/qdisc/hfsc.h new file mode 100644 index 0000000..d6a6417 --- /dev/null +++ b/libnl/include/netlink/route/qdisc/hfsc.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Cong Wang + */ + +#ifndef NETLINK_HFSC_H_ +#define NETLINK_HFSC_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct tc_service_curve; + +extern uint32_t rtnl_qdisc_hfsc_get_defcls(const struct rtnl_qdisc *); +extern int rtnl_qdisc_hfsc_set_defcls(struct rtnl_qdisc *, uint32_t); + +extern int rtnl_class_hfsc_get_rsc(const struct rtnl_class *cls, struct tc_service_curve *tsc); +extern int rtnl_class_hfsc_set_rsc(struct rtnl_class *cls, const struct tc_service_curve *tsc); +extern int rtnl_class_hfsc_get_fsc(const struct rtnl_class *cls, struct tc_service_curve *tsc); +extern int rtnl_class_hfsc_set_fsc(struct rtnl_class *cls, const struct tc_service_curve *tsc); +extern int rtnl_class_hfsc_get_usc(const struct rtnl_class *cls, struct tc_service_curve *tsc); +extern int rtnl_class_hfsc_set_usc(struct rtnl_class *cls, const struct tc_service_curve *tsc); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/htb.h b/libnl/include/netlink/route/qdisc/htb.h new file mode 100644 index 0000000..b751855 --- /dev/null +++ b/libnl/include/netlink/route/qdisc/htb.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + * Copyright (c) 2005 Petr Gotthard + * Copyright (c) 2005 Siemens AG Oesterreich + */ + +#ifndef NETLINK_HTB_H_ +#define NETLINK_HTB_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern uint32_t rtnl_htb_get_rate2quantum(struct rtnl_qdisc *); +extern int rtnl_htb_set_rate2quantum(struct rtnl_qdisc *, uint32_t); +extern uint32_t rtnl_htb_get_defcls(struct rtnl_qdisc *); +extern int rtnl_htb_set_defcls(struct rtnl_qdisc *, uint32_t); + +extern uint32_t rtnl_htb_get_prio(struct rtnl_class *); +extern int rtnl_htb_set_prio(struct rtnl_class *, uint32_t); + +extern uint32_t rtnl_htb_get_rate(struct rtnl_class *); +extern int rtnl_htb_set_rate(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_htb_get_ceil(struct rtnl_class *); +extern int rtnl_htb_set_ceil(struct rtnl_class *, uint32_t); + +extern int rtnl_htb_get_rate64(struct rtnl_class *, uint64_t *); +extern int rtnl_htb_set_rate64(struct rtnl_class *, uint64_t); +extern int rtnl_htb_get_ceil64(struct rtnl_class *, uint64_t *); +extern int rtnl_htb_set_ceil64(struct rtnl_class *, uint64_t); + +extern uint32_t rtnl_htb_get_rbuffer(struct rtnl_class *); +extern int rtnl_htb_set_rbuffer(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_htb_get_cbuffer(struct rtnl_class *); +extern int rtnl_htb_set_cbuffer(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_htb_get_quantum(struct rtnl_class *); +extern int rtnl_htb_set_quantum(struct rtnl_class *, uint32_t); +extern int rtnl_htb_set_level(struct rtnl_class *, int); +extern int rtnl_htb_get_level(struct rtnl_class *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/mqprio.h b/libnl/include/netlink/route/qdisc/mqprio.h new file mode 100644 index 0000000..0b26fdb --- /dev/null +++ b/libnl/include/netlink/route/qdisc/mqprio.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2018 Volodymyr Bendiuga + */ + +#ifndef NETLINK_MQPRIO_H_ +#define NETLINK_MQPRIO_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_qdisc_mqprio_set_num_tc(struct rtnl_qdisc *qdisc, int num_tc); +extern int rtnl_qdisc_mqprio_get_num_tc(struct rtnl_qdisc *qdisc); +extern int rtnl_qdisc_mqprio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[], + int len); +extern uint8_t *rtnl_qdisc_mqprio_get_priomap(struct rtnl_qdisc *qdisc); +extern int rtnl_qdisc_mqprio_hw_offload(struct rtnl_qdisc *qdisc, int offload); +extern int rtnl_qdisc_mqprio_get_hw_offload(struct rtnl_qdisc *qdisc); +extern int rtnl_qdisc_mqprio_set_queue(struct rtnl_qdisc *qdisc, uint16_t count[], + uint16_t offset[], int len); +extern int rtnl_qdisc_mqprio_get_queue(struct rtnl_qdisc *qdisc, uint16_t *count, + uint16_t *offset); +extern int rtnl_qdisc_mqprio_set_mode(struct rtnl_qdisc *qdisc, uint16_t mode); +extern int rtnl_qdisc_mqprio_get_mode(struct rtnl_qdisc *qdisc); +extern int rtnl_qdisc_mqprio_set_shaper(struct rtnl_qdisc *qdisc, uint16_t shaper); +extern int rtnl_qdisc_mqprio_get_shaper(struct rtnl_qdisc *qdisc); +extern int rtnl_qdisc_mqprio_set_min_rate(struct rtnl_qdisc *qdisc, uint64_t min[], + int len); +extern int rtnl_qdisc_mqprio_get_min_rate(struct rtnl_qdisc *qdisc, uint64_t *min); +extern int rtnl_qdisc_mqprio_set_max_rate(struct rtnl_qdisc *qdisc, uint64_t max[], + int len); +extern int rtnl_qdisc_mqprio_get_max_rate(struct rtnl_qdisc *qdisc, uint64_t *max); + +#ifdef __cplusplus +} +#endif + +#endif /* NETLINK_MQPRIO_H_ */ diff --git a/libnl/include/netlink/route/qdisc/netem.h b/libnl/include/netlink/route/qdisc/netem.h new file mode 100644 index 0000000..5012ef5 --- /dev/null +++ b/libnl/include/netlink/route/qdisc/netem.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_NETEM_H_ +#define NETLINK_NETEM_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_netem_set_limit(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_limit(struct rtnl_qdisc *); + +/* Packet Re-ordering */ +extern void rtnl_netem_set_gap(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_gap(struct rtnl_qdisc *); + +extern void rtnl_netem_set_reorder_probability(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *); + +extern void rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *); + +/* Corruption */ +extern void rtnl_netem_set_corruption_probability(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *); + +extern void rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *); + +/* Packet Loss */ +extern void rtnl_netem_set_loss(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_loss(struct rtnl_qdisc *); + +extern void rtnl_netem_set_loss_correlation(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *); + +/* Packet Duplication */ +extern void rtnl_netem_set_duplicate(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_duplicate(struct rtnl_qdisc *); + +extern void rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *); + +/* Packet Delay */ +extern void rtnl_netem_set_delay(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_delay(struct rtnl_qdisc *); + +extern void rtnl_netem_set_jitter(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_jitter(struct rtnl_qdisc *); + +extern void rtnl_netem_set_delay_correlation(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *); + +/* Delay Distribution */ +#define MAXDIST 65536 +extern int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *, const char *); +extern int rtnl_netem_set_delay_distribution_data(struct rtnl_qdisc *, const int16_t *, size_t len); +extern int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *); +extern int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *, int16_t **); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/plug.h b/libnl/include/netlink/route/qdisc/plug.h new file mode 100644 index 0000000..f14c043 --- /dev/null +++ b/libnl/include/netlink/route/qdisc/plug.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2012 Shriram Rajagopalan + */ + +#ifndef NETLINK_PLUG_H_ +#define NETLINK_PLUG_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_qdisc_plug_set_limit(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_plug_buffer(struct rtnl_qdisc *); +extern int rtnl_qdisc_plug_release_one(struct rtnl_qdisc *); +extern int rtnl_qdisc_plug_release_indefinite(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/prio.h b/libnl/include/netlink/route/qdisc/prio.h new file mode 100644 index 0000000..28bcc1b --- /dev/null +++ b/libnl/include/netlink/route/qdisc/prio.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +#ifndef NETLINK_PRIO_H_ +#define NETLINK_PRIO_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Default Values + * @{ + */ + +/** + * Default number of bands. + * @ingroup prio + */ +#define QDISC_PRIO_DEFAULT_BANDS 3 + +/** + * Default priority mapping. + * @ingroup prio + */ +#define QDISC_PRIO_DEFAULT_PRIOMAP \ + { 1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 } + +/** @} */ + +extern void rtnl_qdisc_prio_set_bands(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_prio_get_bands(struct rtnl_qdisc *); +extern int rtnl_qdisc_prio_set_priomap(struct rtnl_qdisc *, uint8_t[], int); +extern uint8_t *rtnl_qdisc_prio_get_priomap(struct rtnl_qdisc *); + +extern char * rtnl_prio2str(int, char *, size_t); +extern int rtnl_str2prio(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/red.h b/libnl/include/netlink/route/qdisc/red.h new file mode 100644 index 0000000..f74ad80 --- /dev/null +++ b/libnl/include/netlink/route/qdisc/red.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_RED_H_ +#define NETLINK_RED_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_red_set_limit(struct rtnl_qdisc *qdisc, int limit); +extern int rtnl_red_get_limit(struct rtnl_qdisc *qdisc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/sfq.h b/libnl/include/netlink/route/qdisc/sfq.h new file mode 100644 index 0000000..38ee0ce --- /dev/null +++ b/libnl/include/netlink/route/qdisc/sfq.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +#ifndef NETLINK_SFQ_H_ +#define NETLINK_SFQ_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_sfq_set_quantum(struct rtnl_qdisc *, int); +extern int rtnl_sfq_get_quantum(struct rtnl_qdisc *); + +extern void rtnl_sfq_set_limit(struct rtnl_qdisc *, int); +extern int rtnl_sfq_get_limit(struct rtnl_qdisc *); + +extern void rtnl_sfq_set_perturb(struct rtnl_qdisc *, int); +extern int rtnl_sfq_get_perturb(struct rtnl_qdisc *); + +extern int rtnl_sfq_get_divisor(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/tbf.h b/libnl/include/netlink/route/qdisc/tbf.h new file mode 100644 index 0000000..b6c4f3d --- /dev/null +++ b/libnl/include/netlink/route/qdisc/tbf.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +#ifndef NETLINK_TBF_H_ +#define NETLINK_TBF_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *); + +extern void rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *, int, int, int); +extern int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *); +extern int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *); +extern int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *); + +extern int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *, int, int, int); +extern int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *); +extern int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *); +extern int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/route.h b/libnl/include/netlink/route/route.h new file mode 100644 index 0000000..4377062 --- /dev/null +++ b/libnl/include/netlink/route/route.h @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef NETLINK_ROUTE_H_ +#define NETLINK_ROUTE_H_ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @ingroup route + * When passed to rtnl_route_alloc_cache() the cache will + * correspond to the contents of the routing cache instead + * of the actual routes. + */ +#define ROUTE_CACHE_CONTENT 1 + +struct rtnl_route; + +struct rtnl_rtcacheinfo +{ + uint32_t rtci_clntref; + uint32_t rtci_last_use; + uint32_t rtci_expires; + int32_t rtci_error; + uint32_t rtci_used; + uint32_t rtci_id; + uint32_t rtci_ts; + uint32_t rtci_tsage; +}; + +extern struct nl_object_ops route_obj_ops; + +extern struct rtnl_route * rtnl_route_alloc(void); +extern void rtnl_route_put(struct rtnl_route *); +extern int rtnl_route_alloc_cache(struct nl_sock *, int, int, + struct nl_cache **); + +extern void rtnl_route_get(struct rtnl_route *); + +extern int rtnl_route_parse(struct nlmsghdr *, struct rtnl_route **); +extern int rtnl_route_build_msg(struct nl_msg *, struct rtnl_route *); + +extern int rtnl_route_lookup(struct nl_sock *sk, struct nl_addr *dst, + struct rtnl_route **result); + +extern int rtnl_route_build_add_request(struct rtnl_route *, int, + struct nl_msg **); +extern int rtnl_route_add(struct nl_sock *, struct rtnl_route *, int); +extern int rtnl_route_build_del_request(struct rtnl_route *, int, + struct nl_msg **); +extern int rtnl_route_delete(struct nl_sock *, struct rtnl_route *, int); + +extern void rtnl_route_set_table(struct rtnl_route *, uint32_t); +extern uint32_t rtnl_route_get_table(struct rtnl_route *); +extern void rtnl_route_set_scope(struct rtnl_route *, uint8_t); +extern uint8_t rtnl_route_get_scope(struct rtnl_route *); +extern void rtnl_route_set_tos(struct rtnl_route *, uint8_t); +extern uint8_t rtnl_route_get_tos(struct rtnl_route *); +extern void rtnl_route_set_protocol(struct rtnl_route *, uint8_t); +extern uint8_t rtnl_route_get_protocol(struct rtnl_route *); +extern void rtnl_route_set_priority(struct rtnl_route *, uint32_t); +extern uint32_t rtnl_route_get_priority(struct rtnl_route *); +extern int rtnl_route_set_family(struct rtnl_route *, uint8_t); +extern uint8_t rtnl_route_get_family(struct rtnl_route *); +extern int rtnl_route_set_type(struct rtnl_route *, uint8_t); +extern uint8_t rtnl_route_get_type(struct rtnl_route *); +extern void rtnl_route_set_flags(struct rtnl_route *, uint32_t); +extern void rtnl_route_unset_flags(struct rtnl_route *, uint32_t); +extern uint32_t rtnl_route_get_flags(struct rtnl_route *); +extern int rtnl_route_set_metric(struct rtnl_route *, int, unsigned int); +extern int rtnl_route_unset_metric(struct rtnl_route *, int); +extern int rtnl_route_get_metric(struct rtnl_route *, int, uint32_t *); +extern int rtnl_route_set_dst(struct rtnl_route *, struct nl_addr *); +extern struct nl_addr *rtnl_route_get_dst(struct rtnl_route *); +extern int rtnl_route_set_src(struct rtnl_route *, struct nl_addr *); +extern struct nl_addr *rtnl_route_get_src(struct rtnl_route *); +extern int rtnl_route_set_pref_src(struct rtnl_route *, struct nl_addr *); +extern struct nl_addr *rtnl_route_get_pref_src(struct rtnl_route *); +extern void rtnl_route_set_iif(struct rtnl_route *, int); +extern int rtnl_route_get_iif(struct rtnl_route *); +extern int rtnl_route_get_src_len(struct rtnl_route *); +extern void rtnl_route_set_ttl_propagate(struct rtnl_route *route, + uint8_t ttl_prop); +extern int rtnl_route_get_ttl_propagate(struct rtnl_route *route); +extern void rtnl_route_set_nhid(struct rtnl_route *, uint32_t); +extern uint32_t rtnl_route_get_nhid(struct rtnl_route *); + +extern void rtnl_route_add_nexthop(struct rtnl_route *, + struct rtnl_nexthop *); +extern void rtnl_route_remove_nexthop(struct rtnl_route *, + struct rtnl_nexthop *); +extern struct nl_list_head *rtnl_route_get_nexthops(struct rtnl_route *); +extern int rtnl_route_get_nnexthops(struct rtnl_route *); + +extern void rtnl_route_foreach_nexthop(struct rtnl_route *r, + void (*cb)(struct rtnl_nexthop *, void *), + void *arg); + +extern struct rtnl_nexthop * rtnl_route_nexthop_n(struct rtnl_route *r, int n); + +extern int rtnl_route_guess_scope(struct rtnl_route *); + +extern char * rtnl_route_table2str(int, char *, size_t); +extern int rtnl_route_str2table(const char *); +extern int rtnl_route_read_table_names(const char *); + +extern char * rtnl_route_proto2str(int, char *, size_t); +extern int rtnl_route_str2proto(const char *); +extern int rtnl_route_read_protocol_names(const char *); + +extern char * rtnl_route_metric2str(int, char *, size_t); +extern int rtnl_route_str2metric(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/rtnl.h b/libnl/include/netlink/route/rtnl.h new file mode 100644 index 0000000..6cae88f --- /dev/null +++ b/libnl/include/netlink/route/rtnl.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +#ifndef NETLINK_RTNL_H_ +#define NETLINK_RTNL_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Realms + * @{ + */ + +/** + * Mask specying the size of each realm part + * @ingroup rtnl + */ +#define RTNL_REALM_MASK (0xFFFF) + +/** + * Extract FROM realm from a realms field + */ +#define RTNL_REALM_FROM(realm) ((realm) >> 16) + +/** + * Extract TO realm from a realms field + */ +#define RTNL_REALM_TO(realm) ((realm) & RTNL_REALM_MASK) + +/** + * Build a realms field + */ +#define RTNL_MAKE_REALM(from, to) \ + ((RTNL_REALM_TO(from) << 16) & RTNL_REALM_TO(to)) + +/** @} */ + + +/* General */ +extern int nl_rtgen_request(struct nl_sock *, int, int, int); + +/* Routing Type Translations */ +extern char * nl_rtntype2str(int, char *, size_t); +extern int nl_str2rtntype(const char *); + +/* Scope Translations */ +extern char * rtnl_scope2str(int, char *, size_t); +extern int rtnl_str2scope(const char *); + +/* Realms Translations */ +extern char * rtnl_realms2str(uint32_t, char *, size_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/rule.h b/libnl/include/netlink/route/rule.h new file mode 100644 index 0000000..26f5b52 --- /dev/null +++ b/libnl/include/netlink/route/rule.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2010 Thomas Graf + */ + +#ifndef NETLINK_RULE_H_ +#define NETLINK_RULE_H_ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_rule; + +/* General */ +extern struct rtnl_rule * rtnl_rule_alloc(void); +extern void rtnl_rule_put(struct rtnl_rule *); + +extern int rtnl_rule_alloc_cache(struct nl_sock *, int, + struct nl_cache **); +extern void rtnl_rule_dump(struct rtnl_rule *, FILE *, struct nl_dump_params *); + +extern int rtnl_rule_build_add_request(struct rtnl_rule *, int, + struct nl_msg **); +extern int rtnl_rule_add(struct nl_sock *, struct rtnl_rule *, int); +extern int rtnl_rule_build_delete_request(struct rtnl_rule *, int, + struct nl_msg **); +extern int rtnl_rule_delete(struct nl_sock *, struct rtnl_rule *, int); + + +/* attribute modification */ +extern void rtnl_rule_set_family(struct rtnl_rule *, int); +extern int rtnl_rule_get_family(struct rtnl_rule *); +extern void rtnl_rule_set_prio(struct rtnl_rule *, uint32_t); +extern uint32_t rtnl_rule_get_prio(struct rtnl_rule *); +extern void rtnl_rule_set_mark(struct rtnl_rule *, uint32_t); +extern uint32_t rtnl_rule_get_mark(struct rtnl_rule *); +extern void rtnl_rule_set_mask(struct rtnl_rule *, uint32_t); +extern uint32_t rtnl_rule_get_mask(struct rtnl_rule *); +extern void rtnl_rule_set_table(struct rtnl_rule *, uint32_t); +extern uint32_t rtnl_rule_get_table(struct rtnl_rule *); +extern void rtnl_rule_set_dsfield(struct rtnl_rule *, uint8_t); +extern uint8_t rtnl_rule_get_dsfield(struct rtnl_rule *); +extern int rtnl_rule_set_src(struct rtnl_rule *, struct nl_addr *); +extern struct nl_addr * rtnl_rule_get_src(struct rtnl_rule *); +extern int rtnl_rule_set_dst(struct rtnl_rule *, struct nl_addr *); +extern struct nl_addr * rtnl_rule_get_dst(struct rtnl_rule *); +extern void rtnl_rule_set_action(struct rtnl_rule *, uint8_t); +extern uint8_t rtnl_rule_get_action(struct rtnl_rule *); +extern int rtnl_rule_set_iif(struct rtnl_rule *, const char *); +extern char * rtnl_rule_get_iif(struct rtnl_rule *); +extern int rtnl_rule_set_oif(struct rtnl_rule *, const char *); +extern char * rtnl_rule_get_oif(struct rtnl_rule *); +extern void rtnl_rule_set_realms(struct rtnl_rule *, uint32_t); +extern uint32_t rtnl_rule_get_realms(struct rtnl_rule *); +extern void rtnl_rule_set_goto(struct rtnl_rule *, uint32_t); +extern uint32_t rtnl_rule_get_goto(struct rtnl_rule *); +extern void rtnl_rule_set_l3mdev(struct rtnl_rule *, int); +extern int rtnl_rule_get_l3mdev(struct rtnl_rule *); +extern int rtnl_rule_set_protocol(struct rtnl_rule *, uint8_t); +extern int rtnl_rule_get_protocol(struct rtnl_rule *, uint8_t *); +extern int rtnl_rule_set_ipproto(struct rtnl_rule *, uint8_t); +extern int rtnl_rule_get_ipproto(struct rtnl_rule *, uint8_t *); +extern int rtnl_rule_set_sport(struct rtnl_rule *, uint16_t start); +extern int rtnl_rule_set_sport_range(struct rtnl_rule *, + uint16_t start, + uint16_t end); +extern int rtnl_rule_get_sport(struct rtnl_rule *, uint16_t *start, + uint16_t *end); +extern int rtnl_rule_set_dport(struct rtnl_rule *, uint16_t start); +extern int rtnl_rule_set_dport_range(struct rtnl_rule *, + uint16_t start, + uint16_t end); +extern int rtnl_rule_get_dport(struct rtnl_rule *, uint16_t *start, + uint16_t *end); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/tc-api.h b/libnl/include/netlink/route/tc-api.h new file mode 100644 index 0000000..36b1b0f --- /dev/null +++ b/libnl/include/netlink/route/tc-api.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Thomas Graf + */ + +#ifndef NETLINK_DUMMY_TC_API_H_ +#define NETLINK_DUMMY_TC_API_H_ + +#include +#include +#include + +#ifndef _NL_NO_WARN_DEPRECATED_HEADER +#warning "You are including a deprecated header file, include ." +#endif + +#endif diff --git a/libnl/include/netlink/route/tc.h b/libnl/include/netlink/route/tc.h new file mode 100644 index 0000000..ee55555 --- /dev/null +++ b/libnl/include/netlink/route/tc.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +#ifndef NETLINK_TC_H_ +#define NETLINK_TC_H_ + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum rtnl_tc_type { + RTNL_TC_TYPE_QDISC, + RTNL_TC_TYPE_CLASS, + RTNL_TC_TYPE_CLS, + RTNL_TC_TYPE_ACT, + __RTNL_TC_TYPE_MAX, +}; + +#define RTNL_TC_TYPE_MAX (__RTNL_TC_TYPE_MAX - 1) + +/** + * Compute tc handle based on major and minor parts + * @ingroup tc + */ +#define TC_HANDLE(maj, min) (TC_H_MAJ((maj) << 16) | TC_H_MIN(min)) + +/** + * Traffic control object + * @ingroup tc + */ +struct rtnl_tc; + +/** + * Macro to cast qdisc/class/classifier to tc object + * @ingroup tc + * + * @code + * rtnl_tc_set_mpu(TC_CAST(qdisc), 40); + * @endcode + */ +#define TC_CAST(ptr) ((struct rtnl_tc *) (ptr)) + +/** + * Traffic control statistical identifier + * @ingroup tc + * + * @code + * uint64_t n = rtnl_tc_get_stat(TC_CAST(class), RTNL_TC_PACKETS); + * @endcode + */ +enum rtnl_tc_stat { + RTNL_TC_PACKETS, /**< Number of packets seen */ + RTNL_TC_BYTES, /**< Total bytes seen */ + RTNL_TC_RATE_BPS, /**< Current bits/s (rate estimator) */ + RTNL_TC_RATE_PPS, /**< Current packet/s (rate estimator) */ + RTNL_TC_QLEN, /**< Current queue length */ + RTNL_TC_BACKLOG, /**< Current backlog length */ + RTNL_TC_DROPS, /**< Total number of packets dropped */ + RTNL_TC_REQUEUES, /**< Total number of requeues */ + RTNL_TC_OVERLIMITS, /**< Total number of overlimits */ + __RTNL_TC_STATS_MAX, +}; + +#define RTNL_TC_STATS_MAX (__RTNL_TC_STATS_MAX - 1) + +extern void rtnl_tc_set_ifindex(struct rtnl_tc *, int); +extern int rtnl_tc_get_ifindex(struct rtnl_tc *); +extern void rtnl_tc_set_link(struct rtnl_tc *, struct rtnl_link *); +extern struct rtnl_link *rtnl_tc_get_link(struct rtnl_tc *); +extern void rtnl_tc_set_mtu(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_mtu(struct rtnl_tc *); +extern void rtnl_tc_set_mpu(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_mpu(struct rtnl_tc *); +extern void rtnl_tc_set_overhead(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_overhead(struct rtnl_tc *); +extern void rtnl_tc_set_linktype(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_linktype(struct rtnl_tc *); +extern void rtnl_tc_set_handle(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_handle(struct rtnl_tc *); +extern void rtnl_tc_set_parent(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_parent(struct rtnl_tc *); +extern int rtnl_tc_set_kind(struct rtnl_tc *, const char *); +extern char * rtnl_tc_get_kind(struct rtnl_tc *); +extern uint64_t rtnl_tc_get_stat(struct rtnl_tc *, enum rtnl_tc_stat); +extern char * rtnl_tc_stat2str(enum rtnl_tc_stat, char *, size_t); +extern int rtnl_tc_str2stat(const char *); + +extern int rtnl_tc_calc_txtime(int, int); +extern int rtnl_tc_calc_bufsize(int, int); +extern int rtnl_tc_calc_cell_log(int); + +extern int rtnl_tc_read_classid_file(void); +extern char * rtnl_tc_handle2str(uint32_t, char *, size_t); +extern int rtnl_tc_str2handle(const char *, uint32_t *); +extern int rtnl_classid_generate(const char *, uint32_t *, + uint32_t); +extern void rtnl_tc_set_chain(struct rtnl_tc *, uint32_t); +extern int rtnl_tc_get_chain(struct rtnl_tc *, uint32_t *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/socket.h b/libnl/include/netlink/socket.h new file mode 100644 index 0000000..2bd98c3 --- /dev/null +++ b/libnl/include/netlink/socket.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +#ifndef NETLINK_SOCKET_H_ +#define NETLINK_SOCKET_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct nl_sock * nl_socket_alloc(void); +extern struct nl_sock * nl_socket_alloc_cb(struct nl_cb *); +extern void nl_socket_free(struct nl_sock *); + +extern uint32_t nl_socket_get_local_port(const struct nl_sock *); +extern void nl_socket_set_local_port(struct nl_sock *, uint32_t); + +extern int nl_socket_add_memberships(struct nl_sock *, int, ...); +extern int nl_socket_add_membership(struct nl_sock *, int); +extern int nl_socket_drop_memberships(struct nl_sock *, int, ...); +extern int nl_socket_drop_membership(struct nl_sock *, + int); +extern void nl_join_groups(struct nl_sock *, int); + + +extern uint32_t nl_socket_get_peer_port(const struct nl_sock *); +extern void nl_socket_set_peer_port(struct nl_sock *, + uint32_t); +extern uint32_t nl_socket_get_peer_groups(const struct nl_sock *sk); +extern void nl_socket_set_peer_groups(struct nl_sock *sk, uint32_t groups); +extern struct nl_cb * nl_socket_get_cb(const struct nl_sock *); +extern void nl_socket_set_cb(struct nl_sock *, + struct nl_cb *); +extern int nl_socket_modify_cb(struct nl_sock *, enum nl_cb_type, + enum nl_cb_kind, + nl_recvmsg_msg_cb_t, void *); +extern int nl_socket_modify_err_cb(struct nl_sock *, enum nl_cb_kind, + nl_recvmsg_err_cb_t, void *); + +extern int nl_socket_set_buffer_size(struct nl_sock *, int, int); +extern int nl_socket_set_msg_buf_size(struct nl_sock *, size_t); +extern size_t nl_socket_get_msg_buf_size(struct nl_sock *); +extern int nl_socket_set_passcred(struct nl_sock *, int); +extern int nl_socket_recv_pktinfo(struct nl_sock *, int); + +extern void nl_socket_disable_seq_check(struct nl_sock *); +extern unsigned int nl_socket_use_seq(struct nl_sock *); +extern void nl_socket_disable_auto_ack(struct nl_sock *); +extern void nl_socket_enable_auto_ack(struct nl_sock *); + +extern int nl_socket_get_fd(const struct nl_sock *); +extern int nl_socket_set_fd(struct nl_sock *sk, int protocol, int fd); +extern int nl_socket_set_nonblocking(const struct nl_sock *); +extern void nl_socket_enable_msg_peek(struct nl_sock *); +extern void nl_socket_disable_msg_peek(struct nl_sock *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/types.h b/libnl/include/netlink/types.h new file mode 100644 index 0000000..a3a28fd --- /dev/null +++ b/libnl/include/netlink/types.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef __NETLINK_TYPES_H_ +#define __NETLINK_TYPES_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @ingroup utils + * Enumeration of dumping variations (dp_type) + */ +enum nl_dump_type { + NL_DUMP_LINE, /**< Dump object briefly on one line */ + NL_DUMP_DETAILS, /**< Dump all attributes but no statistics */ + NL_DUMP_STATS, /**< Dump all attributes including statistics */ + __NL_DUMP_MAX, +}; +#define NL_DUMP_MAX (__NL_DUMP_MAX - 1) + +/** + * @ingroup utils + * Dumping parameters + */ +struct nl_dump_params +{ + /** + * Specifies the type of dump that is requested. + */ + enum nl_dump_type dp_type; + + /** + * Specifies the number of whitespaces to be put in front + * of every new line (indentation). + */ + int dp_prefix; + + /** + * Causes the cache index to be printed for each element. + */ + int dp_print_index; + + /** + * Causes each element to be prefixed with the message type. + */ + int dp_dump_msgtype; + + /** + * A callback invoked for output + * + * Passed arguments are: + * - dumping parameters + * - string to append to the output + */ + void (*dp_cb)(struct nl_dump_params *, char *); + + /** + * A callback invoked for every new line, can be used to + * customize the indentation. + * + * Passed arguments are: + * - dumping parameters + * - line number starting from 0 + */ + void (*dp_nl_cb)(struct nl_dump_params *, int); + + /** + * User data pointer, can be used to pass data to callbacks. + */ + void *dp_data; + + /** + * File descriptor the dumping output should go to + */ + FILE * dp_fd; + + /** + * Alternatively the output may be redirected into a buffer + */ + char * dp_buf; + + /** + * Length of the buffer dp_buf + */ + size_t dp_buflen; + + /** + * PRIVATE + * Set if a dump was performed prior to the actual dump handler. + */ + int dp_pre_dump; + + /** + * PRIVATE + * Owned by the current caller + */ + int dp_ivar; + + unsigned int dp_line; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/utils.h b/libnl/include/netlink/utils.h new file mode 100644 index 0000000..9085c39 --- /dev/null +++ b/libnl/include/netlink/utils.h @@ -0,0 +1,380 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef NETLINK_UTILS_H_ +#define NETLINK_UTILS_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__GNUC__) && __GNUC__ > 5 +#define _nl_attribute_printf(a, b) __attribute__((__format__(printf, a, b))) +#else +#define _nl_attribute_printf(a, b) +#endif + +/** + * @name Probability Constants + * @{ + */ + +/** + * Lower probability limit + * @ingroup utils + */ +#define NL_PROB_MIN 0x0 + +/** + * Upper probability limit nl_dump_type + * @ingroup utils + */ +#define NL_PROB_MAX 0xffffffff + +/** @} */ + +enum { + NL_BYTE_RATE, + NL_BIT_RATE, +}; + +/* unit pretty-printing */ +extern double nl_cancel_down_bytes(unsigned long long, char **); +extern double nl_cancel_down_bits(unsigned long long, char **); +extern int nl_rate2str(unsigned long long, int, char *, size_t); +extern double nl_cancel_down_us(uint32_t, char **); + +/* generic unit translations */ +extern long nl_size2int(const char *); +extern char * nl_size2str(const size_t, char *, const size_t); +extern long nl_prob2int(const char *); + +/* time translations */ +extern int nl_get_user_hz(void); +extern int nl_get_psched_hz(void); +extern uint32_t nl_us2ticks(uint32_t); +extern uint32_t nl_ticks2us(uint32_t); +extern int nl_str2msec(const char *, uint64_t *); +extern char * nl_msec2str(uint64_t, char *, size_t); + +/* link layer protocol translations */ +extern char * nl_llproto2str(int, char *, size_t); +extern int nl_str2llproto(const char *); + +/* ethernet protocol translations */ +extern char * nl_ether_proto2str(int, char *, size_t); +extern int nl_str2ether_proto(const char *); + +/* IP protocol translations */ +extern char * nl_ip_proto2str(int, char *, size_t); +extern int nl_str2ip_proto(const char *); + +/* Dumping helpers */ +extern void nl_new_line(struct nl_dump_params *); +extern void nl_dump(struct nl_dump_params *, const char *, ...) _nl_attribute_printf(2, 3); +extern void nl_dump_line(struct nl_dump_params *, const char *, ...) _nl_attribute_printf(2, 3); + +enum { + NL_CAPABILITY_NONE, + + /** + * rtnl_route_build_msg() no longer guesses the route scope + * if explicitly set to RT_SCOPE_NOWHERE. + * @ingroup utils + */ + NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE = 1, +#define NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE + + /** + * rtnl_link_veth_get_peer() now returns a reference that is owned by the + * caller and must be released by the caller with rtnl_link_put(). + */ + NL_CAPABILITY_ROUTE_LINK_VETH_GET_PEER_OWN_REFERENCE = 2, +#define NL_CAPABILITY_ROUTE_LINK_VETH_GET_PEER_OWN_REFERENCE NL_CAPABILITY_ROUTE_LINK_VETH_GET_PEER_OWN_REFERENCE + + /** + * rtnl_u32_add_action() and rtnl_basic_add_action() now grab a reference to act + * caller are free to release its own + */ + NL_CAPABILITY_ROUTE_LINK_CLS_ADD_ACT_OWN_REFERENCE = 3, +#define NL_CAPABILITY_ROUTE_LINK_CLS_ADD_ACT_OWN_REFERENCE NL_CAPABILITY_ROUTE_LINK_CLS_ADD_ACT_OWN_REFERENCE + + /** + * Indicate that the local port is unspecified until the user accesses + * it (via nl_socket_get_local_port()) or until nl_connect(). More importantly, + * if the port is left unspecified, nl_connect() will retry generating another + * port when bind() fails with ADDRINUSE. + */ + NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE = 4, +#define NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE + + /** + * Indicate that rtnl_link_get_kernel() fails with -NLE_OPNOTSUPP in case + * of older kernals not supporting lookup by ifname. This changes behavior + * from returning -NLE_INVAL to return -NLE_OPNOTSUPP. + */ + NL_CAPABILITY_ROUTE_LINK_GET_KERNEL_FAIL_OPNOTSUPP = 5, +#define NL_CAPABILITY_ROUTE_LINK_GET_KERNEL_FAIL_OPNOTSUPP NL_CAPABILITY_ROUTE_LINK_GET_KERNEL_FAIL_OPNOTSUPP + + /** + * Also consider the a_cacheinfo field (ADDR_ATTR_CACHEINFO) that contains the + * address timestamps and expiry when comparing struct rtnl_addr objects with + * nl_object_diff(). + */ + NL_CAPABILITY_ROUTE_ADDR_COMPARE_CACHEINFO = 6, +#define NL_CAPABILITY_ROUTE_ADDR_COMPARE_CACHEINFO NL_CAPABILITY_ROUTE_ADDR_COMPARE_CACHEINFO + + /** + * The library version is libnl3 3.2.26 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_2_26 = 7, +#define NL_CAPABILITY_VERSION_3_2_26 NL_CAPABILITY_VERSION_3_2_26 + + /** + * nl_recv() fails with NLE_MSG_TRUNC if a message got truncated + * with NL_MSG_PEEK disabled. Previously, the failed message was wrongly + * discarded and the next message received. + */ + NL_CAPABILITY_NL_RECV_FAIL_TRUNC_NO_PEEK = 8, +#define NL_CAPABILITY_NL_RECV_FAIL_TRUNC_NO_PEEK NL_CAPABILITY_NL_RECV_FAIL_TRUNC_NO_PEEK + + /** + * rtnl_link_build_change_request() and rtnl_link_change() would set ifi.ifi_flags but leave + * ifi.ifi_change at zero. This was later fixed to set ifi.ifi_change to the flags that are actually + * set in changes. + */ + NL_CAPABILITY_LINK_BUILD_CHANGE_REQUEST_SET_CHANGE = 9, +#define NL_CAPABILITY_LINK_BUILD_CHANGE_REQUEST_SET_CHANGE NL_CAPABILITY_LINK_BUILD_CHANGE_REQUEST_SET_CHANGE + + /** + * Between 3.2.14 (64fcb47a36ec12d7e7f00605f6a8952ce985dd08) and 3.2.22 (8571f58f23763d8db7365d02c9b27832ad3d7005), + * rtnl_neigh_get() behaved differently and only returned objects with family AF_UNSPEC. + * This capability indicates, that the function was fixed. The absense of the capability, + * doesn't indicate however which behavior the function will have. So beware. */ + NL_CAPABILITY_RTNL_NEIGH_GET_FILTER_AF_UNSPEC_FIX = 10, +#define NL_CAPABILITY_RTNL_NEIGH_GET_FILTER_AF_UNSPEC_FIX NL_CAPABILITY_RTNL_NEIGH_GET_FILTER_AF_UNSPEC_FIX + + /** + * The library version is libnl3 3.2.27 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_2_27 = 11, +#define NL_CAPABILITY_VERSION_3_2_27 NL_CAPABILITY_VERSION_3_2_27 + + /** + * Properly serialize vlan protocol IFLA_VLAN_PROTOCOL. + */ + NL_CAPABILITY_RTNL_LINK_VLAN_PROTOCOL_SERIALZE = 12, +#define NL_CAPABILITY_RTNL_LINK_VLAN_PROTOCOL_SERIALZE NL_CAPABILITY_RTNL_LINK_VLAN_PROTOCOL_SERIALZE + + /** + * Properly read gre REMOTE port. + */ + NL_CAPABILITY_RTNL_LINK_PARSE_GRE_REMOTE = 13, +#define NL_CAPABILITY_RTNL_LINK_PARSE_GRE_REMOTE NL_CAPABILITY_RTNL_LINK_PARSE_GRE_REMOTE + + /** + * Don't skip over vlan ingress-map entries with "to" field zero when serializing + * a netlink message. Previously such entires would be ignored which inhibits the + * user from clearing ingress map entries. + */ + NL_CAPABILITY_RTNL_LINK_VLAN_INGRESS_MAP_CLEAR = 14, +#define NL_CAPABILITY_RTNL_LINK_VLAN_INGRESS_MAP_CLEAR NL_CAPABILITY_RTNL_LINK_VLAN_INGRESS_MAP_CLEAR + + /** + * Consider vxlan link info for nl_object_diff(). + */ + NL_CAPABILITY_RTNL_LINK_VXLAN_IO_COMPARE = 15, +#define NL_CAPABILITY_RTNL_LINK_VXLAN_IO_COMPARE NL_CAPABILITY_RTNL_LINK_VXLAN_IO_COMPARE + + /** + * Support 64 bit attributes for nl_object_diff(). + */ + NL_CAPABILITY_NL_OBJECT_DIFF64 = 16, +#define NL_CAPABILITY_NL_OBJECT_DIFF64 NL_CAPABILITY_NL_OBJECT_DIFF64 + + /** + * Support omitting @key argument to xfrmnl_sa_get_*_params() to check + * for required buffer size for key. + */ + NL_CAPABILITY_XFRM_SA_KEY_SIZE = 17, +#define NL_CAPABILITY_XFRM_SA_KEY_SIZE NL_CAPABILITY_XFRM_SA_KEY_SIZE + + /** + * Properly handle nl_object_identity() for AF_INET and AF_INET6 addresses + * and properly handle the peer/IFA_ADDRESS for IPv4 addresses. + */ + NL_CAPABILITY_RTNL_ADDR_PEER_FIX = 18, +#define NL_CAPABILITY_RTNL_ADDR_PEER_FIX NL_CAPABILITY_RTNL_ADDR_PEER_FIX + + /** + * The library version is libnl3 3.2.28 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_2_28 = 19, +#define NL_CAPABILITY_VERSION_3_2_28 NL_CAPABILITY_VERSION_3_2_28 + + /** + * After NL_CAPABILITY_RTNL_ADDR_PEER_FIX, a follow up regression to lookup + * IPv4 addresses in the cache was fixed (PR#105). + */ + NL_CAPABILITY_RTNL_ADDR_PEER_ID_FIX = 20, +#define NL_CAPABILITY_RTNL_ADDR_PEER_ID_FIX NL_CAPABILITY_RTNL_ADDR_PEER_ID_FIX + + /** + * nl_addr_fill_sockaddr() properly checks that the provided address to + * avoid read-out-of-bounds for invalid addresses. + */ + NL_CAPABILITY_NL_ADDR_FILL_SOCKADDR = 21, +#define NL_CAPABILITY_NL_ADDR_FILL_SOCKADDR NL_CAPABILITY_NL_ADDR_FILL_SOCKADDR + + /** + * Support omitting @ctx_str argument to xfrmnl_sa_get_sec_ctx() to check + * for required buffer size for context string. + */ + NL_CAPABILITY_XFRM_SEC_CTX_LEN = 22, +#define NL_CAPABILITY_XFRM_SEC_CTX_LEN NL_CAPABILITY_XFRM_SEC_CTX_LEN + + /** + * rtnl_link_build_add_request() would set ifi.ifi_flags but leave ifi.ifi_change at zero. + * This was later fixed to set ifi.ifi_change to the flags that are actually + * set + */ + NL_CAPABILITY_LINK_BUILD_ADD_REQUEST_SET_CHANGE = 23, +#define NL_CAPABILITY_LINK_BUILD_ADD_REQUEST_SET_CHANGE NL_CAPABILITY_LINK_BUILD_ADD_REQUEST_SET_CHANGE + + /* Older versions of libnl3 would not use MSG_PEEK for nl_recvmsgs() unless calling + * nl_socket_enable_msg_peek(). Instead, the user had to specify the buffer size via + * nl_socket_set_msg_buf_size(), which in turn would default to 4*getpagesize(). + * + * The default value might not be large enough, so users who were not aware of the + * problem easily ended up using a too small receive buffer. Usually, one wants to + * avoid MSG_PEEK for recvmsg() because it requires an additional syscall. + * + * Now, as indicated by this capability, nl_recvmsgs() would use MSG_PEEK by default. The + * user still can explicitly disable MSG_PEEK by calling nl_socket_disable_msg_peek() or + * by setting the nl_socket_set_msg_buf_size() to a non-zero value. + */ + NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT = 24, +#define NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT + + /** + * The library version is libnl3 3.2.29 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_2_29 = 25, +#define NL_CAPABILITY_VERSION_3_2_29 NL_CAPABILITY_VERSION_3_2_29 + + /** + * Support omitting @ctx_str argument to xfrmnl_sp_get_sec_ctx() to check + * for required buffer size for context string. + */ + NL_CAPABILITY_XFRM_SP_SEC_CTX_LEN = 26, +#define NL_CAPABILITY_XFRM_SP_SEC_CTX_LEN NL_CAPABILITY_XFRM_SP_SEC_CTX_LEN + + /** + * The library version is libnl3 3.3.0 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_3_0 = 27, +#define NL_CAPABILITY_VERSION_3_3_0 NL_CAPABILITY_VERSION_3_3_0 + + /** + * The library version is libnl3 3.4.0 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_4_0 = 28, +#define NL_CAPABILITY_VERSION_3_4_0 NL_CAPABILITY_VERSION_3_4_0 + + /** + * Fixed memory corruption in rtnl_link_vlan_set_egress_map(). Previously, if you tried + * to add more then 4 mappings, a buffer overflow occured. Also fixed nl_object_clone() + * for VLAN links. + */ + NL_CAPABILITY_ROUTE_FIX_VLAN_SET_EGRESS_MAP = 29, +#define NL_CAPABILITY_ROUTE_FIX_VLAN_SET_EGRESS_MAP NL_CAPABILITY_ROUTE_FIX_VLAN_SET_EGRESS_MAP + + /** + * The library version is libnl3 3.5.0 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_5_0 = 30, +#define NL_CAPABILITY_VERSION_3_5_0 NL_CAPABILITY_VERSION_3_5_0 + + /** + * nl_object_identical() can consider objects identical, if they both lack the same + * set of ID attributes. + */ + NL_CAPABILITY_NL_OBJECT_IDENTICAL_PARTIAL = 31, +#define NL_CAPABILITY_NL_OBJECT_IDENTICAL_PARTIAL NL_CAPABILITY_NL_OBJECT_IDENTICAL_PARTIAL + + /** + * The library version is libnl3 3.6.0 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_6_0 = 32, +#define NL_CAPABILITY_VERSION_3_6_0 NL_CAPABILITY_VERSION_3_6_0 + + /** + * The library version is libnl3 3.7.0 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_7_0 = 33, +#define NL_CAPABILITY_VERSION_3_7_0 NL_CAPABILITY_VERSION_3_7_0 + + /** + * The library version is libnl3 3.8.0 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_8_0 = 34, +#define NL_CAPABILITY_VERSION_3_8_0 NL_CAPABILITY_VERSION_3_8_0 + + /** + * The library version is libnl3 3.9.0 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_9_0 = 35, +#define NL_CAPABILITY_VERSION_3_9_0 NL_CAPABILITY_VERSION_3_9_0 + + /** + * The library version is libnl3 3.10.0 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_10_0 = 36, +#define NL_CAPABILITY_VERSION_3_10_0 NL_CAPABILITY_VERSION_3_10_0 + + /** + * The library version is libnl3 3.11.0 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_11_0 = 37, +#define NL_CAPABILITY_VERSION_3_11_0 NL_CAPABILITY_VERSION_3_11_0 + + /** + * The library version is libnl3 3.12.0 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_12_0 = 38, +#define NL_CAPABILITY_VERSION_3_12_0 NL_CAPABILITY_VERSION_3_12_0 + + /** + * The library version is libnl3 3.13.0 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_13_0 = 39, +#define NL_CAPABILITY_VERSION_3_13_0 NL_CAPABILITY_VERSION_3_13_0 + + /** + * The library version is libnl3 3.14.0 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_14_0 = 40, +#define NL_CAPABILITY_VERSION_3_14_0 NL_CAPABILITY_VERSION_3_14_0 + + __NL_CAPABILITY_MAX, + NL_CAPABILITY_MAX = (__NL_CAPABILITY_MAX - 1), +#define NL_CAPABILITY_MAX NL_CAPABILITY_MAX + + /** + * The range 0x7000 to 0x7FFF is reserved for private capabilities. Upstream libnl3 will + * not register capabilities in this range. However, instead of adding private capabilities, + * better register their number with upstream libnl3. */ +#define NL_CAPABILITY_IS_USER_RESERVED(cap) ( ((cap) & ~0x0FFF) == 0x7000 ) +}; +int nl_has_capability (int capability); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/version.h.in b/libnl/include/netlink/version.h.in new file mode 100644 index 0000000..3c43355 --- /dev/null +++ b/libnl/include/netlink/version.h.in @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2011 Thomas Graf + */ + +#ifndef NETLINK_VERSION_H_ +#define NETLINK_VERSION_H_ + +/* Compile Time Versioning Information */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define LIBNL_STRING "@PACKAGE_STRING@" +#define LIBNL_VERSION "@PACKAGE_VERSION@" + +#define LIBNL_VER_MAJ @MAJ_VERSION@ +#define LIBNL_VER_MIN @MIN_VERSION@ +#define LIBNL_VER_MIC @MIC_VERSION@ +#define LIBNL_VER(maj,min) ((maj) << 8 | (min)) +#define LIBNL_VER_NUM LIBNL_VER(LIBNL_VER_MAJ, LIBNL_VER_MIN) + +#define LIBNL_CURRENT @LT_CURRENT@ +#define LIBNL_REVISION @LT_REVISION@ +#define LIBNL_AGE @LT_AGE@ + +/* Run-time version information */ + +extern const int nl_ver_num; +extern const int nl_ver_maj; +extern const int nl_ver_min; +extern const int nl_ver_mic; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/xfrm/ae.h b/libnl/include/netlink/xfrm/ae.h new file mode 100644 index 0000000..95112dd --- /dev/null +++ b/libnl/include/netlink/xfrm/ae.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * Neither the name of Texas Instruments Incorporated 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 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 NETLINK_XFRM_AE_H_ +#define NETLINK_XFRM_AE_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct xfrmnl_ae; + +extern struct xfrmnl_ae*xfrmnl_ae_alloc(void); +extern void xfrmnl_ae_put(struct xfrmnl_ae *); + +extern int xfrmnl_ae_get_kernel(struct nl_sock*, struct nl_addr*, unsigned int, unsigned int, + unsigned int, unsigned int, struct xfrmnl_ae**); +extern int xfrmnl_ae_set(struct nl_sock*, struct xfrmnl_ae*, int); + +extern int xfrmnl_ae_parse(struct nlmsghdr*, struct xfrmnl_ae **); +extern int xfrmnl_ae_build_get_request(struct nl_addr*, unsigned int, unsigned int, + unsigned int, unsigned int, struct nl_msg **); + +extern struct nl_addr* xfrmnl_ae_get_daddr (struct xfrmnl_ae*); +extern int xfrmnl_ae_set_daddr (struct xfrmnl_ae*, struct nl_addr*); + +extern int xfrmnl_ae_get_spi (struct xfrmnl_ae*); +extern int xfrmnl_ae_set_spi (struct xfrmnl_ae*, unsigned int); + +extern int xfrmnl_ae_get_family (struct xfrmnl_ae*); +extern int xfrmnl_ae_set_family (struct xfrmnl_ae*, unsigned int); + +extern int xfrmnl_ae_get_proto (struct xfrmnl_ae*); +extern int xfrmnl_ae_set_proto (struct xfrmnl_ae*, unsigned int); + +extern struct nl_addr* xfrmnl_ae_get_saddr (struct xfrmnl_ae*); +extern int xfrmnl_ae_set_saddr (struct xfrmnl_ae*, struct nl_addr*); + +extern int xfrmnl_ae_get_flags (struct xfrmnl_ae*); +extern int xfrmnl_ae_set_flags (struct xfrmnl_ae*, unsigned int); + +extern int xfrmnl_ae_get_reqid (struct xfrmnl_ae*); +extern int xfrmnl_ae_set_reqid (struct xfrmnl_ae*, unsigned int); + +extern int xfrmnl_ae_get_mark (struct xfrmnl_ae*, unsigned int*, unsigned int*); +extern int xfrmnl_ae_set_mark (struct xfrmnl_ae*, unsigned int, unsigned int); + +extern int xfrmnl_ae_get_curlifetime (struct xfrmnl_ae*, unsigned long long int*, + unsigned long long int*, unsigned long long int*, + unsigned long long int*); +extern int xfrmnl_ae_set_curlifetime (struct xfrmnl_ae*, unsigned long long int, + unsigned long long int, unsigned long long int, + unsigned long long int); + +extern int xfrmnl_ae_get_replay_maxage (struct xfrmnl_ae*); +extern int xfrmnl_ae_set_replay_maxage (struct xfrmnl_ae*, unsigned int); + +extern int xfrmnl_ae_get_replay_maxdiff (struct xfrmnl_ae*); +extern int xfrmnl_ae_set_replay_maxdiff (struct xfrmnl_ae*, unsigned int); + +extern int xfrmnl_ae_get_replay_state (struct xfrmnl_ae*, unsigned int*, unsigned int*, unsigned int*); +extern int xfrmnl_ae_set_replay_state (struct xfrmnl_ae*, unsigned int, unsigned int, unsigned int); + +extern int xfrmnl_ae_get_replay_state_esn (struct xfrmnl_ae*, unsigned int*, unsigned int*, unsigned int*, + unsigned int*, unsigned int*, unsigned int*, unsigned int*); +extern int xfrmnl_ae_set_replay_state_esn (struct xfrmnl_ae*, unsigned int, unsigned int, unsigned int, + unsigned int, unsigned int, unsigned int, unsigned int*); + +extern char* xfrmnl_ae_flags2str(int, char *, size_t); +extern int xfrmnl_ae_str2flag(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/xfrm/lifetime.h b/libnl/include/netlink/xfrm/lifetime.h new file mode 100644 index 0000000..a5d5955 --- /dev/null +++ b/libnl/include/netlink/xfrm/lifetime.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * Neither the name of Texas Instruments Incorporated 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 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 NETLINK_XFRM_LTIME_H_ +#define NETLINK_XFRM_LTIME_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct xfrmnl_ltime_cfg; + +/* Creation */ +extern struct xfrmnl_ltime_cfg* xfrmnl_ltime_cfg_alloc(void); +extern struct xfrmnl_ltime_cfg* xfrmnl_ltime_cfg_clone(struct xfrmnl_ltime_cfg*); + +/* Usage Management */ +extern struct xfrmnl_ltime_cfg* xfrmnl_ltime_cfg_get(struct xfrmnl_ltime_cfg*); +extern void xfrmnl_ltime_cfg_put(struct xfrmnl_ltime_cfg*); +extern int xfrmnl_ltime_cfg_shared(struct xfrmnl_ltime_cfg*); +extern int xfrmnl_ltime_cfg_cmp(struct xfrmnl_ltime_cfg*, struct xfrmnl_ltime_cfg*); + +/* Access Functions */ +extern unsigned long long xfrmnl_ltime_cfg_get_soft_bytelimit (struct xfrmnl_ltime_cfg*); +extern int xfrmnl_ltime_cfg_set_soft_bytelimit (struct xfrmnl_ltime_cfg*, unsigned long long); + +extern unsigned long long xfrmnl_ltime_cfg_get_hard_bytelimit (struct xfrmnl_ltime_cfg*); +extern int xfrmnl_ltime_cfg_set_hard_bytelimit (struct xfrmnl_ltime_cfg*, unsigned long long); + +extern unsigned long long xfrmnl_ltime_cfg_get_soft_packetlimit (struct xfrmnl_ltime_cfg*); +extern int xfrmnl_ltime_cfg_set_soft_packetlimit (struct xfrmnl_ltime_cfg*, unsigned long long); + +extern unsigned long long xfrmnl_ltime_cfg_get_hard_packetlimit (struct xfrmnl_ltime_cfg*); +extern int xfrmnl_ltime_cfg_set_hard_packetlimit (struct xfrmnl_ltime_cfg*, unsigned long long); + +extern unsigned long long xfrmnl_ltime_cfg_get_soft_addexpires (struct xfrmnl_ltime_cfg*); +extern int xfrmnl_ltime_cfg_set_soft_addexpires (struct xfrmnl_ltime_cfg*, unsigned long long); + +extern unsigned long long xfrmnl_ltime_cfg_get_hard_addexpires (struct xfrmnl_ltime_cfg*); +extern int xfrmnl_ltime_cfg_set_hard_addexpires (struct xfrmnl_ltime_cfg*, unsigned long long); + +extern unsigned long long xfrmnl_ltime_cfg_get_soft_useexpires (struct xfrmnl_ltime_cfg*); +extern int xfrmnl_ltime_cfg_set_soft_useexpires (struct xfrmnl_ltime_cfg*, unsigned long long); + +extern unsigned long long xfrmnl_ltime_cfg_get_hard_useexpires (struct xfrmnl_ltime_cfg*); +extern int xfrmnl_ltime_cfg_set_hard_useexpires (struct xfrmnl_ltime_cfg*, unsigned long long); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/xfrm/sa.h b/libnl/include/netlink/xfrm/sa.h new file mode 100644 index 0000000..cd5e552 --- /dev/null +++ b/libnl/include/netlink/xfrm/sa.h @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * Neither the name of Texas Instruments Incorporated 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 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 NETLINK_XFRM_SA_H_ +#define NETLINK_XFRM_SA_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct xfrmnl_sa; + +extern struct xfrmnl_sa* xfrmnl_sa_alloc(void); +extern void xfrmnl_sa_put(struct xfrmnl_sa *); + +extern int xfrmnl_sa_alloc_cache(struct nl_sock *, struct nl_cache **); +extern struct xfrmnl_sa* xfrmnl_sa_get(struct nl_cache*, struct nl_addr*, unsigned int, unsigned int); + +extern int xfrmnl_sa_parse(struct nlmsghdr *n, struct xfrmnl_sa **result); + +extern int xfrmnl_sa_build_get_request(struct nl_addr*, unsigned int, unsigned int, + unsigned int, unsigned int, struct nl_msg **); +extern int xfrmnl_sa_get_kernel(struct nl_sock*, struct nl_addr*, unsigned int, + unsigned int, unsigned int, unsigned int, struct xfrmnl_sa**); + +extern int xfrmnl_sa_build_add_request(struct xfrmnl_sa*, int, struct nl_msg **); +extern int xfrmnl_sa_add(struct nl_sock*, struct xfrmnl_sa*, int); + +extern int xfrmnl_sa_build_update_request(struct xfrmnl_sa*, int, struct nl_msg **); +extern int xfrmnl_sa_update(struct nl_sock*, struct xfrmnl_sa*, int); + +extern int xfrmnl_sa_build_delete_request(struct xfrmnl_sa*, int, struct nl_msg **); +extern int xfrmnl_sa_delete(struct nl_sock*, struct xfrmnl_sa*, int); + +extern struct xfrmnl_sel* xfrmnl_sa_get_sel (struct xfrmnl_sa*); +extern int xfrmnl_sa_set_sel (struct xfrmnl_sa*, struct xfrmnl_sel*); + +extern struct nl_addr* xfrmnl_sa_get_daddr (struct xfrmnl_sa*); +extern int xfrmnl_sa_set_daddr (struct xfrmnl_sa*, struct nl_addr*); + +extern int xfrmnl_sa_get_spi (struct xfrmnl_sa*); +extern int xfrmnl_sa_set_spi (struct xfrmnl_sa*, unsigned int); + +extern int xfrmnl_sa_get_proto (struct xfrmnl_sa*); +extern int xfrmnl_sa_set_proto (struct xfrmnl_sa*, unsigned int); + +extern struct nl_addr* xfrmnl_sa_get_saddr (struct xfrmnl_sa*); +extern int xfrmnl_sa_set_saddr (struct xfrmnl_sa*, struct nl_addr*); + +extern struct xfrmnl_ltime_cfg* xfrmnl_sa_get_lifetime_cfg (struct xfrmnl_sa*); +extern int xfrmnl_sa_set_lifetime_cfg (struct xfrmnl_sa*, struct xfrmnl_ltime_cfg*); + +extern int xfrmnl_sa_get_curlifetime (struct xfrmnl_sa*, unsigned long long int*, + unsigned long long int*, unsigned long long int*, + unsigned long long int*); + +extern int xfrmnl_sa_get_stats (struct xfrmnl_sa*, unsigned long long int*, + unsigned long long int*, unsigned long long int*); + +extern int xfrmnl_sa_get_seq (struct xfrmnl_sa*); + +extern int xfrmnl_sa_get_reqid (struct xfrmnl_sa*); +extern int xfrmnl_sa_set_reqid (struct xfrmnl_sa*, unsigned int); + +extern int xfrmnl_sa_get_family (struct xfrmnl_sa*); +extern int xfrmnl_sa_set_family (struct xfrmnl_sa*, unsigned int); + +extern int xfrmnl_sa_get_mode (struct xfrmnl_sa*); +extern int xfrmnl_sa_set_mode (struct xfrmnl_sa*, unsigned int); + +extern int xfrmnl_sa_get_replay_window (struct xfrmnl_sa*); +extern int xfrmnl_sa_set_replay_window (struct xfrmnl_sa*, unsigned int); + +extern int xfrmnl_sa_get_flags (struct xfrmnl_sa*); +extern int xfrmnl_sa_set_flags (struct xfrmnl_sa*, unsigned int); + +extern int xfrmnl_sa_get_aead_params (struct xfrmnl_sa*, char*, unsigned int*, + unsigned int*, char*); +extern int xfrmnl_sa_set_aead_params (struct xfrmnl_sa*, const char*, unsigned int, + unsigned int, const char*); + +extern int xfrmnl_sa_get_auth_params (struct xfrmnl_sa*, char*, unsigned int*, + unsigned int*, char*); +extern int xfrmnl_sa_set_auth_params (struct xfrmnl_sa*, const char*, unsigned int, + unsigned int, const char*); + +extern int xfrmnl_sa_get_crypto_params (struct xfrmnl_sa*, char*, unsigned int*, char*); +extern int xfrmnl_sa_set_crypto_params (struct xfrmnl_sa*, const char*, unsigned int, + const char*); + +extern int xfrmnl_sa_get_comp_params (struct xfrmnl_sa*, char*, unsigned int*, char*); +extern int xfrmnl_sa_set_comp_params (struct xfrmnl_sa*, const char*, unsigned int, + const char*); + +extern int xfrmnl_sa_get_encap_tmpl (struct xfrmnl_sa*, unsigned int*, unsigned int*, + unsigned int*, struct nl_addr**); +extern int xfrmnl_sa_set_encap_tmpl (struct xfrmnl_sa*, unsigned int, unsigned int, + unsigned int, struct nl_addr*); + +extern int xfrmnl_sa_get_tfcpad (struct xfrmnl_sa*); +extern int xfrmnl_sa_set_tfcpad (struct xfrmnl_sa*, unsigned int); + +extern struct nl_addr* xfrmnl_sa_get_coaddr (struct xfrmnl_sa*); +extern int xfrmnl_sa_set_coaddr (struct xfrmnl_sa*, struct nl_addr*); + +extern int xfrmnl_sa_get_mark (struct xfrmnl_sa*, unsigned int*, unsigned int*); +extern int xfrmnl_sa_set_mark (struct xfrmnl_sa*, unsigned int, unsigned int); + +extern int xfrmnl_sa_get_sec_ctx (struct xfrmnl_sa*, unsigned int*, unsigned int*, + unsigned int*, unsigned int*, char*); +extern int xfrmnl_sa_set_sec_ctx (struct xfrmnl_sa*, unsigned int, unsigned int, + unsigned int, unsigned int, const char*); + +extern int xfrmnl_sa_get_replay_maxage (struct xfrmnl_sa*); +extern int xfrmnl_sa_set_replay_maxage (struct xfrmnl_sa*, unsigned int); + +extern int xfrmnl_sa_get_replay_maxdiff (struct xfrmnl_sa*); +extern int xfrmnl_sa_set_replay_maxdiff (struct xfrmnl_sa*, unsigned int); + +extern int xfrmnl_sa_get_replay_state (struct xfrmnl_sa*, unsigned int*, + unsigned int*, unsigned int*); +extern int xfrmnl_sa_set_replay_state (struct xfrmnl_sa*, unsigned int, + unsigned int, unsigned int); + +extern int xfrmnl_sa_get_replay_state_esn (struct xfrmnl_sa*, unsigned int*, unsigned int*, + unsigned int*, unsigned int*, unsigned int*, + unsigned int*, unsigned int*); +extern int xfrmnl_sa_set_replay_state_esn (struct xfrmnl_sa*, unsigned int, unsigned int, + unsigned int, unsigned int, unsigned int, + unsigned int, unsigned int*); + +extern int xfrmnl_sa_get_user_offload (struct xfrmnl_sa*, int*, uint8_t *); +extern int xfrmnl_sa_set_user_offload (struct xfrmnl_sa*, int, uint8_t); + +extern int xfrmnl_sa_is_expiry_reached (struct xfrmnl_sa*); +extern int xfrmnl_sa_is_hardexpiry_reached (struct xfrmnl_sa*); + +extern char* xfrmnl_sa_flags2str(int, char *, size_t); +extern int xfrmnl_sa_str2flag(const char *); + +extern char* xfrmnl_sa_mode2str(int, char *, size_t); +extern int xfrmnl_sa_str2mode(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/xfrm/selector.h b/libnl/include/netlink/xfrm/selector.h new file mode 100644 index 0000000..2ee6842 --- /dev/null +++ b/libnl/include/netlink/xfrm/selector.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * Neither the name of Texas Instruments Incorporated 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 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 NETLINK_XFRM_SEL_H_ +#define NETLINK_XFRM_SEL_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct xfrmnl_sel; + +/* Creation */ +extern struct xfrmnl_sel* xfrmnl_sel_alloc(void); +extern struct xfrmnl_sel* xfrmnl_sel_clone(struct xfrmnl_sel*); + +/* Usage Management */ +extern struct xfrmnl_sel* xfrmnl_sel_get(struct xfrmnl_sel*); +extern void xfrmnl_sel_put(struct xfrmnl_sel*); +extern int xfrmnl_sel_shared(struct xfrmnl_sel*); +extern int xfrmnl_sel_cmp(struct xfrmnl_sel*, struct xfrmnl_sel*); +extern void xfrmnl_sel_dump(struct xfrmnl_sel*, struct nl_dump_params *); + +/* Access Functions */ +extern struct nl_addr* xfrmnl_sel_get_daddr (struct xfrmnl_sel*); +extern int xfrmnl_sel_set_daddr (struct xfrmnl_sel*, struct nl_addr*); + +extern struct nl_addr* xfrmnl_sel_get_saddr (struct xfrmnl_sel*); +extern int xfrmnl_sel_set_saddr (struct xfrmnl_sel*, struct nl_addr*); + +extern int xfrmnl_sel_get_dport (struct xfrmnl_sel*); +extern int xfrmnl_sel_set_dport (struct xfrmnl_sel*, unsigned int); + +extern int xfrmnl_sel_get_dportmask (struct xfrmnl_sel*); +extern int xfrmnl_sel_set_dportmask (struct xfrmnl_sel*, unsigned int); + +extern int xfrmnl_sel_get_sport (struct xfrmnl_sel*); +extern int xfrmnl_sel_set_sport (struct xfrmnl_sel*, unsigned int); + +extern int xfrmnl_sel_get_sportmask (struct xfrmnl_sel*); +extern int xfrmnl_sel_set_sportmask (struct xfrmnl_sel*, unsigned int); + +extern int xfrmnl_sel_get_family (struct xfrmnl_sel*); +extern int xfrmnl_sel_set_family (struct xfrmnl_sel*, unsigned int); + +extern int xfrmnl_sel_get_prefixlen_d (struct xfrmnl_sel*); +extern int xfrmnl_sel_set_prefixlen_d (struct xfrmnl_sel*, unsigned int); + +extern int xfrmnl_sel_get_prefixlen_s (struct xfrmnl_sel*); +extern int xfrmnl_sel_set_prefixlen_s (struct xfrmnl_sel*, unsigned int); + +extern int xfrmnl_sel_get_proto (struct xfrmnl_sel*); +extern int xfrmnl_sel_set_proto (struct xfrmnl_sel*, unsigned int); + +extern int xfrmnl_sel_get_ifindex (struct xfrmnl_sel*); +extern int xfrmnl_sel_set_ifindex (struct xfrmnl_sel*, unsigned int); + +extern int xfrmnl_sel_get_userid (struct xfrmnl_sel*); +extern int xfrmnl_sel_set_userid (struct xfrmnl_sel*, unsigned int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/xfrm/sp.h b/libnl/include/netlink/xfrm/sp.h new file mode 100644 index 0000000..84cbfb2 --- /dev/null +++ b/libnl/include/netlink/xfrm/sp.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * Neither the name of Texas Instruments Incorporated 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 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 NETLINK_XFRM_SP_H_ +#define NETLINK_XFRM_SP_H_ + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct xfrmnl_sp; + +extern struct xfrmnl_sp* xfrmnl_sp_alloc(void); +extern void xfrmnl_sp_put(struct xfrmnl_sp *); + +extern int xfrmnl_sp_alloc_cache(struct nl_sock *, struct nl_cache **); +extern struct xfrmnl_sp* xfrmnl_sp_get(struct nl_cache*, unsigned int, unsigned int); + +extern int xfrmnl_sp_parse(struct nlmsghdr *n, struct xfrmnl_sp **result); + +extern int xfrmnl_sp_build_get_request(unsigned int, unsigned int, unsigned int, + unsigned int, struct nl_msg **); +extern int xfrmnl_sp_get_kernel(struct nl_sock*, unsigned int, unsigned int, + unsigned int, unsigned int, struct xfrmnl_sp**); + +extern int xfrmnl_sp_add(struct nl_sock*, struct xfrmnl_sp*, int); +extern int xfrmnl_sp_build_add_request(struct xfrmnl_sp*, int, struct nl_msg **); + +extern int xfrmnl_sp_update(struct nl_sock*, struct xfrmnl_sp*, int); +extern int xfrmnl_sp_build_update_request(struct xfrmnl_sp*, int, struct nl_msg **); + +extern int xfrmnl_sp_delete(struct nl_sock*, struct xfrmnl_sp*, int); +extern int xfrmnl_sp_build_delete_request(struct xfrmnl_sp*, int, struct nl_msg **); + +extern struct xfrmnl_sel* xfrmnl_sp_get_sel (struct xfrmnl_sp*); +extern int xfrmnl_sp_set_sel (struct xfrmnl_sp*, struct xfrmnl_sel*); + +extern struct xfrmnl_ltime_cfg* xfrmnl_sp_get_lifetime_cfg (struct xfrmnl_sp*); +extern int xfrmnl_sp_set_lifetime_cfg (struct xfrmnl_sp*, struct xfrmnl_ltime_cfg*); + +extern int xfrmnl_sp_get_curlifetime (struct xfrmnl_sp*, unsigned long long int*, + unsigned long long int*, unsigned long long int*, + unsigned long long int*); + +extern int xfrmnl_sp_get_priority (struct xfrmnl_sp*); +extern int xfrmnl_sp_set_priority (struct xfrmnl_sp*, unsigned int); + +extern int xfrmnl_sp_get_index (struct xfrmnl_sp*); +extern int xfrmnl_sp_set_index (struct xfrmnl_sp*, unsigned int); + +extern int xfrmnl_sp_get_dir (struct xfrmnl_sp*); +extern int xfrmnl_sp_set_dir (struct xfrmnl_sp*, unsigned int); + +extern int xfrmnl_sp_get_action (struct xfrmnl_sp*); +extern int xfrmnl_sp_set_action (struct xfrmnl_sp*, unsigned int); + +extern int xfrmnl_sp_get_flags (struct xfrmnl_sp*); +extern int xfrmnl_sp_set_flags (struct xfrmnl_sp*, unsigned int); + +extern int xfrmnl_sp_get_share (struct xfrmnl_sp*); +extern int xfrmnl_sp_set_share (struct xfrmnl_sp*, unsigned int); + +extern int xfrmnl_sp_get_sec_ctx (struct xfrmnl_sp*, unsigned int*, unsigned int*, + unsigned int*, unsigned int*, unsigned int*, char*); +extern int xfrmnl_sp_set_sec_ctx (struct xfrmnl_sp*, unsigned int, unsigned int, + unsigned int, unsigned int, unsigned int, char*); + +extern int xfrmnl_sp_get_userpolicy_type (struct xfrmnl_sp*); +extern int xfrmnl_sp_set_userpolicy_type (struct xfrmnl_sp*, unsigned int); + +extern void xfrmnl_sp_add_usertemplate(struct xfrmnl_sp*, struct xfrmnl_user_tmpl*); +extern void xfrmnl_sp_remove_usertemplate(struct xfrmnl_sp*, struct xfrmnl_user_tmpl*); +extern struct nl_list_head* xfrmnl_sp_get_usertemplates(struct xfrmnl_sp*); +extern int xfrmnl_sp_get_nusertemplates(struct xfrmnl_sp*); +extern void xfrmnl_sp_foreach_usertemplate(struct xfrmnl_sp*, + void (*cb)(struct xfrmnl_user_tmpl*, void *), + void *arg); +extern struct xfrmnl_user_tmpl* xfrmnl_sp_usertemplate_n(struct xfrmnl_sp*, int); + +extern int xfrmnl_sp_get_mark (struct xfrmnl_sp*, unsigned int*, unsigned int*); +extern int xfrmnl_sp_set_mark (struct xfrmnl_sp*, unsigned int, unsigned int); + +extern char* xfrmnl_sp_action2str(int, char *, size_t); +extern int xfrmnl_sp_str2action(const char *); + +extern char* xfrmnl_sp_flags2str(int, char *, size_t); +extern int xfrmnl_sp_str2flag(const char *); + +extern char* xfrmnl_sp_type2str(int, char *, size_t); +extern int xfrmnl_sp_str2type(const char *); + +extern char* xfrmnl_sp_dir2str(int, char *, size_t); +extern int xfrmnl_sp_str2dir(const char *); + +extern char* xfrmnl_sp_share2str(int, char *, size_t); +extern int xfrmnl_sp_str2share(const char *); + +extern int xfrmnl_sp_index2dir (unsigned int); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/xfrm/template.h b/libnl/include/netlink/xfrm/template.h new file mode 100644 index 0000000..da51e7d --- /dev/null +++ b/libnl/include/netlink/xfrm/template.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * Neither the name of Texas Instruments Incorporated 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 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 NETLINK_XFRM_TEMPL_H_ +#define NETLINK_XFRM_TEMPL_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct xfrmnl_user_tmpl; + +/* Creation */ +extern struct xfrmnl_user_tmpl* xfrmnl_user_tmpl_alloc(void); +extern struct xfrmnl_user_tmpl* xfrmnl_user_tmpl_clone(struct xfrmnl_user_tmpl*); +extern void xfrmnl_user_tmpl_free(struct xfrmnl_user_tmpl* utmpl); + +/* Utility functions */ +extern int xfrmnl_user_tmpl_cmp(struct xfrmnl_user_tmpl*, struct xfrmnl_user_tmpl*); +extern void xfrmnl_user_tmpl_dump(struct xfrmnl_user_tmpl*, struct nl_dump_params*); + +/* Access Functions */ +extern struct nl_addr* xfrmnl_user_tmpl_get_daddr (struct xfrmnl_user_tmpl*); +extern int xfrmnl_user_tmpl_set_daddr (struct xfrmnl_user_tmpl*, struct nl_addr*); + +extern int xfrmnl_user_tmpl_get_spi (struct xfrmnl_user_tmpl*); +extern int xfrmnl_user_tmpl_set_spi (struct xfrmnl_user_tmpl*, unsigned int); + +extern int xfrmnl_user_tmpl_get_proto (struct xfrmnl_user_tmpl*); +extern int xfrmnl_user_tmpl_set_proto (struct xfrmnl_user_tmpl*, unsigned int); + +extern int xfrmnl_user_tmpl_get_family (struct xfrmnl_user_tmpl*); +extern int xfrmnl_user_tmpl_set_family (struct xfrmnl_user_tmpl*, unsigned int); + +extern struct nl_addr* xfrmnl_user_tmpl_get_saddr (struct xfrmnl_user_tmpl*); +extern int xfrmnl_user_tmpl_set_saddr (struct xfrmnl_user_tmpl*, struct nl_addr*); + +extern int xfrmnl_user_tmpl_get_reqid (struct xfrmnl_user_tmpl*); +extern int xfrmnl_user_tmpl_set_reqid (struct xfrmnl_user_tmpl*, unsigned int); + +extern int xfrmnl_user_tmpl_get_mode (struct xfrmnl_user_tmpl*); +extern int xfrmnl_user_tmpl_set_mode (struct xfrmnl_user_tmpl*, unsigned int); + +extern int xfrmnl_user_tmpl_get_share (struct xfrmnl_user_tmpl*); +extern int xfrmnl_user_tmpl_set_share (struct xfrmnl_user_tmpl*, unsigned int); + +extern int xfrmnl_user_tmpl_get_optional (struct xfrmnl_user_tmpl*); +extern int xfrmnl_user_tmpl_set_optional (struct xfrmnl_user_tmpl*, unsigned int); + +extern int xfrmnl_user_tmpl_get_aalgos (struct xfrmnl_user_tmpl*); +extern int xfrmnl_user_tmpl_set_aalgos (struct xfrmnl_user_tmpl*, unsigned int); + +extern int xfrmnl_user_tmpl_get_ealgos (struct xfrmnl_user_tmpl*); +extern int xfrmnl_user_tmpl_set_ealgos (struct xfrmnl_user_tmpl*, unsigned int); + +extern int xfrmnl_user_tmpl_get_calgos (struct xfrmnl_user_tmpl*); +extern int xfrmnl_user_tmpl_set_calgos (struct xfrmnl_user_tmpl*, unsigned int); + +extern char* xfrmnl_user_tmpl_mode2str(int, char *, size_t); +extern int xfrmnl_user_tmpl_str2mode(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/nl-aux-core/README.md b/libnl/include/nl-aux-core/README.md new file mode 100644 index 0000000..a9d6b81 --- /dev/null +++ b/libnl/include/nl-aux-core/README.md @@ -0,0 +1,15 @@ +include/nl-aux-core +==================== + +This contains private/internal helpers that depend on the public API of libnl-3 (core). + +Itself, it must only rely on C, include/base/ and public headers of libnl-3 (core). + +They can be used by all internal code that uses the public API of libnl-3. + +It can also be used by lib/ itself (that is, the implementation of +libnl-core-3). + +It must not be used in public headers, it's internal only. + +Currently this is header-only, it does not require any additional linking. diff --git a/libnl/include/nl-aux-core/nl-core.h b/libnl/include/nl-aux-core/nl-core.h new file mode 100644 index 0000000..41393c0 --- /dev/null +++ b/libnl/include/nl-aux-core/nl-core.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#ifndef NETLINK_NL_AUTO_H_ +#define NETLINK_NL_AUTO_H_ + +#include "base/nl-base-utils.h" + +#define NL_DBG(LVL, FMT, ARG...) \ + do { \ + if ((NL_DEBUG) && (LVL) <= nl_debug) { \ + const int _errsv = errno; \ + \ + fprintf(stderr, "DBG<" #LVL ">%20s:%-4u %s: " FMT, \ + __FILE__, __LINE__, __func__, ##ARG); \ + errno = _errsv; \ + } \ + } while (0) + +struct nl_addr; +void nl_addr_put(struct nl_addr *); +#define _nl_auto_nl_addr _nl_auto(_nl_auto_nl_addr_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct nl_addr *, _nl_auto_nl_addr_fcn, nl_addr_put); + +struct nl_data; +void nl_data_free(struct nl_data *data); +#define _nl_auto_nl_data _nl_auto(_nl_auto_nl_data_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct nl_data *, _nl_auto_nl_data_fcn, + nl_data_free); + +struct nl_msg; +void nlmsg_free(struct nl_msg *); +#define _nl_auto_nl_msg _nl_auto(_nl_auto_nl_msg_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct nl_msg *, _nl_auto_nl_msg_fcn, nlmsg_free); + +struct nl_cache; +void nl_cache_put(struct nl_cache *); +#define _nl_auto_nl_cache _nl_auto(_nl_auto_nl_cache_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct nl_cache *, _nl_auto_nl_cache_fcn, + nl_cache_put); + +struct nl_sock; +void nl_socket_free(struct nl_sock *); +#define _nl_auto_nl_socket _nl_auto(_nl_auto_nl_socket_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct nl_sock *, _nl_auto_nl_socket_fcn, + nl_socket_free); + +struct nl_cache_mngr; +void nl_cache_mngr_free(struct nl_cache_mngr *mngr); +#define _nl_auto_nl_cache_mngr _nl_auto(_nl_auto_nl_cache_mngr_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct nl_cache_mngr *, _nl_auto_nl_cache_mngr_fcn, + nl_cache_mngr_free); + +struct nl_object; +void nl_object_put(struct nl_object *); +#define _nl_auto_nl_object _nl_auto(_nl_auto_nl_object_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct nl_object *, _nl_auto_nl_object_fcn, + nl_object_put); + +struct nl_addr *nl_addr_build(int, const void *, size_t); + +static inline struct nl_addr *_nl_addr_build(int family, const void *buf) +{ + return nl_addr_build(family, buf, _nl_addr_family_to_size(family)); +} + +static inline uint16_t _nla_len(const struct nlattr *nla) +{ + _nl_assert(nla); + _nl_assert(nla->nla_len >= (uint16_t)NLA_HDRLEN); + + return nla->nla_len - (uint16_t)NLA_HDRLEN; +} + +#endif /* NETLINK_NL_AUTO_H_ */ diff --git a/libnl/include/nl-aux-route/README.md b/libnl/include/nl-aux-route/README.md new file mode 100644 index 0000000..3c4d4d0 --- /dev/null +++ b/libnl/include/nl-aux-route/README.md @@ -0,0 +1,18 @@ +include/nl-aux-route +==================== + +This contains private/internal helpers that depend on the public libnl-3 (core) +and libnl-route-3. + +Itself, it must only rely on C, include/base/ and public headers of libnl-3 (core) +and libnl-route-3. + +They can be used by all internal code that uses the public API of both libnl-3 (core) +and libnl-route-3. + +It can also be used by lib/route itself (that is, the implementation of +libnl-route-3). + +It must not be used in public headers, it's internal only. + +Currently this is header-only, it does not require any additional linking. diff --git a/libnl/include/nl-aux-route/nl-route.h b/libnl/include/nl-aux-route/nl-route.h new file mode 100644 index 0000000..9c85337 --- /dev/null +++ b/libnl/include/nl-aux-route/nl-route.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#ifndef __NETLINK_NL_AUX_ROUTE_NL_ROUTE_H__ +#define __NETLINK_NL_AUX_ROUTE_NL_ROUTE_H__ + +#include "base/nl-base-utils.h" + +#include + +struct rtnl_link; +void rtnl_link_put(struct rtnl_link *); +#define _nl_auto_rtnl_link _nl_auto(_nl_auto_rtnl_link_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct rtnl_link *, _nl_auto_rtnl_link_fcn, + rtnl_link_put); + +struct rtnl_route; +void rtnl_route_put(struct rtnl_route *); +#define _nl_auto_rtnl_route _nl_auto(_nl_auto_rtnl_route_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct rtnl_route *, _nl_auto_rtnl_route_fcn, + rtnl_route_put); + +struct rtnl_mdb; +void rtnl_mdb_put(struct rtnl_mdb *); +#define _nl_auto_rtnl_mdb _nl_auto(_nl_auto_rtnl_mdb_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct rtnl_mdb *, _nl_auto_rtnl_mdb_fcn, + rtnl_mdb_put); + +struct rtnl_nexthop; +void rtnl_route_nh_free(struct rtnl_nexthop *); +#define _nl_auto_rtnl_nexthop _nl_auto(_nl_auto_rtnl_nexthop_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct rtnl_nexthop *, _nl_auto_rtnl_nexthop_fcn, + rtnl_route_nh_free); + +struct rtnl_nh; +void rtnl_nh_put(struct rtnl_nh *); +#define _nl_auto_rtnl_nh _nl_auto(_nl_auto_rtnl_nh_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct rtnl_nh *, _nl_auto_rtnl_nh_fcn, rtnl_nh_put); + +struct rtnl_link_af_ops; +void rtnl_link_af_ops_put(struct rtnl_link_af_ops *); +#define _nl_auto_rtnl_link_af_ops _nl_auto(_nl_auto_rtnl_link_af_ops_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct rtnl_link_af_ops *, + _nl_auto_rtnl_link_af_ops_fcn, rtnl_link_af_ops_put); + +#define _nl_auto_rtnl_act _nl_auto(_nl_auto_rtnl_act_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct rtnl_act *, _nl_auto_rtnl_act_fcn, + rtnl_act_put); + +#define _nl_auto_rtnl_act_all _nl_auto(_nl_auto_rtnl_act_fcn_all) +_NL_AUTO_DEFINE_FCN_INDIRECT0(struct rtnl_act *, _nl_auto_rtnl_act_fcn_all, + rtnl_act_put_all); + +struct rtnl_ematch_tree; +void rtnl_ematch_tree_free(struct rtnl_ematch_tree *); +#define _nl_auto_rtnl_ematch_tree _nl_auto(_nl_auto_rtnl_ematch_tree_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct rtnl_ematch_tree *, + _nl_auto_rtnl_ematch_tree_fcn, + rtnl_ematch_tree_free); + +struct rtnl_cls; +void rtnl_cls_put(struct rtnl_cls *); +#define _nl_auto_rtnl_cls _nl_auto(_nl_auto_rtnl_cls_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct rtnl_cls *, _nl_auto_rtnl_cls_fcn, + rtnl_cls_put); + +/*****************************************************************************/ + +static inline int _rtnl_act_append_get(struct rtnl_act **head, + struct rtnl_act *new) +{ + int r; + + r = rtnl_act_append(head, new); + if (r >= 0) + rtnl_act_get(new); + return r; +} + +static inline int _rtnl_act_append_take(struct rtnl_act **head, + struct rtnl_act *new) +{ + int r; + + r = rtnl_act_append(head, new); + if (r < 0) + rtnl_act_put(new); + return r; +} + +#endif /* __NETLINK_NL_AUX_ROUTE_NL_ROUTE_H__ */ diff --git a/libnl/include/nl-aux-xfrm/README.md b/libnl/include/nl-aux-xfrm/README.md new file mode 100644 index 0000000..9a4b6e2 --- /dev/null +++ b/libnl/include/nl-aux-xfrm/README.md @@ -0,0 +1,18 @@ +include/nl-aux-xfrm +=================== + +This contains private/internal helpers that depend on the public libnl-3 (core) +and libnl-xfrm-3. + +Itself, it must only rely on C, include/base/ and public headers of libnl-3 (core) +and libnl-xfrm-3. + +They can be used by all internal code that uses the public API of both libnl-3 (core) +and libnl-xfrm-3. + +It can also be used by lib/xfrm itself (that is, the implementation of +libnl-xfrm-3). + +It must not be used in public headers, it's internal only. + +Currently this is header-only, it does not require any additional linking. diff --git a/libnl/include/nl-aux-xfrm/nl-xfrm.h b/libnl/include/nl-aux-xfrm/nl-xfrm.h new file mode 100644 index 0000000..9c5c46a --- /dev/null +++ b/libnl/include/nl-aux-xfrm/nl-xfrm.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#ifndef __NETLINK_NL_AUX_XFRM_NL_XFRM_H__ +#define __NETLINK_NL_AUX_XFRM_NL_XFRM_H__ + +#include "base/nl-base-utils.h" + +struct xfrmnl_sp; +void xfrmnl_sp_put(struct xfrmnl_sp *sp); +#define _nl_auto_xfrmnl_sp _nl_auto(_nl_auto_xfrmnl_sp_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct xfrmnl_sp *, _nl_auto_xfrmnl_sp_fcn, + xfrmnl_sp_put); + +struct xfrmnl_sa; +void xfrmnl_sa_put(struct xfrmnl_sa *sa); +#define _nl_auto_xfrmnl_sa _nl_auto(_nl_auto_xfrmnl_sa_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct xfrmnl_sa *, _nl_auto_xfrmnl_sa_fcn, + xfrmnl_sa_put); + +struct xfrmnl_ae; +void xfrmnl_ae_put(struct xfrmnl_ae *ae); +#define _nl_auto_xfrmnl_ae _nl_auto(_nl_auto_xfrmnl_ae_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct xfrmnl_ae *, _nl_auto_xfrmnl_ae_fcn, + xfrmnl_ae_put); + +struct xfrmnl_user_tmpl; +void xfrmnl_user_tmpl_free(struct xfrmnl_user_tmpl *utmpl); +#define _nl_auto_xfrmnl_user_tmpl _nl_auto(_nl_auto_xfrmnl_user_tmpl_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct xfrmnl_user_tmpl *, + _nl_auto_xfrmnl_user_tmpl_fcn, + xfrmnl_user_tmpl_free); + +#endif /* __NETLINK_NL_AUX_XFRM_NL_XFRM_H__ */ diff --git a/libnl/include/nl-default.h b/libnl/include/nl-default.h new file mode 100644 index 0000000..83dbcb4 --- /dev/null +++ b/libnl/include/nl-default.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#ifndef __NL_DEFAULT_H__ +#define __NL_DEFAULT_H__ + +#include "config.h" + +#include "base/nl-base-utils.h" + +#endif /* __NL_DEFAULT_H__ */ diff --git a/libnl/include/nl-priv-dynamic-core/README.md b/libnl/include/nl-priv-dynamic-core/README.md new file mode 100644 index 0000000..9b2aef4 --- /dev/null +++ b/libnl/include/nl-priv-dynamic-core/README.md @@ -0,0 +1,8 @@ +include/nl-priv-dynamic-core +============================ + +Contains internal API on top of core (libnl-3). It is +implemented by core and usable to all users that link +against libnl-3. + +Note that the ABI, while being internal, should stay stable. diff --git a/libnl/include/nl-priv-dynamic-core/cache-api.h b/libnl/include/nl-priv-dynamic-core/cache-api.h new file mode 100644 index 0000000..2ddc30c --- /dev/null +++ b/libnl/include/nl-priv-dynamic-core/cache-api.h @@ -0,0 +1,280 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_CACHE_API_H_ +#define NETLINK_CACHE_API_H_ + +#include +#include + +/** + * @ingroup cache + * @defgroup cache_api Cache Implementation + * @brief + * + * @par 1) Cache Definition + * @code + * struct nl_cache_ops my_cache_ops = { + * .co_name = "route/link", + * .co_protocol = NETLINK_ROUTE, + * .co_hdrsize = sizeof(struct ifinfomsg), + * .co_obj_ops = &my_obj_ops, + * }; + * @endcode + * + * @par 2) + * @code + * // The simplest way to fill a cache is by providing a request-update + * // function which must trigger a complete dump on the kernel-side of + * // whatever the cache covers. + * static int my_request_update(struct nl_cache *cache, + * struct nl_sock *socket) + * { + * // In this example, we request a full dump of the interface table + * return nl_rtgen_request(socket, RTM_GETLINK, AF_UNSPEC, NLM_F_DUMP); + * } + * + * // The resulting netlink messages sent back will be fed into a message + * // parser one at a time. The message parser has to extract all relevant + * // information from the message and create an object reflecting the + * // contents of the message and pass it on to the parser callback function + * // provide which will add the object to the cache. + * static int my_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + * struct nlmsghdr *nlh, struct nl_parser_param *pp) + * { + * struct my_obj *obj; + * + * obj = my_obj_alloc(); + * obj->ce_msgtype = nlh->nlmsg_type; + * + * // Parse the netlink message and continue creating the object. + * + * err = pp->pp_cb((struct nl_object *) obj, pp); + * if (err < 0) + * goto errout; + * } + * + * struct nl_cache_ops my_cache_ops = { + * ... + * .co_request_update = my_request_update, + * .co_msg_parser = my_msg_parser, + * }; + * @endcode + * + * @par 3) Notification based Updates + * @code + * // Caches can be kept up-to-date based on notifications if the kernel + * // sends out notifications whenever an object is added/removed/changed. + * // + * // It is trivial to support this, first a list of groups needs to be + * // defined which are required to join in order to receive all necessary + * // notifications. The groups are separated by address family to support + * // the common situation where a separate group is used for each address + * // family. If there is only one group, simply specify AF_UNSPEC. + * static struct nl_af_group addr_groups[] = { + * { AF_INET, RTNLGRP_IPV4_IFADDR }, + * { AF_INET6, RTNLGRP_IPV6_IFADDR }, + * { END_OF_GROUP_LIST }, + * }; + * + * // In order for the caching system to know the meaning of each message + * // type it requires a table which maps each supported message type to + * // a cache action, e.g. RTM_NEWADDR means address has been added or + * // updated, RTM_DELADDR means address has been removed. + * static struct nl_cache_ops rtnl_addr_ops = { + * ... + * .co_msgtypes = { + * { RTM_NEWADDR, NL_ACT_NEW, "new" }, + * { RTM_DELADDR, NL_ACT_DEL, "del" }, + * { RTM_GETADDR, NL_ACT_GET, "get" }, + * END_OF_MSGTYPES_LIST, + * }, + * .co_groups = addr_groups, + * }; + * + * // It is now possible to keep the cache up-to-date using the cache manager. + * @endcode + * @{ + */ + +#define END_OF_MSGTYPES_LIST { -1, -1, NULL } + +/** + * Message type to cache action association + */ +struct nl_msgtype +{ + /** Netlink message type */ + int mt_id; + + /** Cache action to take */ + int mt_act; + + /** Name of operation for human-readable printing */ + char * mt_name; +}; + +/** + * Address family to netlink group association + */ +struct nl_af_group +{ + /** Address family */ + int ag_family; + + /** Netlink group identifier */ + int ag_group; +}; + +#define END_OF_GROUP_LIST AF_UNSPEC, 0 + +/** + * Parser parameters + * + * This structure is used to configure what kind of parser to use + * when parsing netlink messages to create objects. + */ +struct nl_parser_param +{ + /** Function to parse netlink messages into objects */ + int (*pp_cb)(struct nl_object *, struct nl_parser_param *); + + /** Arbitary argument to be passed to the parser */ + void * pp_arg; +}; + +/** + * Cache Operations + * + * This structure defines the characterstics of a cache type. It contains + * pointers to functions which implement the specifics of the object type + * the cache can hold. + */ +struct nl_cache_ops +{ + /** Name of cache type (must be unique) */ + char * co_name; + + /** Size of family specific netlink header */ + int co_hdrsize; + + /** Netlink protocol */ + int co_protocol; + + /** cache object hash size **/ + int co_hash_size; + + /** cache flags */ + unsigned int co_flags; + + /** Reference counter */ + unsigned int co_refcnt; + + /** Group definition */ + struct nl_af_group * co_groups; + + /** + * Called whenever an update of the cache is required. Must send + * a request message to the kernel requesting a complete dump. + */ + int (*co_request_update)(struct nl_cache *, struct nl_sock *); + + /** + * Called whenever a message was received that needs to be parsed. + * Must parse the message and call the paser callback function + * (nl_parser_param) provided via the argument. + */ + int (*co_msg_parser)(struct nl_cache_ops *, struct sockaddr_nl *, + struct nlmsghdr *, struct nl_parser_param *); + + /** + * The function registered under this callback is called after a + * netlink notification associated with this cache type has been + * parsed into an object and is being considered for inclusio into + * the specified cache. + * + * The purpose of this function is to filter out notifications + * which should be ignored when updating caches. + * + * The function must return NL_SKIP to prevent the object from + * being included, or NL_OK to include it. + * + * @code + * int my_filter(struct nl_cache *cache, struct nl_object *obj) + * { + * if (reason_to_not_include_obj(obj)) + * return NL_SKIP; + * + * return NL_OK; + * } + * @endcode + */ + int (*co_event_filter)(struct nl_cache *, struct nl_object *obj); + + /** + * The function registered under this callback is called when an + * object formed from a notification event needs to be included in + * a cache. + * + * For each modified object, the change callback \c change_cb must + * be called with the \c data argument provided. + * + * If no function is registered, the function nl_cache_include() + * will be used for this purpose. + * + * @see nl_cache_include() + */ + int (*co_include_event)(struct nl_cache *cache, struct nl_object *obj, + change_func_t change_cb, change_func_v2_t change_cb_v2, + void *data); + + void (*reserved_1)(void); + void (*reserved_2)(void); + void (*reserved_3)(void); + void (*reserved_4)(void); + void (*reserved_5)(void); + void (*reserved_6)(void); + void (*reserved_7)(void); + void (*reserved_8)(void); + + /** Object operations */ + struct nl_object_ops * co_obj_ops; + + /** Internal, do not touch! */ + struct nl_cache_ops *co_next; + + struct nl_cache *co_major_cache; + struct genl_ops * co_genl; + + /* Message type definition */ + struct nl_msgtype co_msgtypes[]; +}; + +/** @} */ + +struct nl_cache { + struct nl_list_head c_items; + int c_nitems; + int c_iarg1; + int c_iarg2; + int c_refcnt; + unsigned int c_flags; + struct nl_hash_table *hashtable; + struct nl_cache_ops *c_ops; +}; + +static inline const char *nl_cache_name(struct nl_cache *cache) +{ + return cache->c_ops ? cache->c_ops->co_name : "unknown"; +} + +struct nl_cache_assoc { + struct nl_cache *ca_cache; + change_func_t ca_change; + change_func_v2_t ca_change_v2; + void *ca_change_data; +}; + +#endif diff --git a/libnl/include/nl-priv-dynamic-core/nl-core.h b/libnl/include/nl-priv-dynamic-core/nl-core.h new file mode 100644 index 0000000..33ebfa1 --- /dev/null +++ b/libnl/include/nl-priv-dynamic-core/nl-core.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef __NL_SHARED_CORE_NL_CORE_H__ +#define __NL_SHARED_CORE_NL_CORE_H__ + +#define NL_SOCK_PASSCRED (1 << 1) +#define NL_OWN_PORT (1 << 2) +#define NL_MSG_PEEK (1 << 3) +#define NL_MSG_PEEK_EXPLICIT (1 << 4) +#define NL_NO_AUTO_ACK (1 << 5) + +struct nl_sock { + struct sockaddr_nl s_local; + struct sockaddr_nl s_peer; + int s_fd; + int s_proto; + unsigned int s_seq_next; + unsigned int s_seq_expect; + int s_flags; + struct nl_cb *s_cb; + size_t s_bufsize; +}; + +static inline int wait_for_ack(struct nl_sock *sk) +{ + if (sk->s_flags & NL_NO_AUTO_ACK) + return 0; + else + return nl_wait_for_ack(sk); +} + +#define LOOSE_COMPARISON 1 +#define ID_COMPARISON 2 + +#define NL_OBJ_MARK 1 + +struct nl_data { + size_t d_size; + void *d_data; +}; + +struct nl_addr { + int a_family; + unsigned int a_maxsize; + unsigned int a_len; + int a_prefixlen; + int a_refcnt; + char a_addr[0]; +}; + +#define NL_MSG_CRED_PRESENT 1 + +struct nl_msg { + int nm_protocol; + int nm_flags; + struct sockaddr_nl nm_src; + struct sockaddr_nl nm_dst; + struct ucred nm_creds; + struct nlmsghdr *nm_nlh; + size_t nm_size; + int nm_refcnt; +}; + +/*****************************************************************************/ + +int nl_getprotobyname(const char *name); + +bool nl_getprotobynumber(int proto, char *out_name, size_t name_len); + +extern const char *nl_strerror_l(int err); + +extern int __nl_read_num_str_file(const char *path, + int (*cb)(long, const char *)); + +extern int __trans_list_add(int, const char *, struct nl_list_head *); +extern void __trans_list_clear(struct nl_list_head *); + +extern char *__type2str(int, char *, size_t, const struct trans_tbl *, size_t); +extern int __str2type(const char *, const struct trans_tbl *, size_t); + +extern char *__list_type2str(int, char *, size_t, struct nl_list_head *); +extern int __list_str2type(const char *, struct nl_list_head *); + +extern char *__flags2str(int, char *, size_t, const struct trans_tbl *, size_t); +extern int __str2flags(const char *, const struct trans_tbl *, size_t); + +#endif /* __NL_SHARED_CORE_NL_CORE_H__ */ diff --git a/libnl/include/nl-priv-dynamic-core/object-api.h b/libnl/include/nl-priv-dynamic-core/object-api.h new file mode 100644 index 0000000..a20a9d5 --- /dev/null +++ b/libnl/include/nl-priv-dynamic-core/object-api.h @@ -0,0 +1,364 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_OBJECT_API_H_ +#define NETLINK_OBJECT_API_H_ + +#include +#include +#include + +/** + * @ingroup object + * @defgroup object_api Object API + * @brief + * + * @par 1) Object Definition + * @code + * // Define your object starting with the common object header + * struct my_obj { + * NLHDR_COMMON + * int my_data; + * }; + * + * // Fill out the object operations structure + * struct nl_object_ops my_ops = { + * .oo_name = "my_obj", + * .oo_size = sizeof(struct my_obj), + * }; + * + * // At this point the object can be allocated, you may want to provide a + * // separate _alloc() function to ease allocting objects of this kind. + * struct nl_object *obj = nl_object_alloc(&my_ops); + * + * // And release it again... + * nl_object_put(obj); + * @endcode + * + * @par 2) Allocating additional data + * @code + * // You may require to allocate additional data and store it inside + * // object, f.e. assuming there is a field `ptr'. + * struct my_obj { + * NLHDR_COMMON + * void * ptr; + * }; + * + * // And at some point you may assign allocated data to this field: + * my_obj->ptr = calloc(1, ...); + * + * // In order to not introduce any memory leaks you have to release + * // this data again when the last reference is given back. + * static void my_obj_free_data(struct nl_object *obj) + * { + * struct my_obj *my_obj = nl_object_priv(obj); + * + * free(my_obj->ptr); + * } + * + * // Also when the object is cloned, you must ensure for your pointer + * // stay valid even if one of the clones is freed by either making + * // a clone as well or increase the reference count. + * static int my_obj_clone(struct nl_object *src, struct nl_object *dst) + * { + * struct my_obj *my_src = nl_object_priv(src); + * struct my_obj *my_dst = nl_object_priv(dst); + * + * if (src->ptr) { + * dst->ptr = calloc(1, ...); + * memcpy(dst->ptr, src->ptr, ...); + * } + * } + * + * struct nl_object_ops my_ops = { + * ... + * .oo_free_data = my_obj_free_data, + * .oo_clone = my_obj_clone, + * }; + * @endcode + * + * @par 3) Object Dumping + * @code + * static int my_obj_dump_detailed(struct nl_object *obj, + * struct nl_dump_params *params) + * { + * struct my_obj *my_obj = nl_object_priv(obj); + * + * // It is absolutely essential to use nl_dump() when printing + * // any text to make sure the dumping parameters are respected. + * nl_dump(params, "Obj Integer: %d\n", my_obj->my_int); + * + * // Before we can dump the next line, make sure to prefix + * // this line correctly. + * nl_new_line(params); + * + * // You may also split a line into multiple nl_dump() calls. + * nl_dump(params, "String: %s ", my_obj->my_string); + * nl_dump(params, "String-2: %s\n", my_obj->another_string); + * } + * + * struct nl_object_ops my_ops = { + * ... + * .oo_dump[NL_DUMP_FULL] = my_obj_dump_detailed, + * }; + * @endcode + * + * @par 4) Object Attributes + * @code + * // The concept of object attributes is optional but can ease the typical + * // case of objects that have optional attributes, e.g. a route may have a + * // nexthop assigned but it is not required to. + * + * // The first step to define your object specific bitmask listing all + * // attributes + * #define MY_ATTR_FOO (1<<0) + * #define MY_ATTR_BAR (1<<1) + * + * // Bit 31 for attributes is reserved for 32-bit API. + * + * // When assigning an optional attribute to the object, make sure + * // to mark its availability. + * my_obj->foo = 123123; + * my_obj->ce_mask |= MY_ATTR_FOO; + * + * // At any time you may use this mask to check for the availability + * // of the attribute, e.g. while dumping + * if (my_obj->ce_mask & MY_ATTR_FOO) + * nl_dump(params, "foo %d ", my_obj->foo); + * + * // One of the big advantages of this concept is that it allows for + * // standardized comparisons which make it trivial for caches to + * // identify unique objects by use of unified comparison functions. + * // In order for it to work, your object implementation must provide + * // a comparison function and define a list of attributes which + * // combined together make an object unique. + * + * static int my_obj_compare(struct nl_object *_a, struct nl_object *_b, + * uint32_t attrs, int flags) + * { + * struct my_obj *a = nl_object_priv(_a): + * struct my_obj *b = nl_object_priv(_b): + * int diff = 0; + * + * // We help ourselves in defining our own DIFF macro which will + * // call ATTR_DIFF() on both objects which will make sure to only + * // compare the attributes if required. + * #define MY_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + * + * // Call our own diff macro for each attribute to build a bitmask + * // representing the attributes which mismatch. + * diff |= MY_DIFF(MY_ATTR_FOO, a->foo != b->foo) + * diff |= MY_DIFF(MY_ATTR_BAR, strcmp(a->bar, b->bar)) + * + * return diff; + * } + * + * // In order to identify identical objects with differing attributes + * // you must specify the attributes required to uniquely identify + * // your object. Make sure to not include too many attributes, this + * // list is used when caches look for an old version of an object. + * struct nl_object_ops my_ops = { + * ... + * .oo_id_attrs = MY_ATTR_FOO, + * .oo_compare = my_obj_compare, + * }; + * @endcode + * @{ + */ + +/** + * Common Object Header + * + * This macro must be included as first member in every object + * definition to allow objects to be cached. + */ +#define NLHDR_COMMON \ + int ce_refcnt; \ + struct nl_object_ops * ce_ops; \ + struct nl_cache * ce_cache; \ + struct nl_list_head ce_list; \ + int ce_msgtype; \ + int ce_flags; \ + uint64_t ce_mask; + +struct nl_object +{ + NLHDR_COMMON +}; + + +/** + * Return true if attribute is available in both objects + * @arg A an object + * @arg B another object + * @arg ATTR attribute bit + * + * @return True if the attribute is available, otherwise false is returned. + */ +#define AVAILABLE(A, B, ATTR) (((A)->ce_mask & (B)->ce_mask) & (ATTR)) + +/** + * Return true if attribute is available in only one of both objects + * @arg A an object + * @arg B another object + * @arg ATTR attribute bit + * + * @return True if the attribute is available in only one of both objects, + * otherwise false is returned. + */ +#define AVAILABLE_MISMATCH(A, B, ATTR) (((A)->ce_mask ^ (B)->ce_mask) & (ATTR)) + +/** + * Return true if attributes mismatch + * @arg A an object + * @arg B another object + * @arg ATTR attribute bit + * @arg EXPR Comparison expression + * + * This function will check if the attribute in question is available + * in both objects, if not this will count as a mismatch. + * + * If available the function will execute the expression which must + * return true if the attributes mismatch. + * + * @return True if the attribute mismatch, or false if they match. + */ +#define ATTR_MISMATCH(A, B, ATTR, EXPR) (AVAILABLE_MISMATCH(A, B, ATTR) || \ + (AVAILABLE(A, B, ATTR) && (EXPR))) + +/** + * Return attribute bit if attribute does not match + * @arg LIST list of attributes to be compared + * @arg ATTR attribute bit + * @arg A an object + * @arg B another object + * @arg EXPR Comparison expression + * + * This function will check if the attribute in question is available + * in both objects, if not this will count as a mismatch. + * + * If available the function will execute the expression which must + * return true if the attributes mismatch. + * + * In case the attributes mismatch, the attribute is returned, otherwise + * 0 is returned. + * + * @code + * diff |= ATTR_DIFF(attrs, MY_ATTR_FOO, a, b, a->foo != b->foo); + * @endcode + */ +#define ATTR_DIFF(LIST, ATTR, A, B, EXPR) \ +({ uint64_t diff = 0; \ + if (((LIST) & (ATTR)) && ATTR_MISMATCH(A, B, ATTR, EXPR)) \ + diff = ATTR; \ + diff; }) + +/** + * Object Operations + */ +struct nl_object_ops +{ + /** + * Unique name of object type + * + * Must be in the form family/name, e.g. "route/addr" + */ + char * oo_name; + + /** Size of object including its header */ + size_t oo_size; + + /* List of attributes needed to uniquely identify the object */ + uint32_t oo_id_attrs; + + /** + * Constructor function + * + * Will be called when a new object of this type is allocated. + * Can be used to initialize members such as lists etc. + */ + void (*oo_constructor)(struct nl_object *); + + /** + * Destructor function + * + * Will be called when an object is freed. Must free all + * resources which may have been allocated as part of this + * object. + */ + void (*oo_free_data)(struct nl_object *); + + /** + * Cloning function + * + * Will be called when an object needs to be cloned. Please + * note that the generic object code will make an exact + * copy of the object first, therefore you only need to take + * care of members which require reference counting etc. + * + * May return a negative error code to abort cloning. + */ + int (*oo_clone)(struct nl_object *, struct nl_object *); + + /** + * Dumping functions + * + * Will be called when an object is dumped. The implementations + * have to use nl_dump(), nl_dump_line(), and nl_new_line() to + * dump objects. + * + * The functions must return the number of lines printed. + */ + void (*oo_dump[NL_DUMP_MAX+1])(struct nl_object *, + struct nl_dump_params *); + + /** + * Comparison function + * + * Will be called when two objects of the same type are + * compared. It takes the two objects in question, an object + * specific bitmask defining which attributes should be + * compared and flags to control the behaviour. + * + * The function must return a bitmask with the relevant bit + * set for each attribute that mismatches. + */ + uint64_t (*oo_compare)(struct nl_object *, struct nl_object *, + uint64_t, int); + + + /** + * update function + * + * Will be called when the object given by first argument + * needs to be updated with the contents of the second object + * + * The function must return 0 for success and error for failure + * to update. In case of failure its assumed that the original + * object is not touched + */ + int (*oo_update)(struct nl_object *, struct nl_object *); + + /** + * Hash Key generator function + * + * When called returns a hash key for the object being + * referenced. This key will be used by higher level hash functions + * to build association lists. Each object type gets to specify + * it's own key formulation + */ + void (*oo_keygen)(struct nl_object *, uint32_t *, uint32_t); + + char *(*oo_attrs2str)(int, char *, size_t); + + /** + * Get key attributes by family function + */ + uint32_t (*oo_id_attrs_get)(struct nl_object *); +}; + +/** @} */ + +#endif diff --git a/libnl/include/nl-priv-dynamic-route/README.md b/libnl/include/nl-priv-dynamic-route/README.md new file mode 100644 index 0000000..b27e14a --- /dev/null +++ b/libnl/include/nl-priv-dynamic-route/README.md @@ -0,0 +1,11 @@ +include/nl-priv-dynamic-route +============================= + +This exposes private API from libnl-route-3. The only purpose is for tests to +be able to access the internals. This is usable to lib/route itself, and tests +that either statically or dynamically link with libnl-route-3. + +The difference between nl-priv-static-route and nl-priv-dynamic-route, is that +the former uses internal ABI, so it is only usable when the test statically +links with lib/route. On the other hand, nl-priv-dynamic-route also works with +only the public API (that is, dynamically linking with libnl-route-3). diff --git a/libnl/include/nl-priv-dynamic-route/nl-priv-dynamic-route.h b/libnl/include/nl-priv-dynamic-route/nl-priv-dynamic-route.h new file mode 100644 index 0000000..b6192a7 --- /dev/null +++ b/libnl/include/nl-priv-dynamic-route/nl-priv-dynamic-route.h @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + * Copyright (c) 2013 Sassano Systems LLC + */ + +#ifndef __NL_PRIVATE_TYPES_NL_ROUTE_H__ +#define __NL_PRIVATE_TYPES_NL_ROUTE_H__ + +struct rtnl_ematch { + uint16_t e_id; + uint16_t e_kind; + uint16_t e_flags; + uint16_t e_index; + size_t e_datalen; + + struct nl_list_head e_childs; + struct nl_list_head e_list; + struct rtnl_ematch_ops *e_ops; + + void *e_data; +}; + +struct rtnl_ematch_tree { + uint16_t et_progid; + struct nl_list_head et_list; +}; + +/*****************************************************************************/ + +/** + * Traffic control object operations + * @ingroup tc + * + * This structure holds function pointers and settings implementing + * the features of each traffic control object implementation. + */ +struct rtnl_tc_ops { + /** + * Name of traffic control module + */ + char *to_kind; + + /** + * Type of traffic control object + */ + enum rtnl_tc_type to_type; + + /** + * Size of private data + */ + size_t to_size; + + /** + * Dump callbacks + */ + void (*to_dump[NL_DUMP_MAX + 1])(struct rtnl_tc *, void *, + struct nl_dump_params *); + /** + * Used to fill the contents of TCA_OPTIONS + */ + int (*to_msg_fill)(struct rtnl_tc *, void *, struct nl_msg *); + + /** + * Uesd to to fill tc related messages, unlike with to_msg_fill, + * the contents is not encapsulated with a TCA_OPTIONS nested + * attribute. + */ + int (*to_msg_fill_raw)(struct rtnl_tc *, void *, struct nl_msg *); + + /** + * TCA_OPTIONS message parser + */ + int (*to_msg_parser)(struct rtnl_tc *, void *); + + /** + * Called before a tc object is destroyed + */ + void (*to_free_data)(struct rtnl_tc *, void *); + + /** + * Called whenever a classifier object needs to be cloned + */ + int (*to_clone)(void *, void *); + + /** + * Internal, don't touch + */ + struct nl_list_head to_list; +}; + +extern struct rtnl_tc_ops *rtnl_tc_lookup_ops(enum rtnl_tc_type, const char *); + +struct rtnl_tc_ops *rtnl_tc_get_ops(struct rtnl_tc *); + +struct rtnl_nexthop { + uint8_t rtnh_flags; + uint8_t rtnh_flag_mask; + uint8_t rtnh_weight; + /* 1 byte spare */ + uint32_t rtnh_ifindex; + struct nl_addr *rtnh_gateway; + uint32_t ce_mask; /* HACK to support attr macros */ + struct nl_list_head rtnh_list; + uint32_t rtnh_realms; + struct nl_addr *rtnh_newdst; + struct nl_addr *rtnh_via; + struct rtnl_nh_encap *rtnh_encap; +}; + +#endif /* __NL_PRIVATE_TYPES_NL_ROUTE_H__ */ diff --git a/libnl/include/nl-priv-static-route/README.md b/libnl/include/nl-priv-static-route/README.md new file mode 100644 index 0000000..fd2fa8e --- /dev/null +++ b/libnl/include/nl-priv-static-route/README.md @@ -0,0 +1,11 @@ +include/nl-priv-static-route +============================ + +This exposes private API from libnl-route-3. The only purpose is for tests to +be able to access the internals. This is usable to lib/route itself, and to +tests that statically link with libnl-route-3. + +The difference between nl-priv-static-route and nl-priv-dynamic-route, is that +the former uses internal ABI, so it is only usable when the test statically +links with lib/route. On the other hand, nl-priv-dynamic-route also works with +only the public API (that is, dynamically linking with libnl-route-3). diff --git a/libnl/include/nl-priv-static-route/nl-priv-static-route.h b/libnl/include/nl-priv-static-route/nl-priv-static-route.h new file mode 100644 index 0000000..ad9f98f --- /dev/null +++ b/libnl/include/nl-priv-static-route/nl-priv-static-route.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#ifndef NETLINK_ROUTE_UTILS_PRIV_H_ +#define NETLINK_ROUTE_UTILS_PRIV_H_ + +#include + +extern const uint8_t *const _nltst_map_stat_id_from_IPSTATS_MIB_v2; +extern int _nl_bridge_fill_vlan_info(struct nl_msg *msg, + struct rtnl_link_bridge_vlan *vlan_info); + +#endif diff --git a/libnl/lib/addr.c b/libnl/lib/addr.c new file mode 100644 index 0000000..a3e9f99 --- /dev/null +++ b/libnl/lib/addr.c @@ -0,0 +1,1140 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +/** + * @ingroup core_types + * @defgroup addr Network Address + * + * Abstract data type representing any kind of network address + * + * Related sections in the development guide: + * - @core_doc{_abstract_address, Network Addresses} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include + +#include "mpls.h" +#include "nl-priv-dynamic-core/nl-core.h" + +/* All this DECnet stuff is stolen from iproute2, thanks to whoever wrote + * this, probably Alexey. */ +static inline uint16_t dn_ntohs(uint16_t addr) +{ + union { + uint8_t byte[2]; + uint16_t word; + } u = { + .word = addr, + }; + + return ((uint16_t) u.byte[0]) | (((uint16_t) u.byte[1]) << 8); +} + +static inline int do_digit(char *str, uint16_t *addr, uint16_t scale, + size_t *pos, size_t len, int *started) +{ + uint16_t tmp = *addr / scale; + + if (*pos == len) + return 1; + + if (((tmp) > 0) || *started || (scale == 1)) { + *str = tmp + '0'; + *started = 1; + (*pos)++; + *addr -= (tmp * scale); + } + + return 0; +} + +static const char *dnet_ntop(const char *addrbuf, size_t addrlen, char *str, + size_t len) +{ + uint16_t addr = dn_ntohs(*(uint16_t *)addrbuf); + uint16_t area = addr >> 10; + size_t pos = 0; + int started = 0; + + if (addrlen != 2) + return NULL; + + addr &= 0x03ff; + + if (len == 0) + return str; + + if (do_digit(str + pos, &area, 10, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &area, 1, &pos, len, &started)) + return str; + + if (pos == len) + return str; + + *(str + pos) = '.'; + pos++; + started = 0; + + if (do_digit(str + pos, &addr, 1000, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &addr, 100, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &addr, 10, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &addr, 1, &pos, len, &started)) + return str; + + if (pos == len) + return str; + + *(str + pos) = 0; + + return str; +} + +static int dnet_num(const char *src, uint16_t * dst) +{ + int rv = 0; + int tmp; + *dst = 0; + + while ((tmp = *src++) != 0) { + tmp -= '0'; + if ((tmp < 0) || (tmp > 9)) + return rv; + + rv++; + (*dst) *= 10; + (*dst) += tmp; + } + + return rv; +} + +static inline int dnet_pton(const char *src, char *addrbuf) +{ + uint16_t area = 0; + uint16_t node = 0; + int pos; + + pos = dnet_num(src, &area); + if ((pos == 0) || (area > 63) || + ((*(src + pos) != '.') && (*(src + pos) != ','))) + return -NLE_INVAL; + + pos = dnet_num(src + pos + 1, &node); + if ((pos == 0) || (node > 1023)) + return -NLE_INVAL; + + *(uint16_t *)addrbuf = dn_ntohs((area << 10) | node); + + return 1; +} + +static void addr_destroy(struct nl_addr *addr) +{ + if (!addr) + return; + + if (addr->a_refcnt != 1) + BUG(); + + free(addr); +} + +/** + * @name Creating Abstract Network Addresses + * @{ + */ + +/** + * Allocate empty abstract address + * @arg maxsize Upper limit of the binary address to be stored + * + * The new address object will be empty with a prefix length of 0 and will + * be capable of holding binary addresses up to the specified limit. + * + * @see nl_addr_build() + * @see nl_addr_parse() + * @see nl_addr_put() + * + * @return Allocated address object or NULL upon failure. + */ +struct nl_addr *nl_addr_alloc(size_t maxsize) +{ + struct nl_addr *addr; + + addr = calloc(1, sizeof(*addr) + maxsize); + if (!addr) + return NULL; + + addr->a_refcnt = 1; + addr->a_maxsize = maxsize; + + return addr; +} + +/** + * Allocate abstract address based on a binary address. + * @arg family Address family + * @arg buf Binary address + * @arg size Length of binary address + * + * This function will allocate an abstract address capable of holding the + * binary address specified. The prefix length will be set to the full + * length of the binary address provided. + * + * @see nl_addr_alloc() + * @see nl_addr_alloc_attr() + * @see nl_addr_parse() + * @see nl_addr_put() + * + * @return Allocated address object or NULL upon failure. + */ +struct nl_addr *nl_addr_build(int family, const void *buf, size_t size) +{ + struct nl_addr *addr; + + addr = nl_addr_alloc(size); + if (!addr) + return NULL; + + addr->a_family = family; + addr->a_len = size; + switch(family) { + case AF_MPLS: + addr->a_prefixlen = 20; /* MPLS address is a 20-bit label */ + break; + default: + addr->a_prefixlen = size*8; + } + + if (size && buf) + memcpy(addr->a_addr, buf, size); + + return addr; +} + +/** + * Allocate abstract address based on Netlink attribute. + * @arg nla Netlink attribute + * @arg family Address family. + * + * Allocates an abstract address based on the specified Netlink attribute + * by interpreting the payload of the Netlink attribute as the binary + * address. + * + * This function is identical to: + * @code + * nl_addr_build(family, nla_data(nla), nla_len(nla)); + * @endcode + * + * @see nl_addr_alloc() + * @see nl_addr_build() + * @see nl_addr_parse() + * @see nl_addr_put() + * + * @return Allocated address object or NULL upon failure. + */ +struct nl_addr *nl_addr_alloc_attr(const struct nlattr *nla, int family) +{ + return nl_addr_build(family, nla_data(nla), nla_len(nla)); +} + +/** + * Allocate abstract address based on character string + * @arg addrstr Address represented as character string. + * @arg hint Address family hint or AF_UNSPEC. + * @arg result Pointer to store resulting address. + * + * Regognizes the following address formats: + * @code + * Format Len Family + * ---------------------------------------------------------------- + * IPv6 address format 16 AF_INET6 + * ddd.ddd.ddd.ddd 4 AF_INET + * HH:HH:HH:HH:HH:HH 6 AF_LLC + * AA{.|,}NNNN 2 AF_DECnet + * HH:HH:HH:... variable AF_UNSPEC + * @endcode + * + * Special values: + * - none: All bits and length set to 0. + * - {default|all|any}: All bits set to 0, length based on hint or + * AF_INET if no hint is given. + * + * The prefix length may be appened at the end prefixed with a + * slash, e.g. 10.0.0.0/8. + * + * @see nl_addr_alloc() + * @see nl_addr_build() + * @see nl_addr_put() + * + * @return 0 on success or a negative error code. + */ +int nl_addr_parse(const char *addrstr, int hint, struct nl_addr **result) +{ + int err, copy = 0, len = 0, family = AF_UNSPEC, plen = 0; + char *str, *prefix = NULL, buf[256]; + struct nl_addr *addr = NULL; /* gcc ain't that smart */ + + str = strdup(addrstr); + if (!str) { + err = -NLE_NOMEM; + goto errout; + } + + if (hint != AF_MPLS) { + prefix = strchr(str, '/'); + if (prefix) + *prefix = '\0'; + } + + if (!strcasecmp(str, "none")) { + family = hint; + goto prefix; + } + + if (!strcasecmp(str, "default") || + !strcasecmp(str, "all") || + !strcasecmp(str, "any")) { + + switch (hint) { + case AF_INET: + case AF_UNSPEC: + /* Kind of a hack, we assume that if there is + * no hint given the user wants to have a IPv4 + * address given back. */ + family = AF_INET; + len = 4; + goto prefix; + + case AF_INET6: + family = AF_INET6; + len = 16; + goto prefix; + + case AF_LLC: + family = AF_LLC; + len = 6; + goto prefix; + + default: + err = -NLE_AF_NOSUPPORT; + goto errout; + } + } + + copy = 1; + + if (hint == AF_INET || hint == AF_UNSPEC) { + if (inet_pton(AF_INET, str, buf) > 0) { + family = AF_INET; + len = 4; + goto prefix; + } + if (hint == AF_INET) { + err = -NLE_NOADDR; + goto errout; + } + } + + if (hint == AF_INET6 || hint == AF_UNSPEC) { + if (inet_pton(AF_INET6, str, buf) > 0) { + family = AF_INET6; + len = 16; + goto prefix; + } + if (hint == AF_INET6) { + err = -NLE_NOADDR; + goto errout; + } + } + + if ((hint == AF_LLC || hint == AF_UNSPEC) && strchr(str, ':')) { + unsigned int a, b, c, d, e, f; + + if (sscanf(str, "%02x:%02x:%02x:%02x:%02x:%02x", + &a, &b, &c, &d, &e, &f) == 6) { + family = AF_LLC; + len = 6; + buf[0] = (unsigned char) a; + buf[1] = (unsigned char) b; + buf[2] = (unsigned char) c; + buf[3] = (unsigned char) d; + buf[4] = (unsigned char) e; + buf[5] = (unsigned char) f; + goto prefix; + } + + if (hint == AF_LLC) { + err = -NLE_NOADDR; + goto errout; + } + } + + if ((hint == AF_DECnet || hint == AF_UNSPEC) && + (strchr(str, '.') || strchr(str, ','))) { + if (dnet_pton(str, buf) > 0) { + family = AF_DECnet; + len = 2; + goto prefix; + } + if (hint == AF_DECnet) { + err = -NLE_NOADDR; + goto errout; + } + } + + if (hint == AF_MPLS) { + len = mpls_pton(AF_MPLS, str, buf, sizeof(buf)); + if (len <= 0) { + err = -NLE_INVAL; + goto errout; + } + family = AF_MPLS; + plen = 20; + goto prefix; + } + + if (hint == AF_UNSPEC && strchr(str, ':')) { + size_t i = 0; + char *s = str, *p; + for (;;) { + long l = strtol(s, &p, 16); + + if (s == p || l > 0xff || i >= sizeof(buf)) { + err = -NLE_INVAL; + goto errout; + } + + buf[i++] = (unsigned char) l; + if (*p == '\0') + break; + s = ++p; + } + + len = i; + family = AF_UNSPEC; + goto prefix; + } + + err = -NLE_NOADDR; + goto errout; + +prefix: + addr = nl_addr_alloc(len); + if (!addr) { + err = -NLE_NOMEM; + goto errout; + } + + nl_addr_set_family(addr, family); + + if (copy) + nl_addr_set_binary_addr(addr, buf, len); + else + addr->a_len = len; + + if (prefix) { + char *p; + long pl = strtol(++prefix, &p, 0); + if (p == prefix) { + addr_destroy(addr); + err = -NLE_INVAL; + goto errout; + } + nl_addr_set_prefixlen(addr, pl); + } else { + if (copy && !plen) + plen = len * 8; + nl_addr_set_prefixlen(addr, plen); + } + *result = addr; + err = 0; +errout: + free(str); + + return err; +} + +/** + * Clone existing abstract address object + * @arg addr Abstract address object + * + * Allocates new abstract address representing an identical clone of an + * existing address. + * + * @see nl_addr_alloc() + * @see nl_addr_put() + * + * @return Allocated abstract address or NULL upon failure. + */ +struct nl_addr *nl_addr_clone(const struct nl_addr *addr) +{ + struct nl_addr *new; + + new = nl_addr_build(addr->a_family, addr->a_addr, addr->a_len); + if (new) + new->a_prefixlen = addr->a_prefixlen; + + return new; +} + +/** @} */ + +/** + * @name Managing Usage References + * @{ + */ + +/** + * Increase the reference counter of an abstract address + * @arg addr Abstract address + * + * Increases the reference counter of the address and thus prevents the + * release of the memory resources until the reference is given back + * using the function nl_addr_put(). + * + * @see nl_addr_put() + * + * @return Pointer to the existing abstract address + */ +struct nl_addr *nl_addr_get(struct nl_addr *addr) +{ + addr->a_refcnt++; + + return addr; +} + +/** + * Decrease the reference counter of an abstract address + * @arg addr Abstract addr + * + * @note The resources of the abstract address will be freed after the + * last reference to the address has been returned. + * + * @see nl_addr_get() + */ +void nl_addr_put(struct nl_addr *addr) +{ + if (!addr) + return; + + if (addr->a_refcnt == 1) + addr_destroy(addr); + else + addr->a_refcnt--; +} + +/** + * Check whether an abstract address is shared. + * @arg addr Abstract address object. + * + * @return Non-zero if the abstract address is shared, otherwise 0. + */ +int nl_addr_shared(const struct nl_addr *addr) +{ + return addr->a_refcnt > 1; +} + +/** @} */ + +/** + * @name Miscellaneous + * @{ + */ + +/** + * Compare abstract addresses + * @arg a An abstract address + * @arg b Another abstract address + * + * Verifies whether the address family, address length, prefix length, and + * binary addresses of two abstract addresses matches. + * + * @note This function will *not* respect the prefix length in the sense + * that only the actual prefix will be compared. Please refer to the + * nl_addr_cmp_prefix() function if you require this functionality. + * + * @see nl_addr_cmp_prefix() + * + * @return Integer less than, equal to or greather than zero if the two + * addresses match. + */ +int nl_addr_cmp(const struct nl_addr *a, const struct nl_addr *b) +{ + int d; + + if (a == b) + return 0; + if (!a) + return -1; + if (!b) + return 1; + + d = a->a_family - b->a_family; + if (d == 0) { + d = a->a_len - b->a_len; + + if (a->a_len && d == 0) { + d = memcmp(a->a_addr, b->a_addr, a->a_len); + + if (d == 0) + return (a->a_prefixlen - b->a_prefixlen); + } + } + + return d; +} + +/** + * Compare the prefix of two abstract addresses + * @arg a An abstract address + * @arg b Another abstract address + * + * Verifies whether the address family and the binary address covered by + * the smaller prefix length of the two abstract addresses matches. + * + * @see nl_addr_cmp() + * + * @return Integer less than, equal to or greather than zero if the two + * addresses match. + */ +int nl_addr_cmp_prefix(const struct nl_addr *a, const struct nl_addr *b) +{ + int d = a->a_family - b->a_family; + + if (d == 0) { + int len = _NL_MIN(a->a_prefixlen, b->a_prefixlen); + int bytes = len / 8; + + d = memcmp(a->a_addr, b->a_addr, bytes); + if (d == 0 && (len % 8) != 0) { + int mask = (0xFF00 >> (len % 8)) & 0xFF; + + d = (a->a_addr[bytes] & mask) - + (b->a_addr[bytes] & mask); + } + } + + return d; +} + +/** + * Returns true if the address consists of all zeros + * @arg addr Abstract address + * + * @return 1 if the binary address consists of all zeros, 0 otherwise. + */ +int nl_addr_iszero(const struct nl_addr *addr) +{ + unsigned int i; + + for (i = 0; i < addr->a_len; i++) + if (addr->a_addr[i]) + return 0; + + return 1; +} + +/** + * Check if address string is parseable for a specific address family + * @arg addr Address represented as character string. + * @arg family Desired address family. + * + * @return 1 if the address is parseable assuming the specified address family, + * otherwise 0 is returned. + */ +int nl_addr_valid(const char *addr, int family) +{ + int ret; + char buf[256]; /* MPLS has N-labels at 4-bytes / label */ + + switch (family) { + case AF_INET: + case AF_INET6: + ret = inet_pton(family, addr, buf); + if (ret <= 0) + return 0; + break; + + case AF_MPLS: + ret = mpls_pton(family, addr, buf, sizeof(buf)); + if (ret <= 0) + return 0; + break; + + case AF_DECnet: + ret = dnet_pton(addr, buf); + if (ret <= 0) + return 0; + break; + + case AF_LLC: + if (sscanf(addr, "%*02x:%*02x:%*02x:%*02x:%*02x:%*02x") != 6) + return 0; + break; + } + + return 1; +} + +/** + * Guess address family of abstract address based on address size + * @arg addr Abstract address object. + * + * @return Numeric address family or AF_UNSPEC + */ +int nl_addr_guess_family(const struct nl_addr *addr) +{ + switch (addr->a_len) { + case 4: + return AF_INET; + case 6: + return AF_LLC; + case 16: + return AF_INET6; + default: + return AF_UNSPEC; + } +} + +/** + * Fill out sockaddr structure with values from abstract address object. + * @arg addr Abstract address object. + * @arg sa Destination sockaddr structure buffer. + * @arg salen Length of sockaddr structure buffer. + * + * Fills out the specified sockaddr structure with the data found in the + * specified abstract address. The salen argument needs to be set to the + * size of sa but will be modified to the actual size used during before + * the function exits. + * + * @return 0 on success or a negative error code + */ +int nl_addr_fill_sockaddr(const struct nl_addr *addr, struct sockaddr *sa, + socklen_t *salen) +{ + switch (addr->a_family) { + case AF_INET: { + struct sockaddr_in *sai = (struct sockaddr_in *) sa; + + if (*salen < sizeof(*sai)) + return -NLE_INVAL; + + if (addr->a_len == 4) + memcpy(&sai->sin_addr, addr->a_addr, 4); + else if (addr->a_len != 0) + return -NLE_INVAL; + else + memset(&sai->sin_addr, 0, 4); + + sai->sin_family = addr->a_family; + *salen = sizeof(*sai); + } + break; + + case AF_INET6: { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) sa; + + if (*salen < sizeof(*sa6)) + return -NLE_INVAL; + + if (addr->a_len == 16) + memcpy(&sa6->sin6_addr, addr->a_addr, 16); + else if (addr->a_len != 0) + return -NLE_INVAL; + else + memset(&sa6->sin6_addr, 0, 16); + + sa6->sin6_family = addr->a_family; + *salen = sizeof(*sa6); + } + break; + + default: + return -NLE_INVAL; + } + + return 0; +} + + +/** @} */ + +/** + * @name Getting Information About Addresses + * @{ + */ + +/** + * Call getaddrinfo() for an abstract address object. + * @arg addr Abstract address object. + * @arg result Pointer to store resulting address list. + * + * Calls getaddrinfo() for the specified abstract address in AI_NUMERICHOST + * mode. + * + * @note The caller is responsible for freeing the linked list using the + * interface provided by getaddrinfo(3). + * + * @return 0 on success or a negative error code. + */ +int nl_addr_info(const struct nl_addr *addr, struct addrinfo **result) +{ + int err; + char buf[INET6_ADDRSTRLEN+5]; + struct addrinfo hint = { + .ai_flags = AI_NUMERICHOST, + .ai_family = addr->a_family, + }; + + nl_addr2str(addr, buf, sizeof(buf)); + + err = getaddrinfo(buf, NULL, &hint, result); + if (err != 0) { + switch (err) { + case EAI_ADDRFAMILY: return -NLE_AF_NOSUPPORT; + case EAI_AGAIN: return -NLE_AGAIN; + case EAI_BADFLAGS: return -NLE_INVAL; + case EAI_FAIL: return -NLE_NOADDR; + case EAI_FAMILY: return -NLE_AF_NOSUPPORT; + case EAI_MEMORY: return -NLE_NOMEM; + case EAI_NODATA: return -NLE_NOADDR; + case EAI_NONAME: return -NLE_OBJ_NOTFOUND; + case EAI_SERVICE: return -NLE_OPNOTSUPP; + case EAI_SOCKTYPE: return -NLE_BAD_SOCK; + default: return -NLE_FAILURE; + } + } + + return 0; +} + +/** + * Resolve abstract address object to a name using getnameinfo(). + * @arg addr Abstract address object. + * @arg host Destination buffer for host name. + * @arg hostlen Length of destination buffer. + * + * Resolves the abstract address to a name and writes the looked up result + * into the host buffer. getnameinfo() is used to perform the lookup and + * is put into NI_NAMEREQD mode so the function will fail if the lookup + * couldn't be performed. + * + * @return 0 on success or a negative error code. + */ +int nl_addr_resolve(const struct nl_addr *addr, char *host, size_t hostlen) +{ + int err; + struct sockaddr_in6 buf; + socklen_t salen = sizeof(buf); + + err = nl_addr_fill_sockaddr(addr, (struct sockaddr *) &buf, &salen); + if (err < 0) + return err; + + err = getnameinfo((struct sockaddr *) &buf, salen, host, hostlen, + NULL, 0, NI_NAMEREQD); + if (err < 0) + return nl_syserr2nlerr(err); + + return 0; +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +/** + * Set address family + * @arg addr Abstract address object + * @arg family Address family + * + * @see nl_addr_get_family() + */ +void nl_addr_set_family(struct nl_addr *addr, int family) +{ + addr->a_family = family; +} + +/** + * Return address family + * @arg addr Abstract address object + * + * @see nl_addr_set_family() + * + * @return The numeric address family or `AF_UNSPEC` + */ +int nl_addr_get_family(const struct nl_addr *addr) +{ + return addr->a_family; +} + +/** + * Set binary address of abstract address object. + * @arg addr Abstract address object. + * @arg buf Buffer containing binary address. + * @arg len Length of buffer containing binary address. + * + * Modifies the binary address portion of the abstract address. The + * abstract address must be capable of holding the required amount + * or this function will fail. + * + * @note This function will *not* modify the prefix length. It is within + * the responsibility of the caller to set the prefix length to the + * desirable length. + * + * @see nl_addr_alloc() + * @see nl_addr_get_binary_addr() + * @see nl_addr_get_len() + * + * @return 0 on success or a negative error code. + */ +int nl_addr_set_binary_addr(struct nl_addr *addr, const void *buf, size_t len) +{ + if (len > addr->a_maxsize) + return -NLE_RANGE; + + addr->a_len = len; + memset(addr->a_addr, 0, addr->a_maxsize); + + if (len) + memcpy(addr->a_addr, buf, len); + + return 0; +} + +/** + * Get binary address of abstract address object. + * @arg addr Abstract address object. + * + * @see nl_addr_set_binary_addr() + * @see nl_addr_get_len() + * + * @return Pointer to binary address of length nl_addr_get_len() + */ +void *nl_addr_get_binary_addr(const struct nl_addr *addr) +{ + return (void*)addr->a_addr; +} + +/** + * Get length of binary address of abstract address object. + * @arg addr Abstract address object. + * + * @see nl_addr_get_binary_addr() + * @see nl_addr_set_binary_addr() + */ +unsigned int nl_addr_get_len(const struct nl_addr *addr) +{ + return addr->a_len; +} + +/** + * Set the prefix length of an abstract address + * @arg addr Abstract address object + * @arg prefixlen New prefix length + * + * @see nl_addr_get_prefixlen() + */ +void nl_addr_set_prefixlen(struct nl_addr *addr, int prefixlen) +{ + addr->a_prefixlen = prefixlen; +} + +/** + * Return prefix length of abstract address object. + * @arg addr Abstract address object + * + * @see nl_addr_set_prefixlen() + */ +unsigned int nl_addr_get_prefixlen(const struct nl_addr *addr) +{ + return addr->a_prefixlen; +} + +/** @} */ + +/** + * @name Translations to Strings + * @{ + */ + +/** + * Convert abstract address object to character string. + * @arg addr Abstract address object. + * @arg buf Destination buffer. + * @arg size Size of destination buffer. + * + * Converts an abstract address to a character string and stores + * the result in the specified destination buffer. + * + * @return Address represented in ASCII stored in destination buffer. + */ +char *nl_addr2str(const struct nl_addr *addr, char *buf, size_t size) +{ + unsigned int i; + char tmp[16]; + + if (!addr || !addr->a_len) { + snprintf(buf, size, "none"); + if (addr) + goto prefix; + else + return buf; + } + + switch (addr->a_family) { + case AF_INET: + inet_ntop(AF_INET, addr->a_addr, buf, size); + break; + + case AF_INET6: + inet_ntop(AF_INET6, addr->a_addr, buf, size); + break; + + case AF_MPLS: + mpls_ntop(AF_MPLS, addr->a_addr, buf, size); + break; + + case AF_DECnet: + dnet_ntop(addr->a_addr, addr->a_len, buf, size); + break; + + case AF_LLC: + default: + snprintf(buf, size, "%02x", + (unsigned char) addr->a_addr[0]); + for (i = 1; i < addr->a_len; i++) { + snprintf(tmp, sizeof(tmp), ":%02x", + (unsigned char) addr->a_addr[i]); + strncat(buf, tmp, size - strlen(buf) - 1); + } + break; + } + +prefix: + if (addr->a_family != AF_MPLS && + (unsigned)addr->a_prefixlen != (8u * ((size_t)addr->a_len))) { + snprintf(tmp, sizeof(tmp), "/%d", addr->a_prefixlen); + strncat(buf, tmp, size - strlen(buf) - 1); + } + + return buf; +} + +/** @} */ + +/** + * @name Address Family Transformations + * @{ + */ + +static const struct trans_tbl afs[] = { + __ADD(AF_UNSPEC,unspec), + __ADD(AF_UNIX,unix), + __ADD(AF_INET,inet), + __ADD(AF_AX25,ax25), + __ADD(AF_IPX,ipx), + __ADD(AF_APPLETALK,appletalk), + __ADD(AF_NETROM,netrom), + __ADD(AF_BRIDGE,bridge), + __ADD(AF_ATMPVC,atmpvc), + __ADD(AF_X25,x25), + __ADD(AF_INET6,inet6), + __ADD(AF_ROSE,rose), + __ADD(AF_DECnet,decnet), + __ADD(AF_NETBEUI,netbeui), + __ADD(AF_SECURITY,security), + __ADD(AF_KEY,key), + __ADD(AF_NETLINK,netlink), + __ADD(AF_PACKET,packet), + __ADD(AF_ASH,ash), + __ADD(AF_ECONET,econet), + __ADD(AF_ATMSVC,atmsvc), +#ifdef AF_RDS + __ADD(AF_RDS,rds), +#endif + __ADD(AF_SNA,sna), + __ADD(AF_IRDA,irda), + __ADD(AF_PPPOX,pppox), + __ADD(AF_WANPIPE,wanpipe), + __ADD(AF_LLC,llc), +#ifdef AF_CAN + __ADD(AF_CAN,can), +#endif +#ifdef AF_TIPC + __ADD(AF_TIPC,tipc), +#endif + __ADD(AF_BLUETOOTH,bluetooth), +#ifdef AF_IUCV + __ADD(AF_IUCV,iucv), +#endif +#ifdef AF_RXRPC + __ADD(AF_RXRPC,rxrpc), +#endif +#ifdef AF_ISDN + __ADD(AF_ISDN,isdn), +#endif +#ifdef AF_PHONET + __ADD(AF_PHONET,phonet), +#endif +#ifdef AF_IEEE802154 + __ADD(AF_IEEE802154,ieee802154), +#endif +#ifdef AF_CAIF + __ADD(AF_CAIF,caif), +#endif +#ifdef AF_ALG + __ADD(AF_ALG,alg), +#endif +#ifdef AF_NFC + __ADD(AF_NFC,nfc), +#endif +#ifdef AF_VSOCK + __ADD(AF_VSOCK,vsock), +#endif + __ADD(AF_MPLS,mpls), +}; + +char *nl_af2str(int family, char *buf, size_t size) +{ + return __type2str(family, buf, size, afs, ARRAY_SIZE(afs)); +} + +int nl_str2af(const char *name) +{ + int fam = __str2type(name, afs, ARRAY_SIZE(afs)); + return fam >= 0 ? fam : -EINVAL; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/attr.c b/libnl/lib/attr.c new file mode 100644 index 0000000..5046197 --- /dev/null +++ b/libnl/lib/attr.c @@ -0,0 +1,1120 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include + +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-aux-core/nl-core.h" + +/** + * @ingroup msg + * @defgroup attr Attributes + * Netlink Attributes Construction/Parsing Interface + * + * Related sections in the development guide: + * - @core_doc{core_attr,Netlink Attributes} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +/** + * @name Attribute Size Calculation + * @{ + */ + +/** + * Return size of attribute whithout padding. + * @arg payload Payload length of attribute. + * + * @code + * <-------- nla_attr_size(payload) ---------> + * +------------------+- - -+- - - - - - - - - +- - -+ + * | Attribute Header | Pad | Payload | Pad | + * +------------------+- - -+- - - - - - - - - +- - -+ + * @endcode + * + * @return Size of attribute in bytes without padding. + */ +int nla_attr_size(int payload) +{ + return NLA_HDRLEN + payload; +} + +/** + * Return size of attribute including padding. + * @arg payload Payload length of attribute. + * + * @code + * <----------- nla_total_size(payload) -----------> + * +------------------+- - -+- - - - - - - - - +- - -+ + * | Attribute Header | Pad | Payload | Pad | + * +------------------+- - -+- - - - - - - - - +- - -+ + * @endcode + * + * @return Size of attribute in bytes. + */ +int nla_total_size(int payload) +{ + return NLA_ALIGN(nla_attr_size(payload)); +} + +/** + * Return length of padding at the tail of the attribute. + * @arg payload Payload length of attribute. + * + * @code + * +------------------+- - -+- - - - - - - - - +- - -+ + * | Attribute Header | Pad | Payload | Pad | + * +------------------+- - -+- - - - - - - - - +- - -+ + * <---> + * @endcode + * + * @return Length of padding in bytes. + */ +int nla_padlen(int payload) +{ + return nla_total_size(payload) - nla_attr_size(payload); +} + +/** @} */ + +/** + * @name Parsing Attributes + * @{ + */ + +/** + * Return type of the attribute. + * @arg nla Attribute. + * + * @return Type of attribute. + */ +int nla_type(const struct nlattr *nla) +{ + return nla->nla_type & NLA_TYPE_MASK; +} + +/** + * Return pointer to the payload section. + * @arg nla Attribute. + * + * @return Pointer to start of payload section. + */ +void *nla_data(const struct nlattr *nla) +{ + return (char *) nla + NLA_HDRLEN; +} + +/** + * Return length of the payload . + * @arg nla Attribute + * + * @return Length of payload in bytes. + */ +int nla_len(const struct nlattr *nla) +{ + return _nla_len(nla); +} + +/** + * Check if the attribute header and payload can be accessed safely. + * @arg nla Attribute of any kind. + * @arg remaining Number of bytes remaining in attribute stream. + * + * Verifies that the header and payload do not exceed the number of + * bytes left in the attribute stream. This function must be called + * before access the attribute header or payload when iterating over + * the attribute stream using nla_next(). + * + * @return True if the attribute can be accessed safely, false otherwise. + */ +int nla_ok(const struct nlattr *nla, int remaining) +{ + _NL_STATIC_ASSERT(sizeof(*nla) == NLA_HDRLEN); + + return remaining >= (int) sizeof(*nla) && + nla->nla_len >= sizeof(*nla) && + nla->nla_len <= remaining; +} + +/** + * Return next attribute in a stream of attributes. + * @arg nla Attribute of any kind. + * @arg remaining Variable to count remaining bytes in stream. + * + * Calculates the offset to the next attribute based on the attribute + * given. The attribute provided is assumed to be accessible, the + * caller is responsible to use nla_ok() beforehand. The offset (length + * of specified attribute including padding) is then subtracted from + * the remaining bytes variable and a pointer to the next attribute is + * returned. + * + * nla_next() can be called as long as remainig is >0. + * + * @return Pointer to next attribute. + */ +struct nlattr *nla_next(const struct nlattr *nla, int *remaining) +{ + int totlen = NLA_ALIGN(nla->nla_len); + + *remaining -= totlen; + return (struct nlattr *) ((char *) nla + totlen); +} + +static uint16_t nla_attr_minlen[NLA_TYPE_MAX+1] = { + [NLA_U8] = sizeof(uint8_t), + [NLA_U16] = sizeof(uint16_t), + [NLA_U32] = sizeof(uint32_t), + [NLA_U64] = sizeof(uint64_t), + [NLA_STRING] = 1, + [NLA_FLAG] = 0, + [NLA_SINT] = sizeof(uint32_t), + [NLA_UINT] = sizeof(uint32_t), +}; + +static int validate_nla(const struct nlattr *nla, int maxtype, + const struct nla_policy *policy) +{ + const struct nla_policy *pt; + unsigned int minlen = 0; + int type = nla_type(nla); + + if (type < 0 || type > maxtype) + return 0; + + pt = &policy[type]; + + if (pt->type > NLA_TYPE_MAX) + BUG(); + + if (pt->minlen) + minlen = pt->minlen; + else if (pt->type != NLA_UNSPEC) + minlen = nla_attr_minlen[pt->type]; + + if (_nla_len(nla) < minlen) + return -NLE_RANGE; + + if (pt->maxlen && nla_len(nla) > pt->maxlen) + return -NLE_RANGE; + + if (pt->type == NLA_STRING) { + const char *data = nla_data(nla); + if (data[nla_len(nla) - 1] != '\0') + return -NLE_INVAL; + } + + return 0; +} + + +/** + * Create attribute index based on a stream of attributes. + * @arg tb Index array to be filled (maxtype+1 elements). + * @arg maxtype Maximum attribute type expected and accepted. + * @arg head Head of attribute stream. + * @arg len Length of attribute stream. + * @arg policy Attribute validation policy. + * + * Iterates over the stream of attributes and stores a pointer to each + * attribute in the index array using the attribute type as index to + * the array. Attribute with a type greater than the maximum type + * specified will be silently ignored in order to maintain backwards + * compatibility. If \a policy is not NULL, the attribute will be + * validated using the specified policy. + * + * @see nla_validate + * @return 0 on success or a negative error code. + */ +int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len, + const struct nla_policy *policy) +{ + struct nlattr *nla; + int rem, err; + + memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); + + nla_for_each_attr(nla, head, len, rem) { + int type = nla_type(nla); + + if (type > maxtype) + continue; + + if (policy) { + err = validate_nla(nla, maxtype, policy); + if (err < 0) + return err; + } + + if (tb[type]) + NL_DBG(1, "Attribute of type %#x found multiple times in message, " + "previous attribute is being ignored.\n", type); + + tb[type] = nla; + } + + if (rem > 0) { + NL_DBG(1, "netlink: %d bytes leftover after parsing " + "attributes.\n", rem); + } + + return 0; +} + +/** + * Validate a stream of attributes. + * @arg head Head of attributes stream. + * @arg len Length of attributes stream. + * @arg maxtype Maximum attribute type expected and accepted. + * @arg policy Validation policy. + * + * Iterates over the stream of attributes and validates each attribute + * one by one using the specified policy. Attributes with a type greater + * than the maximum type specified will be silently ignored in order to + * maintain backwards compatibility. + * + * See section @core_doc{core_attr_parse,Attribute Parsing} for more details. + * + * @return 0 on success or a negative error code. + */ +int nla_validate(const struct nlattr *head, int len, int maxtype, + const struct nla_policy *policy) +{ + const struct nlattr *nla; + int rem, err; + + nla_for_each_attr(nla, head, len, rem) { + err = validate_nla(nla, maxtype, policy); + if (err < 0) + goto errout; + } + + err = 0; +errout: + return err; +} + +/** + * Find a single attribute in a stream of attributes. + * @arg head Head of attributes stream. + * @arg len Length of attributes stream. + * @arg attrtype Attribute type to look for. + * + * Iterates over the stream of attributes and compares each type with + * the type specified. Returns the first attribute which matches the + * type. + * + * @return Pointer to attribute found or NULL. + */ +struct nlattr *nla_find(const struct nlattr *head, int len, int attrtype) +{ + const struct nlattr *nla; + int rem; + + nla_for_each_attr(nla, head, len, rem) + if (nla_type(nla) == attrtype) + return (struct nlattr*)nla; + + return NULL; +} + +/** @} */ + +/** + * @name Helper Functions + * @{ + */ + +/** + * Copy attribute payload to another memory area. + * @arg dest Pointer to destination memory area. + * @arg src Attribute + * @arg count Number of bytes to copy at most. + * + * Note: The number of bytes copied is limited by the length of + * the attribute payload. + * + * @return The number of bytes copied to dest. + */ +int nla_memcpy(void *dest, const struct nlattr *src, int count) +{ + int minlen; + + if (!src) + return 0; + + minlen = _NL_MIN(count, nla_len(src)); + + if (minlen <= 0) + return 0; + + memcpy(dest, nla_data(src), minlen); + return minlen; +} + +/** + * Copy string attribute payload to a buffer. + * @arg dst Pointer to destination buffer. + * @arg nla Attribute of type NLA_STRING. + * @arg dstsize Size of destination buffer in bytes. + * + * Copies at most dstsize - 1 bytes to the destination buffer. + * The result is always a valid NUL terminated string. Unlike + * strlcpy the destination buffer is always padded out. + * + * @return The length of string attribute without the terminating NUL. + */ +size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize) +{ + size_t srclen = nla_len(nla); + const char *src = nla_data(nla); + + if (srclen > 0 && src[srclen - 1] == '\0') + srclen--; + + if (dstsize > 0) { + size_t len = (srclen >= dstsize) ? dstsize - 1 : srclen; + + memset(dst, 0, dstsize); + memcpy(dst, src, len); + } + + return srclen; +} + +/** + * Compare attribute payload with memory area. + * @arg nla Attribute. + * @arg data Memory area to compare to. + * @arg size Number of bytes to compare. + * + * @see memcmp(3) + * @return An integer less than, equal to, or greater than zero. + */ +int nla_memcmp(const struct nlattr *nla, const void *data, size_t size) +{ + int d = nla_len(nla) - size; + + if (d == 0) + d = memcmp(nla_data(nla), data, size); + + return d; +} + +/** + * Compare string attribute payload with string + * @arg nla Attribute of type NLA_STRING. + * @arg str NUL terminated string. + * + * @see strcmp(3) + * @return An integer less than, equal to, or greater than zero. + */ +int nla_strcmp(const struct nlattr *nla, const char *str) +{ + int len = strlen(str) + 1; + int d = nla_len(nla) - len; + + if (d == 0) + d = memcmp(nla_data(nla), str, len); + + return d; +} + +/** @} */ + +/** + * @name Unspecific Attribute + * @{ + */ + +/** + * Reserve space for a attribute. + * @arg msg Netlink Message. + * @arg attrtype Attribute Type. + * @arg attrlen Length of payload. + * + * Reserves room for a attribute in the specified netlink message and + * fills in the attribute header (type, length). Returns NULL if there + * is unsuficient space for the attribute. + * + * Any padding between payload and the start of the next attribute is + * zeroed out. + * + * @return Pointer to start of attribute or NULL on failure. + */ +struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int attrlen) +{ + struct nlattr *nla; + size_t tlen; + + if (attrlen < 0) + return NULL; + + tlen = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + nla_total_size(attrlen); + + if (tlen > msg->nm_size || tlen > UINT32_MAX) + return NULL; + + nla = (struct nlattr *) nlmsg_tail(msg->nm_nlh); + nla->nla_type = attrtype; + nla->nla_len = nla_attr_size(attrlen); + + if (attrlen) + memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen)); + msg->nm_nlh->nlmsg_len = tlen; + + NL_DBG(2, "msg %p: attr <%p> %d: Reserved %d (%d) bytes at offset +%td " + "nlmsg_len=%d\n", msg, nla, nla->nla_type, + nla_total_size(attrlen), attrlen, + (char *) nla - (char *) nlmsg_data(msg->nm_nlh), + msg->nm_nlh->nlmsg_len); + + return nla; +} + +/** + * Add a unspecific attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg datalen Length of data to be used as payload. + * @arg data Pointer to data to be used as attribute payload. + * + * Reserves room for a unspecific attribute and copies the provided data + * into the message as payload of the attribute. Returns an error if there + * is insufficient space for the attribute. + * + * @see nla_reserve + * @return 0 on success or a negative error code. + */ +int nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data) +{ + struct nlattr *nla; + + nla = nla_reserve(msg, attrtype, datalen); + if (!nla) { + if (datalen < 0) + return -NLE_INVAL; + + return -NLE_NOMEM; + } + + if (datalen > 0) { + memcpy(nla_data(nla), data, datalen); + NL_DBG(2, "msg %p: attr <%p> %d: Wrote %d bytes at offset +%td\n", + msg, nla, nla->nla_type, datalen, + (char *) nla - (char *) nlmsg_data(msg->nm_nlh)); + } + + return 0; +} + +/** + * Add abstract data as unspecific attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg data Abstract data object. + * + * Equivalent to nla_put() except that the length of the payload is + * derived from the abstract data object. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_data(struct nl_msg *msg, int attrtype, const struct nl_data *data) +{ + return nla_put(msg, attrtype, nl_data_get_size(data), + nl_data_get(data)); +} + +/** + * Add abstract address as unspecific attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg addr Abstract address object. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_addr(struct nl_msg *msg, int attrtype, struct nl_addr *addr) +{ + if (nl_addr_get_len(addr) == 0) + return -NLE_INVAL; + + return nla_put(msg, attrtype, nl_addr_get_len(addr), + nl_addr_get_binary_addr(addr)); +} + +/** @} */ + +/** + * @name Integer Attributes + */ + +/** + * Add 8 bit signed integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_s8(struct nl_msg *msg, int attrtype, int8_t value) +{ + return nla_put(msg, attrtype, sizeof(int8_t), &value); +} + +/** + * Return value of 8 bit signed integer attribute. + * @arg nla 8 bit integer attribute + * + * @return Payload as 8 bit integer. + */ +int8_t nla_get_s8(const struct nlattr *nla) +{ + return *(const int8_t *) nla_data(nla); +} + +/** + * Add 8 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_u8(struct nl_msg *msg, int attrtype, uint8_t value) +{ + return nla_put(msg, attrtype, sizeof(uint8_t), &value); +} + +/** + * Return value of 8 bit integer attribute. + * @arg nla 8 bit integer attribute + * + * @return Payload as 8 bit integer. + */ +uint8_t nla_get_u8(const struct nlattr *nla) +{ + return *(const uint8_t *) nla_data(nla); +} + +/** + * Add 16 bit signed integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_s16(struct nl_msg *msg, int attrtype, int16_t value) +{ + return nla_put(msg, attrtype, sizeof(int16_t), &value); +} + +/** + * Return payload of 16 bit signed integer attribute. + * @arg nla 16 bit integer attribute + * + * @return Payload as 16 bit integer. + */ +int16_t nla_get_s16(const struct nlattr *nla) +{ + return *(const int16_t *) nla_data(nla); +} + +/** + * Add 16 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_u16(struct nl_msg *msg, int attrtype, uint16_t value) +{ + return nla_put(msg, attrtype, sizeof(uint16_t), &value); +} + +/** + * Return payload of 16 bit integer attribute. + * @arg nla 16 bit integer attribute + * + * @return Payload as 16 bit integer. + */ +uint16_t nla_get_u16(const struct nlattr *nla) +{ + return *(const uint16_t *) nla_data(nla); +} + +/** + * Add 32 bit signed integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_s32(struct nl_msg *msg, int attrtype, int32_t value) +{ + return nla_put(msg, attrtype, sizeof(int32_t), &value); +} + +/** + * Return payload of 32 bit signed integer attribute. + * @arg nla 32 bit integer attribute. + * + * @return Payload as 32 bit integer. + */ +int32_t nla_get_s32(const struct nlattr *nla) +{ + return *(const int32_t *) nla_data(nla); +} + +/** + * Add 32 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value) +{ + return nla_put(msg, attrtype, sizeof(uint32_t), &value); +} + +/** + * Return payload of 32 bit integer attribute. + * @arg nla 32 bit integer attribute. + * + * @return Payload as 32 bit integer. + */ +uint32_t nla_get_u32(const struct nlattr *nla) +{ + return *(const uint32_t *) nla_data(nla); +} + +/** + * Add 64 bit signed integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_s64(struct nl_msg *msg, int attrtype, int64_t value) +{ + return nla_put(msg, attrtype, sizeof(int64_t), &value); +} + +/** + * Return payload of s64 attribute + * @arg nla s64 netlink attribute + * + * @return Payload as 64 bit integer. + */ +int64_t nla_get_s64(const struct nlattr *nla) +{ + int64_t tmp = 0; + + if (nla && _nla_len(nla) >= sizeof(tmp)) + memcpy(&tmp, nla_data(nla), sizeof(tmp)); + + return tmp; +} + +/** + * Add 64 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_u64(struct nl_msg *msg, int attrtype, uint64_t value) +{ + return nla_put(msg, attrtype, sizeof(uint64_t), &value); +} + +/** + * Return payload of u64 attribute + * @arg nla u64 netlink attribute + * + * @return Payload as 64 bit integer. + */ +uint64_t nla_get_u64(const struct nlattr *nla) +{ + uint64_t tmp = 0; + + if (nla && _nla_len(nla) >= sizeof(tmp)) + memcpy(&tmp, nla_data(nla), sizeof(tmp)); + + return tmp; +} + +/** + * Add variable-length signed integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_sint(struct nl_msg *msg, int attrtype, int64_t value) +{ + int64_t tmp64 = value; + int32_t tmp32 = value; + + if (tmp64 == tmp32) + return nla_put_s32(msg, attrtype, tmp32); + return nla_put(msg, attrtype, sizeof(int64_t), &value); +} + +/** + * Return payload of variable-length signed integer attribute + * @arg nla variable-length signed integer attribute + * + * @return Payload as 64 bit integer. + */ +int64_t nla_get_sint(const struct nlattr *nla) +{ + if (nla && _nla_len(nla) == sizeof(int32_t)) + return nla_get_s32(nla); + return nla_get_s64(nla); +} + +/** + * Add variable-length unsigned integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_uint(struct nl_msg *msg, int attrtype, uint64_t value) +{ + uint64_t tmp64 = value; + uint32_t tmp32 = value; + + if (tmp64 == tmp32) + return nla_put_u32(msg, attrtype, tmp32); + return nla_put(msg, attrtype, sizeof(uint64_t), &value); +} + +/** + * Return payload of variable-length unsigned integer attribute + * @arg nla variable-length unsigned integer attribute + * + * @return Payload as 64 bit integer. + */ +uint64_t nla_get_uint(const struct nlattr *nla) +{ + if (nla && _nla_len(nla) == sizeof(uint32_t)) + return nla_get_u32(nla); + return nla_get_u64(nla); +} + +/** @} */ + +/** + * @name String Attribute + */ + +/** + * Add string attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg str NUL terminated string. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_string(struct nl_msg *msg, int attrtype, const char *str) +{ + return nla_put(msg, attrtype, strlen(str) + 1, str); +} + +/** + * Return payload of string attribute. + * @arg nla String attribute. + * + * @return Pointer to attribute payload. + */ +char *nla_get_string(const struct nlattr *nla) +{ + return (char *) nla_data(nla); +} + +char *nla_strdup(const struct nlattr *nla) +{ + return strdup(nla_get_string(nla)); +} + +/** @} */ + +/** + * @name Flag Attribute + */ + +/** + * Add flag netlink attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_flag(struct nl_msg *msg, int attrtype) +{ + return nla_put(msg, attrtype, 0, NULL); +} + +/** + * Return true if flag attribute is set. + * @arg nla Flag netlink attribute. + * + * @return True if flag is set, otherwise false. + */ +int nla_get_flag(const struct nlattr *nla) +{ + return !!nla; +} + +/** @} */ + +/** + * @name Microseconds Attribute + */ + +/** + * Add a msecs netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg msecs number of msecs + */ +int nla_put_msecs(struct nl_msg *n, int attrtype, unsigned long msecs) +{ + return nla_put_u64(n, attrtype, msecs); +} + +/** + * Return payload of msecs attribute + * @arg nla msecs netlink attribute + * + * @return the number of milliseconds. + */ +unsigned long nla_get_msecs(const struct nlattr *nla) +{ + return nla_get_u64(nla); +} + +/** @} */ + +/** + * @name Nested Attribute + */ + +/** + * Add nested attributes to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg nested Message containing attributes to be nested. + * + * Takes the attributes found in the \a nested message and appends them + * to the message \a msg nested in a container of the type \a attrtype. + * The \a nested message may not have a family specific header. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_nested(struct nl_msg *msg, int attrtype, + const struct nl_msg *nested) +{ + NL_DBG(2, "msg %p: attr <> %d: adding msg %p as nested attribute\n", + msg, attrtype, nested); + + return nla_put(msg, attrtype, nlmsg_datalen(nested->nm_nlh), + nlmsg_data(nested->nm_nlh)); +} + + +/** + * Start a new level of nested attributes. + * @arg msg Netlink message. + * @arg attrtype Attribute type of container. + * + * @return Pointer to container attribute. + */ +struct nlattr *nla_nest_start(struct nl_msg *msg, int attrtype) +{ + struct nlattr *start = (struct nlattr *) nlmsg_tail(msg->nm_nlh); + + if (nla_put(msg, NLA_F_NESTED | attrtype, 0, NULL) < 0) + return NULL; + + NL_DBG(2, "msg %p: attr <%p> %d: starting nesting\n", + msg, start, start->nla_type); + + return start; +} + +static int _nest_end(struct nl_msg *msg, struct nlattr *start, int keep_empty) +{ + size_t pad, len; + + len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) start; + + if ( len > USHRT_MAX + || (!keep_empty && len == NLA_HDRLEN)) { + /* + * Max nlattr size exceeded or empty nested attribute, trim the + * attribute header again + */ + nla_nest_cancel(msg, start); + + /* Return error only if nlattr size was exceeded */ + return (len == NLA_HDRLEN) ? 0 : -NLE_ATTRSIZE; + } + + start->nla_len = len; + + pad = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) - msg->nm_nlh->nlmsg_len; + if (pad > 0) { + /* + * Data inside attribute does not end at a alignment boundry. + * Pad accordingly and accoun for the additional space in + * the message. nlmsg_reserve() may never fail in this situation, + * the allocate message buffer must be a multiple of NLMSG_ALIGNTO. + */ + if (!nlmsg_reserve(msg, pad, 0)) + BUG(); + + NL_DBG(2, "msg %p: attr <%p> %d: added %zu bytes of padding\n", + msg, start, start->nla_type, pad); + } + + NL_DBG(2, "msg %p: attr <%p> %d: closing nesting, len=%u\n", + msg, start, start->nla_type, start->nla_len); + + return 0; +} + +/** + * Finalize nesting of attributes. + * @arg msg Netlink message. + * @arg start Container attribute as returned from nla_nest_start(). + * + * Corrects the container attribute header to include the appeneded attributes. + * + * @return 0 on success or a negative error code. + */ +int nla_nest_end(struct nl_msg *msg, struct nlattr *start) +{ + return _nest_end (msg, start, 0); +} + +/** + * Finalize nesting of attributes without stripping off empty attributes. + * @arg msg Netlink message. + * @arg start Container attribute as returned from nla_nest_start(). + * + * Corrects the container attribute header to include the appeneded attributes. + * Keep empty attribute if NO actual attribute payload exists. + * + * @return 0 on success or a negative error code. + */ +int nla_nest_end_keep_empty(struct nl_msg *msg, struct nlattr *start) +{ + return _nest_end (msg, start, 1); +} + +/** + * Cancel the addition of a nested attribute + * @arg msg Netlink message + * @arg attr Nested netlink attribute + * + * Removes any partially added nested Netlink attribute from the message + * by resetting the message to the size before the call to nla_nest_start() + * and by overwriting any potentially touched message segments with 0. + */ +void nla_nest_cancel(struct nl_msg *msg, const struct nlattr *attr) +{ + ssize_t len; + + if (!attr) { + /* For robustness, allow a NULL attr to do nothing. NULL is also + * what nla_nest_start() when out of buffer space. + * + * Warning, before libnl-3.8, the function did not accept NULL! + * If you care, catch NULL yourself. */ + return; + } + + len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) attr; + if (len < 0) + BUG(); + else if (len > 0) { + msg->nm_nlh->nlmsg_len -= len; + memset(nlmsg_tail(msg->nm_nlh), 0, len); + } +} + +/** + * Create attribute index based on nested attribute + * @arg tb Index array to be filled (maxtype+1 elements). + * @arg maxtype Maximum attribute type expected and accepted. + * @arg nla Nested Attribute. + * @arg policy Attribute validation policy. + * + * Feeds the stream of attributes nested into the specified attribute + * to nla_parse(). + * + * @see nla_parse + * @return 0 on success or a negative error code. + */ +int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla, + const struct nla_policy *policy) +{ + return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy); +} + +/** + * Return true if attribute has NLA_F_NESTED flag set + * @arg attr Netlink attribute + * + * @return True if attribute has NLA_F_NESTED flag set, oterhwise False. + */ +int nla_is_nested(const struct nlattr *attr) +{ + return !!(attr->nla_type & NLA_F_NESTED); +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/cache.c b/libnl/lib/cache.c new file mode 100644 index 0000000..f133d6c --- /dev/null +++ b/libnl/lib/cache.c @@ -0,0 +1,1322 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup cache_mngt + * @defgroup cache Cache + * + * @code + * Cache Management | | Type Specific Cache Operations + * + * | | +----------------+ +------------+ + * | request update | | msg_parser | + * | | +----------------+ +------------+ + * +- - - - -^- - - - - - - -^- -|- - - - + * nl_cache_update: | | | | + * 1) --------- co_request_update ------+ | | + * | | | + * 2) destroy old cache +----------- pp_cb ---------|---+ + * | | | + * 3) ---------- nl_recvmsgs ----------+ +- cb_valid -+ + * +--------------+ | | | | + * | nl_cache_add |<-----+ + - - -v- -|- - - - - - - - - - - + * +--------------+ | | +-------------+ + * | nl_recvmsgs | + * | | +-----|-^-----+ + * +---v-|---+ + * | | | nl_recv | + * +---------+ + * | | Core Netlink + * @endcode + * + * Related sections in the development guide: + * - @core_doc{core_cache, Caching System} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include + +#include "nl-core.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/object-api.h" +#include "nl-priv-dynamic-core/cache-api.h" +#include "nl-aux-core/nl-core.h" + +/** + * @name Access Functions + * @{ + */ + +/** + * Return the number of items in the cache + * @arg cache cache handle + */ +int nl_cache_nitems(struct nl_cache *cache) +{ + return cache->c_nitems; +} + +/** + * Return the number of items matching a filter in the cache + * @arg cache Cache object. + * @arg filter Filter object. + */ +int nl_cache_nitems_filter(struct nl_cache *cache, struct nl_object *filter) +{ + struct nl_object *obj; + int nitems = 0; + + if (cache->c_ops == NULL) + BUG(); + + nl_list_for_each_entry(obj, &cache->c_items, ce_list) { + if (filter && !nl_object_match_filter(obj, filter)) + continue; + + nitems++; + } + + return nitems; +} + +/** + * Returns \b true if the cache is empty. + * @arg cache Cache to check + * @return \a true if the cache is empty, otherwise \b false is returned. + */ +int nl_cache_is_empty(struct nl_cache *cache) +{ + return nl_list_empty(&cache->c_items); +} + +/** + * Return the operations set of the cache + * @arg cache cache handle + */ +struct nl_cache_ops *nl_cache_get_ops(struct nl_cache *cache) +{ + return cache->c_ops; +} + +/** + * Return the first element in the cache + * @arg cache cache handle + */ +struct nl_object *nl_cache_get_first(struct nl_cache *cache) +{ + if (nl_list_empty(&cache->c_items)) + return NULL; + + return nl_list_entry(cache->c_items.next, + struct nl_object, ce_list); +} + +/** + * Return the last element in the cache + * @arg cache cache handle + */ +struct nl_object *nl_cache_get_last(struct nl_cache *cache) +{ + if (nl_list_empty(&cache->c_items)) + return NULL; + + return nl_list_entry(cache->c_items.prev, + struct nl_object, ce_list); +} + +/** + * Return the next element in the cache + * @arg obj current object + */ +struct nl_object *nl_cache_get_next(struct nl_object *obj) +{ + if (nl_list_at_tail(obj, &obj->ce_cache->c_items, ce_list)) + return NULL; + else + return nl_list_entry(obj->ce_list.next, + struct nl_object, ce_list); +} + +/** + * Return the previous element in the cache + * @arg obj current object + */ +struct nl_object *nl_cache_get_prev(struct nl_object *obj) +{ + if (nl_list_at_head(obj, &obj->ce_cache->c_items, ce_list)) + return NULL; + else + return nl_list_entry(obj->ce_list.prev, + struct nl_object, ce_list); +} + +/** @} */ + +/** + * @name Cache Allocation/Deletion + * @{ + */ + +/** + * Allocate new cache + * @arg ops Cache operations + * + * Allocate and initialize a new cache based on the cache operations + * provided. + * + * @return Allocated cache or NULL if allocation failed. + */ +struct nl_cache *nl_cache_alloc(struct nl_cache_ops *ops) +{ + struct nl_cache *cache; + + cache = calloc(1, sizeof(*cache)); + if (!cache) + return NULL; + + nl_init_list_head(&cache->c_items); + cache->c_ops = ops; + cache->c_flags |= ops->co_flags; + cache->c_refcnt = 1; + + /* + * If object type provides a hash keygen + * functions, allocate a hash table for the + * cache objects for faster lookups + */ + if (ops->co_obj_ops->oo_keygen) { + int hashtable_size; + + if (ops->co_hash_size) + hashtable_size = ops->co_hash_size; + else + hashtable_size = NL_MAX_HASH_ENTRIES; + + cache->hashtable = nl_hash_table_alloc(hashtable_size); + } + + NL_DBG(2, "Allocated cache %p <%s>.\n", cache, nl_cache_name(cache)); + + return cache; +} + +/** + * Allocate new cache and fill it + * @arg ops Cache operations + * @arg sock Netlink socket + * @arg result Result pointer + * + * Allocate new cache and fill it. Equivalent to calling: + * @code + * cache = nl_cache_alloc(ops); + * nl_cache_refill(sock, cache); + * @endcode + * + * @see nl_cache_alloc + * + * @return 0 on success or a negative error code. + */ +int nl_cache_alloc_and_fill(struct nl_cache_ops *ops, struct nl_sock *sock, + struct nl_cache **result) +{ + struct nl_cache *cache; + int err; + + if (!(cache = nl_cache_alloc(ops))) + return -NLE_NOMEM; + + if (sock && (err = nl_cache_refill(sock, cache)) < 0) { + nl_cache_free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** + * Allocate new cache based on type name + * @arg kind Name of cache type + * @arg result Result pointer + * + * Lookup cache ops via nl_cache_ops_lookup() and allocate the cache + * by calling nl_cache_alloc(). Stores the allocated cache in the + * result pointer provided. + * + * @see nl_cache_alloc + * + * @return 0 on success or a negative error code. + */ +int nl_cache_alloc_name(const char *kind, struct nl_cache **result) +{ + struct nl_cache_ops *ops; + struct nl_cache *cache; + + ops = nl_cache_ops_lookup_safe(kind); + if (!ops) + return -NLE_NOCACHE; + + cache = nl_cache_alloc(ops); + nl_cache_ops_put(ops); + if (!cache) + return -NLE_NOMEM; + + *result = cache; + return 0; +} + +/** + * Allocate new cache containing a subset of an existing cache + * @arg orig Original cache to base new cache on + * @arg filter Filter defining the subset to be filled into the new cache + * + * Allocates a new cache matching the type of the cache specified by + * \p orig. Iterates over the \p orig cache applying the specified + * \p filter and copies all objects that match to the new cache. + * + * The copied objects are clones but do not contain a reference to each + * other. Later modifications to objects in the original cache will + * not affect objects in the new cache. + * + * @return A newly allocated cache or NULL. + */ +struct nl_cache *nl_cache_subset(struct nl_cache *orig, + struct nl_object *filter) +{ + struct nl_cache *cache; + struct nl_object *obj; + + if (!filter) + BUG(); + + cache = nl_cache_alloc(orig->c_ops); + if (!cache) + return NULL; + + NL_DBG(2, "Filling subset of cache %p <%s> with filter %p into %p\n", + orig, nl_cache_name(orig), filter, cache); + + nl_list_for_each_entry(obj, &orig->c_items, ce_list) { + if (!nl_object_match_filter(obj, filter)) + continue; + + nl_cache_add(cache, obj); + } + + return cache; +} + +/** + * Allocate new cache and copy the contents of an existing cache + * @arg cache Original cache to base new cache on + * + * Allocates a new cache matching the type of the cache specified by + * \p cache. Iterates over the \p cache cache and copies all objects + * to the new cache. + * + * The copied objects are clones but do not contain a reference to each + * other. Later modifications to objects in the original cache will + * not affect objects in the new cache. + * + * @return A newly allocated cache or NULL. + */ +struct nl_cache *nl_cache_clone(struct nl_cache *cache) +{ + struct nl_cache_ops *ops = nl_cache_get_ops(cache); + struct nl_cache *clone; + struct nl_object *obj; + + clone = nl_cache_alloc(ops); + if (!clone) + return NULL; + + NL_DBG(2, "Cloning %p into %p\n", cache, clone); + + nl_list_for_each_entry(obj, &cache->c_items, ce_list) + nl_cache_add(clone, obj); + + return clone; +} + +/** + * Remove all objects of a cache. + * @arg cache Cache to clear + * + * The objects are unliked/removed from the cache by calling + * nl_cache_remove() on each object in the cache. If any of the objects + * to not contain any further references to them, those objects will + * be freed. + * + * Unlike with nl_cache_free(), the cache is not freed just emptied. + */ +void nl_cache_clear(struct nl_cache *cache) +{ + struct nl_object *obj, *tmp; + + NL_DBG(2, "Clearing cache %p <%s>...\n", cache, nl_cache_name(cache)); + + nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) + nl_cache_remove(obj); +} + +static void __nl_cache_free(struct nl_cache *cache) +{ + nl_cache_clear(cache); + + if (cache->hashtable) + nl_hash_table_free(cache->hashtable); + + NL_DBG(2, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache)); + free(cache); +} + +/** + * Increase reference counter of cache + * @arg cache Cache + */ +void nl_cache_get(struct nl_cache *cache) +{ + cache->c_refcnt++; + + NL_DBG(3, "Incremented cache %p <%s> reference count to %d\n", + cache, nl_cache_name(cache), cache->c_refcnt); +} + +/** + * Free a cache. + * @arg cache Cache to free. + * + * Calls nl_cache_clear() to remove all objects associated with the + * cache and frees the cache afterwards. + * + * @see nl_cache_clear() + */ +void nl_cache_free(struct nl_cache *cache) +{ + if (!cache) + return; + + cache->c_refcnt--; + + NL_DBG(3, "Decremented cache %p <%s> reference count, %d remaining\n", + cache, nl_cache_name(cache), cache->c_refcnt); + + if (cache->c_refcnt <= 0) + __nl_cache_free(cache); +} + +void nl_cache_put(struct nl_cache *cache) +{ + nl_cache_free(cache); +} + +/** @} */ + +/** + * @name Cache Modifications + * @{ + */ + +static int __cache_add(struct nl_cache *cache, struct nl_object *obj) +{ + int ret; + + obj->ce_cache = cache; + + if (cache->hashtable) { + ret = nl_hash_table_add(cache->hashtable, obj); + if (ret < 0) { + obj->ce_cache = NULL; + return ret; + } + } + + nl_list_add_tail(&obj->ce_list, &cache->c_items); + cache->c_nitems++; + + NL_DBG(3, "Added object %p to cache %p <%s>, nitems %d\n", + obj, cache, nl_cache_name(cache), cache->c_nitems); + + return 0; +} + +/** + * Add object to cache. + * @arg cache Cache + * @arg obj Object to be added to the cache + * + * Adds the object \p obj to the specified \p cache. In case the object + * is already associated with another cache, the object is cloned before + * adding it to the cache. In this case, the sole reference to the object + * will be the one of the cache. Therefore clearing/freeing the cache + * will result in the object being freed again. + * + * If the object has not been associated with a cache yet, the reference + * counter of the object is incremented to account for the additional + * reference. + * + * The type of the object and cache must match, otherwise an error is + * returned (-NLE_OBJ_MISMATCH). + * + * @see nl_cache_move() + * + * @return 0 or a negative error code. + */ +int nl_cache_add(struct nl_cache *cache, struct nl_object *obj) +{ + struct nl_object *new; + int ret = 0; + + if (cache->c_ops->co_obj_ops != obj->ce_ops) + return -NLE_OBJ_MISMATCH; + + if (!nl_list_empty(&obj->ce_list)) { + NL_DBG(3, "Object %p already in cache, cloning new object\n", obj); + + new = nl_object_clone(obj); + if (!new) + return -NLE_NOMEM; + } else { + nl_object_get(obj); + new = obj; + } + + ret = __cache_add(cache, new); + if (ret < 0) + nl_object_put(new); + + return ret; +} + +/** + * Move object from one cache to another + * @arg cache Cache to move object to. + * @arg obj Object subject to be moved + * + * Removes the the specified object \p obj from its associated cache + * and moves it to another cache. + * + * If the object is not associated with a cache, the function behaves + * just like nl_cache_add(). + * + * The type of the object and cache must match, otherwise an error is + * returned (-NLE_OBJ_MISMATCH). + * + * @see nl_cache_add() + * + * @return 0 on success or a negative error code. + */ +int nl_cache_move(struct nl_cache *cache, struct nl_object *obj) +{ + if (cache->c_ops->co_obj_ops != obj->ce_ops) + return -NLE_OBJ_MISMATCH; + + NL_DBG(3, "Moving object %p from cache %p to cache %p\n", + obj, obj->ce_cache, cache); + + /* Acquire reference, if already in a cache this will be + * reverted during removal */ + nl_object_get(obj); + + if (!nl_list_empty(&obj->ce_list)) + nl_cache_remove(obj); + + return __cache_add(cache, obj); +} + +/** + * Remove object from cache. + * @arg obj Object to remove from cache + * + * Removes the object \c obj from the cache it is associated with. The + * reference counter of the object will be decremented. If the reference + * to the object was the only one remaining, the object will be freed. + * + * If no cache is associated with the object, this function is a NOP. + */ +void nl_cache_remove(struct nl_object *obj) +{ + int ret; + struct nl_cache *cache = obj->ce_cache; + + if (cache == NULL) + return; + + if (cache->hashtable) { + ret = nl_hash_table_del(cache->hashtable, obj); + if (ret < 0) + NL_DBG(2, "Failed to delete %p from cache %p <%s>.\n", + obj, cache, nl_cache_name(cache)); + } + + nl_list_del(&obj->ce_list); + obj->ce_cache = NULL; + nl_object_put(obj); + cache->c_nitems--; + + NL_DBG(2, "Deleted object %p from cache %p <%s>.\n", + obj, cache, nl_cache_name(cache)); +} + +/** @} */ + +/** + * @name Synchronization + * @{ + */ + +/** + * Set synchronization arg1 of cache + * @arg cache Cache + * @arg arg argument + * + * Synchronization arguments are used to specify filters when + * requesting dumps from the kernel. + */ +void nl_cache_set_arg1(struct nl_cache *cache, int arg) +{ + cache->c_iarg1 = arg; +} + +/** + * Set synchronization arg2 of cache + * @arg cache Cache + * @arg arg argument + * + * Synchronization arguments are used to specify filters when + * requesting dumps from the kernel. + */ +void nl_cache_set_arg2(struct nl_cache *cache, int arg) +{ + cache->c_iarg2 = arg; +} + +/** + * Set cache flags + * @arg cache Cache + * @arg flags Flags + */ +void nl_cache_set_flags(struct nl_cache *cache, unsigned int flags) +{ + cache->c_flags |= flags; +} + +/** + * Invoke the request-update operation + * @arg sk Netlink socket. + * @arg cache Cache + * + * This function causes the \e request-update function of the cache + * operations to be invoked. This usually causes a dump request to + * be sent over the netlink socket which triggers the kernel to dump + * all objects of a specific type to be dumped onto the netlink + * socket for pickup. + * + * The behaviour of this function depends on the implemenation of + * the \e request_update function of each individual type of cache. + * + * This function will not have any effects on the cache (unless the + * request_update implementation of the cache operations does so). + * + * Use nl_cache_pickup() to pick-up (read) the objects from the socket + * and fill them into the cache. + * + * @see nl_cache_pickup(), nl_cache_resync() + * + * @return 0 on success or a negative error code. Some implementations + * of co_request_update() return a positive number on success that is + * the number of bytes sent. Treat any non-negative number as success too. + */ +static int nl_cache_request_full_dump(struct nl_sock *sk, + struct nl_cache *cache) +{ + if (sk->s_proto != cache->c_ops->co_protocol) + return -NLE_PROTO_MISMATCH; + + if (cache->c_ops->co_request_update == NULL) + return -NLE_OPNOTSUPP; + + NL_DBG(2, "Requesting update from kernel for cache %p <%s>\n", + cache, nl_cache_name(cache)); + + return cache->c_ops->co_request_update(cache, sk); +} + +/** @cond SKIP */ +struct update_xdata { + struct nl_cache_ops *ops; + struct nl_parser_param *params; +}; + +static int update_msg_parser(struct nl_msg *msg, void *arg) +{ + struct update_xdata *x = arg; + int ret = 0; + + ret = nl_cache_parse(x->ops, &msg->nm_src, msg->nm_nlh, x->params); + if (ret == -NLE_EXIST) + return NL_SKIP; + else + return ret; +} +/** @endcond */ + +/** + * Pick-up a netlink request-update with your own parser + * @arg sk Netlink socket + * @arg cache Cache + * @arg param Parser parameters + */ +static int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache, + struct nl_parser_param *param) +{ + int err; + struct nl_cb *cb; + struct update_xdata x = { + .ops = cache->c_ops, + .params = param, + }; + + NL_DBG(2, "Picking up answer for cache %p <%s>\n", + cache, nl_cache_name(cache)); + + cb = nl_cb_clone(sk->s_cb); + if (cb == NULL) + return -NLE_NOMEM; + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, update_msg_parser, &x); + + err = nl_recvmsgs(sk, cb); + if (err < 0) + NL_DBG(2, "While picking up for %p <%s>, recvmsgs() returned %d: %s\n", + cache, nl_cache_name(cache), err, nl_geterror(err)); + + nl_cb_put(cb); + + return err; +} + +static int pickup_checkdup_cb(struct nl_object *c, struct nl_parser_param *p) +{ + struct nl_cache *cache = (struct nl_cache *)p->pp_arg; + _nl_auto_nl_object struct nl_object *old = NULL; + + old = nl_cache_search(cache, c); + if (old) { + if (nl_object_update(old, c) == 0) + return 0; + + nl_cache_remove(old); + } + + return nl_cache_add(cache, c); +} + +static int pickup_cb(struct nl_object *c, struct nl_parser_param *p) +{ + struct nl_cache *cache = p->pp_arg; + + return nl_cache_add(cache, c); +} + +static int __nl_cache_pickup(struct nl_sock *sk, struct nl_cache *cache, + int checkdup) +{ + struct nl_parser_param p; + + p.pp_cb = checkdup ? pickup_checkdup_cb : pickup_cb; + p.pp_arg = cache; + + if (sk->s_proto != cache->c_ops->co_protocol) + return -NLE_PROTO_MISMATCH; + + return __cache_pickup(sk, cache, &p); +} + +/** + * Pickup a netlink dump response and put it into a cache. + * @arg sk Netlink socket. + * @arg cache Cache to put items into. + * + * Waits for netlink messages to arrive, parses them and puts them into + * the specified cache. If an old object with same key attributes is + * present in the cache, it is replaced with the new object. + * If the old object type supports an update operation, an update is + * attempted before a replace. + * + * @return 0 on success or a negative error code. + */ +int nl_cache_pickup_checkdup(struct nl_sock *sk, struct nl_cache *cache) +{ + return __nl_cache_pickup(sk, cache, 1); +} + +/** + * Pickup a netlink dump response and put it into a cache. + * @arg sk Netlink socket. + * @arg cache Cache to put items into. + * + * Waits for netlink messages to arrive, parses them and puts them into + * the specified cache. + * + * @return 0 on success or a negative error code. + */ +int nl_cache_pickup(struct nl_sock *sk, struct nl_cache *cache) +{ + return __nl_cache_pickup(sk, cache, 0); +} + +static int cache_include(struct nl_cache *cache, struct nl_object *obj, + struct nl_msgtype *type, change_func_t cb, + change_func_v2_t cb_v2, void *data) +{ + _nl_auto_nl_object struct nl_object *old = NULL; + _nl_auto_nl_object struct nl_object *clone = NULL; + uint64_t diff = 0; + + switch (type->mt_act) { + case NL_ACT_NEW: + case NL_ACT_DEL: + old = nl_cache_search(cache, obj); + if (old) { + if (cb_v2 && old->ce_ops->oo_update) { + clone = nl_object_clone(old); + diff = nl_object_diff64(old, obj); + } + /* + * Some objects types might support merging the new + * object with the old existing cache object. + * Handle them first. + */ + if (nl_object_update(old, obj) == 0) { + if (cb_v2) { + cb_v2(cache, clone, old, diff, + NL_ACT_CHANGE, data); + nl_object_put(clone); + } else if (cb) + cb(cache, old, NL_ACT_CHANGE, data); + return 0; + } + nl_cache_remove(old); + if (type->mt_act == NL_ACT_DEL) { + if (cb_v2) + cb_v2(cache, old, NULL, 0, NL_ACT_DEL, + data); + else if (cb) + cb(cache, old, NL_ACT_DEL, data); + } + } + + if (type->mt_act == NL_ACT_NEW) { + nl_cache_move(cache, obj); + if (old == NULL) { + if (cb_v2) { + cb_v2(cache, NULL, obj, 0, NL_ACT_NEW, + data); + } else if (cb) + cb(cache, obj, NL_ACT_NEW, data); + } else if (old) { + diff = 0; + if (cb || cb_v2) + diff = nl_object_diff64(old, obj); + if (diff && cb_v2) { + cb_v2(cache, old, obj, diff, NL_ACT_CHANGE, + data); + } else if (diff && cb) + cb(cache, obj, NL_ACT_CHANGE, data); + + } + } + break; + default: + NL_DBG(2, "Unknown action associated to object %p\n", obj); + return 0; + } + + return 0; +} + +int nl_cache_include(struct nl_cache *cache, struct nl_object *obj, + change_func_t change_cb, void *data) +{ + struct nl_cache_ops *ops = cache->c_ops; + int i; + + if (ops->co_obj_ops != obj->ce_ops) + return -NLE_OBJ_MISMATCH; + + for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) + if (ops->co_msgtypes[i].mt_id == obj->ce_msgtype) + return cache_include(cache, obj, &ops->co_msgtypes[i], + change_cb, NULL, data); + + NL_DBG(3, "Object %p does not seem to belong to cache %p <%s>\n", + obj, cache, nl_cache_name(cache)); + + return -NLE_MSGTYPE_NOSUPPORT; +} + +int nl_cache_include_v2(struct nl_cache *cache, struct nl_object *obj, + change_func_v2_t change_cb, void *data) +{ + struct nl_cache_ops *ops = cache->c_ops; + int i; + + if (ops->co_obj_ops != obj->ce_ops) + return -NLE_OBJ_MISMATCH; + + for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) + if (ops->co_msgtypes[i].mt_id == obj->ce_msgtype) + return cache_include(cache, obj, &ops->co_msgtypes[i], + NULL, change_cb, data); + + NL_DBG(3, "Object %p does not seem to belong to cache %p <%s>\n", + obj, cache, nl_cache_name(cache)); + + return -NLE_MSGTYPE_NOSUPPORT; +} + +static int resync_cb(struct nl_object *c, struct nl_parser_param *p) +{ + struct nl_cache_assoc *ca = p->pp_arg; + + if (ca->ca_change_v2) + return nl_cache_include_v2(ca->ca_cache, c, ca->ca_change_v2, + ca->ca_change_data); + else + return nl_cache_include(ca->ca_cache, c, ca->ca_change, + ca->ca_change_data); +} + +int nl_cache_resync(struct nl_sock *sk, struct nl_cache *cache, + change_func_t change_cb, void *data) +{ + struct nl_object *obj, *next; + struct nl_af_group *grp; + struct nl_cache_assoc ca = { + .ca_cache = cache, + .ca_change = change_cb, + .ca_change_data = data, + }; + struct nl_parser_param p = { + .pp_cb = resync_cb, + .pp_arg = &ca, + }; + int err; + + if (sk->s_proto != cache->c_ops->co_protocol) + return -NLE_PROTO_MISMATCH; + + NL_DBG(1, "Resyncing cache %p <%s>...\n", cache, nl_cache_name(cache)); + + /* Mark all objects so we can see if some of them are obsolete */ + nl_cache_mark_all(cache); + + grp = cache->c_ops->co_groups; + do { + if (grp && grp->ag_group && + (cache->c_flags & NL_CACHE_AF_ITER)) + nl_cache_set_arg1(cache, grp->ag_family); + +restart: + err = nl_cache_request_full_dump(sk, cache); + if (err < 0) + goto errout; + + err = __cache_pickup(sk, cache, &p); + if (err == -NLE_DUMP_INTR) + goto restart; + else if (err < 0) + goto errout; + + if (grp) + grp++; + } while (grp && grp->ag_group && + (cache->c_flags & NL_CACHE_AF_ITER)); + + nl_list_for_each_entry_safe(obj, next, &cache->c_items, ce_list) { + if (nl_object_is_marked(obj)) { + nl_object_get(obj); + nl_cache_remove(obj); + if (change_cb) + change_cb(cache, obj, NL_ACT_DEL, data); + nl_object_put(obj); + } + } + + NL_DBG(1, "Finished resyncing %p <%s>\n", cache, nl_cache_name(cache)); + + err = 0; +errout: + return err; +} + +/** @} */ + +/** + * @name Parsing + * @{ + */ + +/** @cond SKIP */ +int nl_cache_parse(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *params) +{ + int i, err; + + if (!nlmsg_valid_hdr(nlh, ops->co_hdrsize)) + return -NLE_MSG_TOOSHORT; + + for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) { + if (ops->co_msgtypes[i].mt_id == nlh->nlmsg_type) { + err = ops->co_msg_parser(ops, who, nlh, params); + if (err != -NLE_OPNOTSUPP) + goto errout; + } + } + + + err = -NLE_MSGTYPE_NOSUPPORT; +errout: + return err; +} +/** @endcond */ + +/** + * Parse a netlink message and add it to the cache. + * @arg cache cache to add element to + * @arg msg netlink message + * + * Parses a netlink message by calling the cache specific message parser + * and adds the new element to the cache. If an old object with same key + * attributes is present in the cache, it is replaced with the new object. + * If the old object type supports an update operation, an update is + * attempted before a replace. + * + * @return 0 or a negative error code. + */ +int nl_cache_parse_and_add(struct nl_cache *cache, struct nl_msg *msg) +{ + struct nl_parser_param p = { + .pp_cb = pickup_cb, + .pp_arg = cache, + }; + + return nl_cache_parse(cache->c_ops, NULL, nlmsg_hdr(msg), &p); +} + +/** + * (Re)fill a cache with the contents in the kernel. + * @arg sk Netlink socket. + * @arg cache cache to update + * + * Clears the specified cache and fills it with the current state in + * the kernel. + * + * @return 0 or a negative error code. + */ +int nl_cache_refill(struct nl_sock *sk, struct nl_cache *cache) +{ + struct nl_af_group *grp; + int err; + + if (sk->s_proto != cache->c_ops->co_protocol) + return -NLE_PROTO_MISMATCH; + + nl_cache_clear(cache); + grp = cache->c_ops->co_groups; + do { + if (grp && grp->ag_group && + (cache->c_flags & NL_CACHE_AF_ITER)) + nl_cache_set_arg1(cache, grp->ag_family); + +restart: + err = nl_cache_request_full_dump(sk, cache); + if (err < 0) + return err; + + NL_DBG(2, "Updating cache %p <%s> for family %u, request sent, waiting for reply\n", + cache, nl_cache_name(cache), grp ? grp->ag_family : AF_UNSPEC); + + err = nl_cache_pickup(sk, cache); + if (err == -NLE_DUMP_INTR) { + NL_DBG(2, "Dump interrupted, restarting!\n"); + goto restart; + } else if (err < 0) + break; + + if (grp) + grp++; + } while (grp && grp->ag_group && + (cache->c_flags & NL_CACHE_AF_ITER)); + + return err; +} + +/** @} */ + +/** + * @name Utillities + * @{ + */ +static struct nl_object *__cache_fast_lookup(struct nl_cache *cache, + struct nl_object *needle) +{ + struct nl_object *obj; + + obj = nl_hash_table_lookup(cache->hashtable, needle); + if (obj) { + nl_object_get(obj); + return obj; + } + + return NULL; +} + +/** + * Search object in cache + * @arg cache Cache + * @arg needle Object to look for. + * + * Searches the cache for an object which matches the object \p needle. + * The function nl_object_identical() is used to determine if the + * objects match. If a matching object is found, the reference counter + * is incremented and the object is returned. + * + * Therefore, if an object is returned, the reference to the object + * must be returned by calling nl_object_put() after usage. + * + * @return Reference to object or NULL if not found. + */ +struct nl_object *nl_cache_search(struct nl_cache *cache, + struct nl_object *needle) +{ + struct nl_object *obj; + + if (cache->hashtable) + return __cache_fast_lookup(cache, needle); + + nl_list_for_each_entry(obj, &cache->c_items, ce_list) { + if (nl_object_identical(obj, needle)) { + nl_object_get(obj); + return obj; + } + } + + return NULL; +} + +/** + * Find object in cache + * @arg cache Cache + * @arg filter object acting as a filter + * + * Searches the cache for an object which matches the object filter. + * If the filter attributes matches the object type id attributes, + * and the cache supports hash lookups, a faster hashtable lookup + * is used to return the object. Else, function nl_object_match_filter() is + * used to determine if the objects match. If a matching object is + * found, the reference counter is incremented and the object is returned. + * + * Therefore, if an object is returned, the reference to the object + * must be returned by calling nl_object_put() after usage. + * + * @return Reference to object or NULL if not found. + */ +struct nl_object *nl_cache_find(struct nl_cache *cache, + struct nl_object *filter) +{ + struct nl_object *obj; + + if (cache->c_ops == NULL) + BUG(); + + if ((nl_object_get_id_attrs(filter) == filter->ce_mask) + && cache->hashtable) + return __cache_fast_lookup(cache, filter); + + nl_list_for_each_entry(obj, &cache->c_items, ce_list) { + if (nl_object_match_filter(obj, filter)) { + nl_object_get(obj); + return obj; + } + } + + return NULL; +} + +/** + * Mark all objects of a cache + * @arg cache Cache + * + * Marks all objects of a cache by calling nl_object_mark() on each + * object associated with the cache. + */ +void nl_cache_mark_all(struct nl_cache *cache) +{ + struct nl_object *obj; + + NL_DBG(2, "Marking all objects in cache %p <%s>\n", + cache, nl_cache_name(cache)); + + nl_list_for_each_entry(obj, &cache->c_items, ce_list) + nl_object_mark(obj); +} + +/** @} */ + +/** + * @name Dumping + * @{ + */ + +/** + * Dump all elements of a cache. + * @arg cache cache to dump + * @arg params dumping parameters + * + * Dumps all elements of the \a cache to the file descriptor \a fd. + */ +void nl_cache_dump(struct nl_cache *cache, struct nl_dump_params *params) +{ + nl_cache_dump_filter(cache, params, NULL); +} + +/** + * Dump all elements of a cache (filtered). + * @arg cache cache to dump + * @arg params dumping parameters + * @arg filter filter object + * + * Dumps all elements of the \a cache to the file descriptor \a fd + * given they match the given filter \a filter. + */ +void nl_cache_dump_filter(struct nl_cache *cache, + struct nl_dump_params *params, + struct nl_object *filter) +{ + struct nl_dump_params params_copy; + struct nl_object_ops *ops; + struct nl_object *obj; + int type; + + NL_DBG(2, "Dumping cache %p <%s> with filter %p\n", + cache, nl_cache_name(cache), filter); + + if (!params) { + /* It doesn't really make sense that @params is an optional parameter. In the + * past, nl_cache_dump() was documented that the @params would be optional, so + * try to save it. + * + * Note that this still isn't useful, because we don't set any dump option. + * It only exists not to crash applications that wrongly pass %NULL here. */ + _nl_assert_not_reached (); + params_copy = (struct nl_dump_params) { + .dp_type = NL_DUMP_DETAILS, + }; + params = ¶ms_copy; + } + + type = params->dp_type; + + if (type > NL_DUMP_MAX || type < 0) + BUG(); + + if (cache->c_ops == NULL) + BUG(); + + ops = cache->c_ops->co_obj_ops; + if (!ops->oo_dump[type]) + return; + + if (params->dp_buf) + memset(params->dp_buf, 0, params->dp_buflen); + + nl_list_for_each_entry(obj, &cache->c_items, ce_list) { + if (filter && !nl_object_match_filter(obj, filter)) + continue; + + NL_DBG(4, "Dumping object %p...\n", obj); + dump_from_ops(obj, params); + } +} + +/** @} */ + +/** + * @name Iterators + * @{ + */ + +/** + * Call a callback on each element of the cache. + * @arg cache cache to iterate on + * @arg cb callback function + * @arg arg argument passed to callback function + * + * Calls a callback function \a cb on each element of the \a cache. + * The argument \a arg is passed on the callback function. + */ +void nl_cache_foreach(struct nl_cache *cache, + void (*cb)(struct nl_object *, void *), void *arg) +{ + nl_cache_foreach_filter(cache, NULL, cb, arg); +} + +/** + * Call a callback on each element of the cache (filtered). + * @arg cache cache to iterate on + * @arg filter filter object + * @arg cb callback function + * @arg arg argument passed to callback function + * + * Calls a callback function \a cb on each element of the \a cache + * that matches the \a filter. The argument \a arg is passed on + * to the callback function. + */ +void nl_cache_foreach_filter(struct nl_cache *cache, struct nl_object *filter, + void (*cb)(struct nl_object *, void *), void *arg) +{ + struct nl_object *obj, *tmp; + + if (cache->c_ops == NULL) + BUG(); + + nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) { + if (filter) { + int diff = nl_object_match_filter(obj, filter); + + NL_DBG(3, "%p<->%p object difference: %x\n", + obj, filter, diff); + + if (!diff) + continue; + } + + /* Caller may hold obj for a long time */ + nl_object_get(obj); + + cb(obj, arg); + + nl_object_put(obj); + } +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/cache_mngr.c b/libnl/lib/cache_mngr.c new file mode 100644 index 0000000..8d8e262 --- /dev/null +++ b/libnl/lib/cache_mngr.c @@ -0,0 +1,663 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup cache_mngt + * @defgroup cache_mngr Manager + * @brief Manager keeping caches up to date automatically. + * + * The cache manager keeps caches up to date automatically by listening to + * netlink notifications and integrating the received information into the + * existing cache. + * + * @note This functionality is still considered experimental. + * + * Related sections in the development guide: + * - @core_doc{_cache_manager,Cache Manager} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#include "nl-default.h" + +#include +#include +#include + +#include "nl-core.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" +#include "nl-aux-core/nl-core.h" + +#define NL_ALLOCATED_SYNC_SOCK 4 + +/** @cond SKIP */ +struct nl_cache_mngr +{ + int cm_protocol; + int cm_flags; + int cm_nassocs; + struct nl_sock * cm_sock; + struct nl_sock * cm_sync_sock; + struct nl_cache_assoc * cm_assocs; +}; + +#define NASSOC_INIT 16 +#define NASSOC_EXPAND 8 +/** @endcond */ + +static int include_cb(struct nl_object *obj, struct nl_parser_param *p) +{ + struct nl_cache_assoc *ca = p->pp_arg; + struct nl_cache_ops *ops = ca->ca_cache->c_ops; + + NL_DBG(2, "Including object %p into cache %p\n", obj, ca->ca_cache); + + if (NL_DEBUG && nl_debug >= 4) + nl_object_dump(obj, &nl_debug_dp); + + if (ops->co_event_filter) + if (ops->co_event_filter(ca->ca_cache, obj) != NL_OK) + return 0; + + if (ops->co_include_event) + return ops->co_include_event(ca->ca_cache, obj, ca->ca_change, + ca->ca_change_v2, + ca->ca_change_data); + else { + if (ca->ca_change_v2) + return nl_cache_include_v2(ca->ca_cache, obj, ca->ca_change_v2, ca->ca_change_data); + else + return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data); + } + +} + +static int event_input(struct nl_msg *msg, void *arg) +{ + struct nl_cache_mngr *mngr = arg; + int protocol = nlmsg_get_proto(msg); + int type = nlmsg_hdr(msg)->nlmsg_type; + struct nl_cache_ops *ops; + int i, n; + struct nl_parser_param p = { + .pp_cb = include_cb, + }; + + NL_DBG(2, "Cache manager %p, handling new message %p as event\n", + mngr, msg); + + if (NL_DEBUG && nl_debug >= 4) + nl_msg_dump(msg, stderr); + + if (mngr->cm_protocol != protocol) + BUG(); + + for (i = 0; i < mngr->cm_nassocs; i++) { + if (mngr->cm_assocs[i].ca_cache) { + ops = mngr->cm_assocs[i].ca_cache->c_ops; + for (n = 0; ops->co_msgtypes[n].mt_id >= 0; n++) + if (ops->co_msgtypes[n].mt_id == type) + goto found; + } + } + + return NL_SKIP; + +found: + NL_DBG(2, "Associated message %p to cache %p\n", + msg, mngr->cm_assocs[i].ca_cache); + p.pp_arg = &mngr->cm_assocs[i]; + + return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p); +} + +/** + * Allocate new cache manager + * @arg sk Netlink socket or NULL to auto allocate + * @arg protocol Netlink protocol this manager is used for + * @arg flags Flags (\c NL_AUTO_PROVIDE) + * @arg result Result pointer + * + * Allocates a new cache manager for the specified netlink protocol. + * + * 1. If sk is not specified (\c NULL) a netlink socket matching the + * specified protocol will be automatically allocated. + * + * 2. The socket will be put in non-blocking mode and sequence checking + * will be disabled regardless of whether the socket was provided by + * the caller or automatically allocated. + * + * 3. The socket will be connected. + * + * If the flag \c NL_AUTO_PROVIDE is specified, any cache added to the + * manager will automatically be made available to other users using + * nl_cache_mngt_provide(). + * + * @note If the socket is provided by the caller, it is NOT recommended + * to use the socket for anything else besides receiving netlink + * notifications. + * + * @return 0 on success or a negative error code. + */ +int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags, + struct nl_cache_mngr **result) +{ + return nl_cache_mngr_alloc_ex(sk, NULL, protocol, flags, result); +} + +/** + * Allocate new cache manager, with custom callback on refill socket + * @arg sk Netlink socket or NULL to auto allocate + * @arg sync_sk Blocking Netlink socket for cache refills + * @arg protocol Netlink protocol this manager is used for + * @arg flags Flags (\c NL_AUTO_PROVIDE) + * @arg result Result pointer + * + * Same as \f nl_cache_mngr_alloc, but sets custom refill socket + * Note: ownership of the sync_sk passes to the cache manager + */ +int nl_cache_mngr_alloc_ex(struct nl_sock *sk, struct nl_sock *sync_sk, int protocol, int flags, + struct nl_cache_mngr **result) +{ + _nl_auto_nl_cache_mngr struct nl_cache_mngr *mngr = NULL; + int err; + + /* Catch abuse of flags */ + if (flags & NL_ALLOCATED_SOCK) + BUG(); + flags = flags & NL_AUTO_PROVIDE; + + mngr = calloc(1, sizeof(*mngr)); + if (!mngr) + return -NLE_NOMEM; + + mngr->cm_flags = flags; + + if (!sk) { + if (!(sk = nl_socket_alloc())) + return -NLE_NOMEM; + mngr->cm_flags |= NL_ALLOCATED_SOCK; + } + mngr->cm_sock = sk; + + if(!sync_sk) { + if (!(sync_sk = nl_socket_alloc())) + return -NLE_NOMEM; + mngr->cm_flags |= NL_ALLOCATED_SYNC_SOCK; + } + mngr->cm_sync_sock = sync_sk; + + mngr->cm_nassocs = NASSOC_INIT; + mngr->cm_protocol = protocol; + mngr->cm_assocs = calloc(mngr->cm_nassocs, + sizeof(struct nl_cache_assoc)); + if (!mngr->cm_assocs) + return -NLE_NOMEM; + + /* Required to receive async event notifications */ + nl_socket_disable_seq_check(mngr->cm_sock); + + if ((err = nl_connect(mngr->cm_sock, protocol)) < 0) + return err; + + if ((err = nl_socket_set_nonblocking(mngr->cm_sock)) < 0) + return err; + + if ((err = nl_connect(mngr->cm_sync_sock, protocol)) < 0) + return err; + + NL_DBG(1, "Allocated cache manager %p, protocol %d, %d caches\n", + mngr, protocol, mngr->cm_nassocs); + + *result = _nl_steal_pointer(&mngr); + return 0; +} + +/** + * Set change_func_v2 for cache manager + * @arg mngr Cache manager. + * @arg cache Cache associated with the callback + * @arg cb Function to be called upon changes. + * @arg data Argument passed on to change callback + * + * Adds callback change_func_v2 to a registered cache. This callback provides + * in like the standard change_func the added or remove netlink object. In case + * of a change the old and the new object is provided as well as the according + * diff. If this callback is registered this has a higher priority then the + * change_func registered during cache registration. Hence only one callback is + * executed. + * + * The first netlink object in the callback is refering to the old object and + * the second to the new. This means on NL_ACT_CHANGE the first is the previous + * object in the cache and the second the updated version. On NL_ACT_DEL the + * first is the deleted object the second is NULL. On NL_ACT_NEW the first is + * NULL and the second the new netlink object. + * + * The user is responsible for calling nl_cache_mngr_poll() or monitor + * the socket and call nl_cache_mngr_data_ready() to allow the library + * to process netlink notification events. + * + * @see nl_cache_mngr_poll() + * @see nl_cache_mngr_data_ready() + * + * @return 0 on success or a negative error code. + * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and + * cache type + * @return -NLE_OPNOTSUPP Cache type does not support updates + * @return -NLE_RANGE Cache of this type is not registered + */ +static int nl_cache_mngr_set_change_func_v2(struct nl_cache_mngr *mngr, + struct nl_cache *cache, + change_func_v2_t cb, void *data) +{ + struct nl_cache_ops *ops; + int i; + + ops = cache->c_ops; + if (!ops) + return -NLE_INVAL; + + if (ops->co_protocol != mngr->cm_protocol) + return -NLE_PROTO_MISMATCH; + + if (ops->co_groups == NULL) + return -NLE_OPNOTSUPP; + + for (i = 0; i < mngr->cm_nassocs; i++) + if (mngr->cm_assocs[i].ca_cache == cache) + break; + + if (i >= mngr->cm_nassocs) { + return -NLE_RANGE; + } + + mngr->cm_assocs[i].ca_change_v2 = cb; + mngr->cm_assocs[i].ca_change_data = data; + + return 0; +} + +/** + * Add cache to cache manager + * @arg mngr Cache manager. + * @arg cache Cache to be added to cache manager + * @arg cb Function to be called upon changes. + * @arg data Argument passed on to change callback + * + * Adds cache to the manager. The operation will trigger a full + * dump request from the kernel to initially fill the contents + * of the cache. The manager will subscribe to the notification group + * of the cache and keep track of any further changes. + * + * The user is responsible for calling nl_cache_mngr_poll() or monitor + * the socket and call nl_cache_mngr_data_ready() to allow the library + * to process netlink notification events. + * + * @see nl_cache_mngr_poll() + * @see nl_cache_mngr_data_ready() + * + * @return 0 on success or a negative error code. + * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and + * cache type + * @return -NLE_OPNOTSUPP Cache type does not support updates + * @return -NLE_EXIST Cache of this type already being managed + */ +int nl_cache_mngr_add_cache(struct nl_cache_mngr *mngr, struct nl_cache *cache, + change_func_t cb, void *data) +{ + struct nl_cache_ops *ops; + struct nl_af_group *grp; + int err, i; + + ops = cache->c_ops; + if (!ops) + return -NLE_INVAL; + + if (ops->co_protocol != mngr->cm_protocol) + return -NLE_PROTO_MISMATCH; + + if (ops->co_groups == NULL) + return -NLE_OPNOTSUPP; + + for (i = 0; i < mngr->cm_nassocs; i++) + if (mngr->cm_assocs[i].ca_cache && + mngr->cm_assocs[i].ca_cache->c_ops == ops) + return -NLE_EXIST; + + for (i = 0; i < mngr->cm_nassocs; i++) + if (!mngr->cm_assocs[i].ca_cache) + break; + + if (i >= mngr->cm_nassocs) { + struct nl_cache_assoc *cm_assocs; + int cm_nassocs = mngr->cm_nassocs + NASSOC_EXPAND; + + cm_assocs = realloc(mngr->cm_assocs, + cm_nassocs * sizeof(struct nl_cache_assoc)); + if (cm_assocs == NULL) + return -NLE_NOMEM; + + memset(cm_assocs + mngr->cm_nassocs, 0, + NASSOC_EXPAND * sizeof(struct nl_cache_assoc)); + mngr->cm_assocs = cm_assocs; + mngr->cm_nassocs = cm_nassocs; + + NL_DBG(1, "Increased capacity of cache manager %p " \ + "to %d\n", mngr, mngr->cm_nassocs); + } + + for (grp = ops->co_groups; grp->ag_group; grp++) { + err = nl_socket_add_membership(mngr->cm_sock, grp->ag_group); + if (err < 0) + return err; + } + + err = nl_cache_refill(mngr->cm_sync_sock, cache); + if (err < 0) + goto errout_drop_membership; + + mngr->cm_assocs[i].ca_cache = cache; + mngr->cm_assocs[i].ca_change = cb; + mngr->cm_assocs[i].ca_change_data = data; + + if (mngr->cm_flags & NL_AUTO_PROVIDE) + nl_cache_mngt_provide(cache); + + NL_DBG(1, "Added cache %p <%s> to cache manager %p\n", + cache, nl_cache_name(cache), mngr); + + return 0; + +errout_drop_membership: + for (grp = ops->co_groups; grp->ag_group; grp++) + nl_socket_drop_membership(mngr->cm_sock, grp->ag_group); + + return err; +} + +/** + * Add cache to cache manager + * @arg mngr Cache manager. + * @arg cache Cache to be added to cache manager + * @arg cb V2 function to be called upon changes. + * @arg data Argument passed on to change callback + * + * Adds cache to the manager. The operation will trigger a full + * dump request from the kernel to initially fill the contents + * of the cache. The manager will subscribe to the notification group + * of the cache and keep track of any further changes. + * + * The user is responsible for calling nl_cache_mngr_poll() or monitor + * the socket and call nl_cache_mngr_data_ready() to allow the library + * to process netlink notification events. + * + * @see nl_cache_mngr_poll() + * @see nl_cache_mngr_data_ready() + * + * @return 0 on success or a negative error code. + * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and + * cache type + * @return -NLE_OPNOTSUPP Cache type does not support updates + * @return -NLE_EXIST Cache of this type already being managed + */ +int nl_cache_mngr_add_cache_v2(struct nl_cache_mngr *mngr, struct nl_cache *cache, + change_func_v2_t cb, void *data) { + int err; + err = nl_cache_mngr_add_cache(mngr, cache, NULL, NULL); + if (err < 0) + return err; + + return nl_cache_mngr_set_change_func_v2(mngr, cache, cb, data); +} + +/** + * Add cache to cache manager + * @arg mngr Cache manager. + * @arg name Name of cache to keep track of + * @arg cb Function to be called upon changes. + * @arg data Argument passed on to change callback + * @arg result Pointer to store added cache (optional) + * + * Allocates a new cache of the specified type and adds it to the manager. + * The operation will trigger a full dump request from the kernel to + * initially fill the contents of the cache. The manager will subscribe + * to the notification group of the cache and keep track of any further + * changes. + * + * The user is responsible for calling nl_cache_mngr_poll() or monitor + * the socket and call nl_cache_mngr_data_ready() to allow the library + * to process netlink notification events. + * + * @note Versions up to 3.4.0 actually required the result argument, preventing + * NULL to be passed. + * + * @see nl_cache_mngr_poll() + * @see nl_cache_mngr_data_ready() + * + * @return 0 on success or a negative error code. + * @return -NLE_NOCACHE Unknown cache type + * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and + * cache type + * @return -NLE_OPNOTSUPP Cache type does not support updates + * @return -NLE_EXIST Cache of this type already being managed + */ +int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name, + change_func_t cb, void *data, struct nl_cache **result) +{ + struct nl_cache_ops *ops; + struct nl_cache *cache; + int err; + + ops = nl_cache_ops_lookup_safe(name); + if (!ops) + return -NLE_NOCACHE; + + cache = nl_cache_alloc(ops); + nl_cache_ops_put(ops); + if (!cache) + return -NLE_NOMEM; + + err = nl_cache_mngr_add_cache(mngr, cache, cb, data); + if (err < 0) + goto errout_free_cache; + + if (result) + *result = cache; + return 0; + +errout_free_cache: + nl_cache_free(cache); + + return err; +} + +/** + * Get socket file descriptor + * @arg mngr Cache Manager + * + * Get the file descriptor of the socket associated with the manager. + * + * @note Do not use the socket for anything besides receiving + * notifications. + */ +int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr) +{ + return nl_socket_get_fd(mngr->cm_sock); +} + +/** + * Check for event notifications + * @arg mngr Cache Manager + * @arg timeout Upper limit poll() will block, in milliseconds. + * + * Causes poll() to be called to check for new event notifications + * being available. Calls nl_cache_mngr_data_ready() to process + * available data. + * + * This functionally is ideally called regularly during an idle + * period. + * + * A timeout can be specified in milliseconds to limit the time the + * function will wait for updates. + * + * @see nl_cache_mngr_data_ready() + * + * @return The number of messages processed or a negative error code. + */ +int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout) +{ + int ret; + struct pollfd fds = { + .fd = nl_socket_get_fd(mngr->cm_sock), + .events = POLLIN, + }; + + NL_DBG(3, "Cache manager %p, poll() fd %d\n", mngr, fds.fd); + ret = poll(&fds, 1, timeout); + NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret); + if (ret < 0) { + NL_DBG(4, "nl_cache_mngr_poll(%p): poll() failed with %d (%s)\n", + mngr, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + /* No events, return */ + if (ret == 0) + return 0; + + return nl_cache_mngr_data_ready(mngr); +} + +/** + * Receive available event notifications + * @arg mngr Cache manager + * + * This function can be called if the socket associated to the manager + * contains updates to be received. This function should only be used + * if nl_cache_mngr_poll() is not used. + * + * The function will process messages until there is no more data to + * be read from the socket. + * + * @see nl_cache_mngr_poll() + * + * @return The number of messages processed or a negative error code. + */ +int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr) +{ + int err, nread = 0; + struct nl_cb *cb; + + NL_DBG(2, "Cache manager %p, reading new data from fd %d\n", + mngr, nl_socket_get_fd(mngr->cm_sock)); + + cb = nl_cb_clone(mngr->cm_sock->s_cb); + if (cb == NULL) + return -NLE_NOMEM; + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, event_input, mngr); + + while ((err = nl_recvmsgs_report(mngr->cm_sock, cb)) > 0) { + NL_DBG(2, "Cache manager %p, recvmsgs read %d messages\n", + mngr, err); + nread += err; + } + + nl_cb_put(cb); + if (err < 0 && err != -NLE_AGAIN) + return err; + + return nread; +} + +/** + * Print information about cache manager + * @arg mngr Cache manager + * @arg p Dumping parameters + * + * Prints information about the cache manager including all managed caches. + * + * @note This is a debugging function. + */ +void nl_cache_mngr_info(struct nl_cache_mngr *mngr, struct nl_dump_params *p) +{ + char buf[128]; + int i; + + nl_dump_line(p, "cache-manager <%p>\n", mngr); + nl_dump_line(p, " .protocol = %s\n", + nl_nlfamily2str(mngr->cm_protocol, buf, sizeof(buf))); + nl_dump_line(p, " .flags = %#x\n", mngr->cm_flags); + nl_dump_line(p, " .nassocs = %u\n", mngr->cm_nassocs); + nl_dump_line(p, " .sock = <%p>\n", mngr->cm_sock); + + for (i = 0; i < mngr->cm_nassocs; i++) { + struct nl_cache_assoc *assoc = &mngr->cm_assocs[i]; + + if (assoc->ca_cache) { + nl_dump_line(p, " .cache[%d] = <%p> {\n", i, assoc->ca_cache); + nl_dump_line(p, " .name = %s\n", assoc->ca_cache->c_ops->co_name); + nl_dump_line(p, " .change_func = <%p>\n", assoc->ca_change); + nl_dump_line(p, " .change_data = <%p>\n", assoc->ca_change_data); + nl_dump_line(p, " .nitems = %u\n", nl_cache_nitems(assoc->ca_cache)); + nl_dump_line(p, " .objects = {\n"); + + p->dp_prefix += 6; + nl_cache_dump(assoc->ca_cache, p); + p->dp_prefix -= 6; + + nl_dump_line(p, " }\n"); + nl_dump_line(p, " }\n"); + } + } +} + +/** + * Free cache manager and all caches. + * @arg mngr Cache manager. + * + * Release all resources held by a cache manager. + */ +void nl_cache_mngr_free(struct nl_cache_mngr *mngr) +{ + int i; + + if (!mngr) + return; + + if (mngr->cm_sock) + nl_close(mngr->cm_sock); + + if (mngr->cm_sync_sock) + nl_close(mngr->cm_sync_sock); + + if (mngr->cm_flags & NL_ALLOCATED_SOCK) + nl_socket_free(mngr->cm_sock); + + if (mngr->cm_flags & NL_ALLOCATED_SYNC_SOCK) + nl_socket_free(mngr->cm_sync_sock); + + for (i = 0; i < mngr->cm_nassocs; i++) { + if (mngr->cm_assocs[i].ca_cache) { + nl_cache_mngt_unprovide(mngr->cm_assocs[i].ca_cache); + nl_cache_free(mngr->cm_assocs[i].ca_cache); + } + } + + free(mngr->cm_assocs); + + NL_DBG(1, "Cache manager %p freed\n", mngr); + + free(mngr); +} + +/** @} */ diff --git a/libnl/lib/cache_mngt.c b/libnl/lib/cache_mngt.c new file mode 100644 index 0000000..7969d35 --- /dev/null +++ b/libnl/lib/cache_mngt.c @@ -0,0 +1,442 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup core + * @defgroup cache_mngt Caching System + * + * Related sections in the development guide: + * - @core_doc{core_cache, Caching System} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#include "nl-default.h" + +#include +#include +#include + +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/object-api.h" +#include "nl-priv-dynamic-core/cache-api.h" +#include "nl-aux-core/nl-core.h" + +static struct nl_cache_ops *cache_ops; +static NL_RW_LOCK(cache_ops_lock); + +/** + * @name Cache Operations Sets + * @{ + */ + +static struct nl_cache_ops *__nl_cache_ops_lookup(const char *name) +{ + struct nl_cache_ops *ops; + + for (ops = cache_ops; ops; ops = ops->co_next) + if (!strcmp(ops->co_name, name)) + return ops; + + return NULL; +} + +/** + * Increment reference counter + * @arg ops Cache operations + */ +void nl_cache_ops_get(struct nl_cache_ops *ops) +{ + ops->co_refcnt++; +} + +/** + * Decrement reference counter + * @arg ops Cache operations + */ +void nl_cache_ops_put(struct nl_cache_ops *ops) +{ + ops->co_refcnt--; +} + +/** + * Lookup cache operations by name + * @arg name name of the cache type + * + * @attention This function is not safe, it does not increment the reference + * counter. Please use nl_cache_ops_lookup_safe(). + * + * @return The cache operations or NULL if not found. + */ +struct nl_cache_ops *nl_cache_ops_lookup(const char *name) +{ + struct nl_cache_ops *ops; + + nl_read_lock(&cache_ops_lock); + ops = __nl_cache_ops_lookup(name); + nl_read_unlock(&cache_ops_lock); + + return ops; +} + +/** + * Lookup cache operations by name + * @arg name name of the cache type + * + * @note The reference counter of the returned cache operation is incremented + * and must be decremented after use with nl_cache_ops_put(). + * + * @return The cache operations or NULL if not found. + */ +struct nl_cache_ops *nl_cache_ops_lookup_safe(const char *name) +{ + struct nl_cache_ops *ops; + + nl_write_lock(&cache_ops_lock); + if ((ops = __nl_cache_ops_lookup(name))) + nl_cache_ops_get(ops); + nl_write_unlock(&cache_ops_lock); + + return ops; +} + +static struct nl_cache_ops *__cache_ops_associate(int protocol, int msgtype) +{ + int i; + struct nl_cache_ops *ops; + + for (ops = cache_ops; ops; ops = ops->co_next) { + if (ops->co_protocol != protocol) + continue; + + for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) + if (ops->co_msgtypes[i].mt_id == msgtype) + return ops; + } + + return NULL; +} + +/** + * Associate protocol and message type to cache operations + * @arg protocol netlink protocol + * @arg msgtype netlink message type + * + * @attention This function is not safe, it does not increment the reference + * counter. Please use nl_cache_ops_associate_safe(). + * + * @see nl_cache_ops_associate_safe() + * + * @return The cache operations or NULL if no match found. + */ +struct nl_cache_ops *nl_cache_ops_associate(int protocol, int msgtype) +{ + struct nl_cache_ops *ops; + + nl_read_lock(&cache_ops_lock); + ops = __cache_ops_associate(protocol, msgtype); + nl_read_unlock(&cache_ops_lock); + + return ops; +} + +/** + * Associate protocol and message type to cache operations + * @arg protocol netlink protocol + * @arg msgtype netlink message type + * + * Searches the registered cache operations for a matching protocol + * and message type. + * + * @note The reference counter of the returned cache operation is incremented + * and must be decremented after use with nl_cache_ops_put(). + * + * @return The cache operations or NULL if no no match was found. + */ +struct nl_cache_ops *nl_cache_ops_associate_safe(int protocol, int msgtype) +{ + struct nl_cache_ops *ops; + + nl_write_lock(&cache_ops_lock); + if ((ops = __cache_ops_associate(protocol, msgtype))) + nl_cache_ops_get(ops); + nl_write_unlock(&cache_ops_lock); + + return ops; +} + +/** + * Lookup message type cache association + * @arg ops cache operations + * @arg msgtype netlink message type + * + * Searches for a matching message type association ing the specified + * cache operations. + * + * @attention The guranteed lifetime of the returned message type is bound + * to the lifetime of the underlying cache operations. + * + * @return A message type association or NULL. + */ +struct nl_msgtype *nl_msgtype_lookup(struct nl_cache_ops *ops, int msgtype) +{ + int i; + + for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) + if (ops->co_msgtypes[i].mt_id == msgtype) + return &ops->co_msgtypes[i]; + + return NULL; +} + +/* Must hold cache_ops_lock */ +static struct nl_cache_ops *cache_ops_lookup_for_obj(struct nl_object_ops *obj_ops) +{ + struct nl_cache_ops *ops; + + for (ops = cache_ops; ops; ops = ops->co_next) + if (ops->co_obj_ops == obj_ops) + return ops; + + return NULL; + +} + +/** + * Call a function for each registered cache operation + * @arg cb Callback function to be called + * @arg arg User specific argument. + */ +void nl_cache_ops_foreach(void (*cb)(struct nl_cache_ops *, void *), void *arg) +{ + struct nl_cache_ops *ops; + + nl_read_lock(&cache_ops_lock); + for (ops = cache_ops; ops; ops = ops->co_next) + cb(ops, arg); + nl_read_unlock(&cache_ops_lock); +} + +/** + * Set default flags for caches of this type + * @arg ops Cache ops + * @arg flags Flags to set + * + * The cache operation flags will be derived to all caches allocates + * based on this set of cache operations. + */ +void nl_cache_ops_set_flags(struct nl_cache_ops *ops, unsigned int flags) +{ + nl_write_lock(&cache_ops_lock); + ops->co_flags |= flags; + nl_write_unlock(&cache_ops_lock); +} + +/** + * Register a set of cache operations + * @arg ops cache operations + * + * Called by users of caches to announce the avaibility of + * a certain cache type. + * + * @return 0 on success or a negative error code. + */ +int nl_cache_mngt_register(struct nl_cache_ops *ops) +{ + if (!ops->co_name || !ops->co_obj_ops) + return -NLE_INVAL; + + /* oo_keygen() also needs oo_compare() */ + BUG_ON (ops->co_obj_ops->oo_keygen && !ops->co_obj_ops->oo_compare); + + nl_write_lock(&cache_ops_lock); + if (__nl_cache_ops_lookup(ops->co_name)) { + nl_write_unlock(&cache_ops_lock); + return -NLE_EXIST; + } + + ops->co_refcnt = 0; + ops->co_next = cache_ops; + cache_ops = ops; + nl_write_unlock(&cache_ops_lock); + + NL_DBG(1, "Registered cache operations %s\n", ops->co_name); + + return 0; +} + +/** + * Unregister a set of cache operations + * @arg ops cache operations + * + * Called by users of caches to announce a set of + * cache operations is no longer available. The + * specified cache operations must have been registered + * previously using nl_cache_mngt_register() + * + * @return 0 on success or a negative error code + */ +int nl_cache_mngt_unregister(struct nl_cache_ops *ops) +{ + struct nl_cache_ops *t, **tp; + int err = 0; + + nl_write_lock(&cache_ops_lock); + + if (ops->co_refcnt > 0) { + err = -NLE_BUSY; + goto errout; + } + + for (tp = &cache_ops; (t=*tp) != NULL; tp = &t->co_next) + if (t == ops) + break; + + if (!t) { + err = -NLE_NOCACHE; + goto errout; + } + + NL_DBG(1, "Unregistered cache operations %s\n", ops->co_name); + + *tp = t->co_next; +errout: + nl_write_unlock(&cache_ops_lock); + + return err; +} + +/** @} */ + +/** + * @name Global Cache Provisioning/Requiring + * @{ + */ + +/** + * Provide a cache for global use + * @arg cache cache to provide + * + * Offers the specified cache to be used by other modules. + * Only one cache per type may be shared at a time, + * a previsouly provided caches will be overwritten. + */ +void nl_cache_mngt_provide(struct nl_cache *cache) +{ + struct nl_cache_ops *ops; + + nl_write_lock(&cache_ops_lock); + + ops = cache_ops_lookup_for_obj(cache->c_ops->co_obj_ops); + if (!ops) + BUG(); + else { + nl_cache_get(cache); + + /* + * Hold a reference to the cache operations to ensure the + * ops don't go away while we use it to store the cache pointer. + */ + if (!ops->co_major_cache) + nl_cache_ops_get(ops); + + ops->co_major_cache = cache; + } + + nl_write_unlock(&cache_ops_lock); +} + +/** + * Unprovide a cache for global use + * @arg cache cache to unprovide + * + * Cancels the offer to use a cache globally. The + * cache will no longer be returned via lookups but + * may still be in use. + */ +void nl_cache_mngt_unprovide(struct nl_cache *cache) +{ + struct nl_cache_ops *ops; + + nl_write_lock(&cache_ops_lock); + + ops = cache_ops_lookup_for_obj(cache->c_ops->co_obj_ops); + if (!ops) + BUG(); + else if (ops->co_major_cache == cache) { + nl_cache_free(ops->co_major_cache); + nl_cache_ops_put(ops); + ops->co_major_cache = NULL; + } + + nl_write_unlock(&cache_ops_lock); +} + +struct nl_cache *__nl_cache_mngt_require(const char *name) +{ + struct nl_cache_ops *ops; + struct nl_cache *cache = NULL; + + ops = nl_cache_ops_lookup_safe(name); + if (ops) { + cache = ops->co_major_cache; + nl_cache_ops_put(ops); + } + + return cache; +} + +/** + * Return cache previously provided via nl_cache_mngt_provide() + * @arg name Name of cache to lookup + * + * @attention This function is not safe, it does not increment the reference + * counter. Please use nl_cache_mngt_require_safe(). + * + * @see nl_cache_mngt_require_safe() + * + * @return Pointer to cache or NULL if none registered + */ +struct nl_cache *nl_cache_mngt_require(const char *name) +{ + struct nl_cache *cache; + + if (!(cache = __nl_cache_mngt_require(name))) + NL_DBG(1, "Application BUG: Your application must " + "call nl_cache_mngt_provide() and\nprovide a valid " + "%s cache to be used for internal lookups.\nSee the " + " API documentation for more details.\n", name); + + return cache; +} + +/** + * Return cache previously provided via nl_cache_mngt_provide() + * @arg name Name of cache to lookup + * + * @note The reference counter of the returned cache is incremented + * and must be decremented after use with nl_cache_put(). + * + * @return Pointer to cache or NULL if none registered + */ +struct nl_cache *nl_cache_mngt_require_safe(const char *name) +{ + struct nl_cache *cache; + + if ((cache = nl_cache_mngt_require(name))) + nl_cache_get(cache); + + return cache; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/cli/cls/basic.c b/libnl/lib/cli/cls/basic.c new file mode 100644 index 0000000..5d33e89 --- /dev/null +++ b/libnl/lib/cli/cls/basic.c @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2011 Thomas Graf + */ + +#include "nl-default.h" + +#include +#include +#include +#include + +#include "base/nl-base-utils.h" + +static void print_usage(void) +{ + printf( +"Usage: nl-cls-add [...] basic [OPTIONS]...\n" +"\n" +"OPTIONS\n" +" -h, --help Show this help text.\n" +" -t, --target=ID Target class to send matching packets to\n" +" -e, --ematch=EXPR Ematch expression\n" +"\n" +"EXAMPLE" +" # Create a \"catch-all\" classifier, attached to \"q_root\", classyfing\n" +" # all not yet classified packets to class \"c_default\"\n" +" nl-cls-add --dev=eth0 --parent=q_root basic --target=c_default\n"); +} + +static void parse_argv(struct rtnl_tc *tc, int argc, char **argv) +{ + struct rtnl_cls *cls = (struct rtnl_cls *) tc; + struct rtnl_ematch_tree *tree; + uint32_t target; + int err; + + for (;;) { + int c, optidx = 0; + enum { + ARG_TARGET = 257, + ARG_DEFAULT = 258, + }; + static struct option long_opts[] = { + { "help", 0, 0, 'h' }, + { "target", 1, 0, 't' }, + { "ematch", 1, 0, 'e' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "ht:e:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(); + exit(0); + + case 't': + if ((err = rtnl_tc_str2handle(optarg, &target)) < 0) + nl_cli_fatal(err, "Unable to parse target \"%s\":", + optarg, nl_geterror(err)); + + rtnl_basic_set_target(cls, target); + break; + + case 'e': + tree = nl_cli_cls_parse_ematch(cls, optarg); + rtnl_basic_set_ematch(cls, tree); + break; + } + } +} + +static struct nl_cli_tc_module basic_module = +{ + .tm_name = "basic", + .tm_type = RTNL_TC_TYPE_CLS, + .tm_parse_argv = parse_argv, +}; + +static void _nl_init basic_init(void) +{ + nl_cli_tc_register(&basic_module); +} + +static void _nl_exit basic_exit(void) +{ + nl_cli_tc_unregister(&basic_module); +} diff --git a/libnl/lib/cli/cls/cgroup.c b/libnl/lib/cli/cls/cgroup.c new file mode 100644 index 0000000..296df69 --- /dev/null +++ b/libnl/lib/cli/cls/cgroup.c @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2011 Thomas Graf + */ + +#include "nl-default.h" + +#include +#include +#include +#include + +static void print_usage(void) +{ + printf( +"Usage: nl-cls-add [...] cgroup [OPTIONS]...\n" +"\n" +"OPTIONS\n" +" -h, --help Show this help text.\n" +" -e, --ematch=EXPR Ematch expression\n" +"\n" +"EXAMPLE" +" nl-cls-add --dev=eth0 --parent=q_root cgroup\n"); +} + +static void parse_argv(struct rtnl_tc *tc, int argc, char **argv) +{ + struct rtnl_cls *cls = (struct rtnl_cls *) tc; + struct rtnl_ematch_tree *tree; + + for (;;) { + int c, optidx = 0; + static struct option long_opts[] = { + { "help", 0, 0, 'h' }, + { "ematch", 1, 0, 'e' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "he:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(); + exit(0); + + case 'e': + tree = nl_cli_cls_parse_ematch(cls, optarg); + rtnl_cgroup_set_ematch(cls, tree); + break; + } + } +} + +static struct nl_cli_tc_module cgroup_module = +{ + .tm_name = "cgroup", + .tm_type = RTNL_TC_TYPE_CLS, + .tm_parse_argv = parse_argv, +}; + +static void _nl_init cgroup_init(void) +{ + nl_cli_tc_register(&cgroup_module); +} + +static void _nl_exit cgroup_exit(void) +{ + nl_cli_tc_unregister(&cgroup_module); +} diff --git a/libnl/lib/cli/qdisc/bfifo.c b/libnl/lib/cli/qdisc/bfifo.c new file mode 100644 index 0000000..30db045 --- /dev/null +++ b/libnl/lib/cli/qdisc/bfifo.c @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2011 Thomas Graf + */ + +#include "nl-default.h" + +#include +#include +#include + +static void print_usage(void) +{ + printf( +"Usage: nl-qdisc-add [...] bfifo [OPTIONS]...\n" +"\n" +"OPTIONS\n" +" --help Show this help text.\n" +" --limit=LIMIT Maximum queue length in number of bytes.\n" +"\n" +"EXAMPLE" +" # Attach bfifo with a 4KB bytes limit to eth1\n" +" nl-qdisc-add --dev=eth1 --parent=root bfifo --limit=4096\n"); +} + +static void bfifo_parse_argv(struct rtnl_tc *tc, int argc, char **argv) +{ + struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc; + int limit; + + for (;;) { + int c, optidx = 0; + enum { + ARG_LIMIT = 257, + }; + static struct option long_opts[] = { + { "help", 0, 0, 'h' }, + { "limit", 1, 0, ARG_LIMIT }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "h", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(); + return; + + case ARG_LIMIT: + limit = nl_size2int(optarg); + if (limit < 0) { + nl_cli_fatal(limit, "Unable to parse bfifo limit " + "\"%s\": Invalid format.", optarg); + } + + rtnl_qdisc_fifo_set_limit(qdisc, limit); + break; + } + } +} + +static struct nl_cli_tc_module bfifo_module = +{ + .tm_name = "bfifo", + .tm_type = RTNL_TC_TYPE_QDISC, + .tm_parse_argv = bfifo_parse_argv, +}; + +static void _nl_init bfifo_init(void) +{ + nl_cli_tc_register(&bfifo_module); +} + +static void _nl_exit bfifo_exit(void) +{ + nl_cli_tc_unregister(&bfifo_module); +} diff --git a/libnl/lib/cli/qdisc/blackhole.c b/libnl/lib/cli/qdisc/blackhole.c new file mode 100644 index 0000000..0ae0a53 --- /dev/null +++ b/libnl/lib/cli/qdisc/blackhole.c @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2011 Thomas Graf + */ + +#include "nl-default.h" + +#include +#include + +static void print_usage(void) +{ + printf( +"Usage: nl-qdisc-add [...] blackhole [OPTIONS]...\n" +"\n" +"OPTIONS\n" +" --help Show this help text.\n" +"\n" +"EXAMPLE" +" # Drop all outgoing packets on eth1\n" +" nl-qdisc-add --dev=eth1 --parent=root blackhole\n"); +} + +static void blackhole_parse_argv(struct rtnl_tc *tc, int argc, char **argv) +{ + for (;;) { + int c, optidx = 0; + static struct option long_opts[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "h", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(); + return; + } + } +} + +static struct nl_cli_tc_module blackhole_module = +{ + .tm_name = "blackhole", + .tm_type = RTNL_TC_TYPE_QDISC, + .tm_parse_argv = blackhole_parse_argv, +}; + +static void _nl_init blackhole_init(void) +{ + nl_cli_tc_register(&blackhole_module); +} + +static void _nl_exit blackhole_exit(void) +{ + nl_cli_tc_unregister(&blackhole_module); +} diff --git a/libnl/lib/cli/qdisc/fq_codel.c b/libnl/lib/cli/qdisc/fq_codel.c new file mode 100644 index 0000000..83e409a --- /dev/null +++ b/libnl/lib/cli/qdisc/fq_codel.c @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +#include "nl-default.h" + +#include +#include +#include + +static void print_usage(void) +{ + printf( +"Usage: nl-qdisc-add [...] fq_codel [OPTIONS]...\n" +"\n" +"OPTIONS\n" +" --help Show this help text.\n" +" --limit=LIMIT Maximum queue length in number of bytes.\n" +" --quantum=SIZE Amount of bytes to serve at once.\n" +" --flows=N Number of flows.\n" +" --interval=N The interval in usec.\n" +" --target=N The minimum delay in usec.\n" +"\n" +"EXAMPLE" +" # Attach fq_codel with a 4096 packets limit to eth1\n" +" nl-qdisc-add --dev=eth1 --parent=root fq_codel --limit=4096\n"); +} + +static void fq_codel_parse_argv(struct rtnl_tc *tc, int argc, char **argv) +{ + struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc; + int limit, flows; + uint32_t quantum, target, interval; + + for (;;) { + int c, optidx = 0; + enum { + ARG_LIMIT = 257, + ARG_QUANTUM = 258, + ARG_FLOWS, + ARG_INTERVAL, + ARG_TARGET, + }; + static struct option long_opts[] = { + { "help", 0, 0, 'h' }, + { "limit", 1, 0, ARG_LIMIT }, + { "quantum", 1, 0, ARG_QUANTUM }, + { "flows", 1, 0, ARG_FLOWS}, + { "interval", 1, 0, ARG_INTERVAL}, + { "target", 1, 0, ARG_TARGET}, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "h", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(); + return; + + case ARG_LIMIT: + limit = nl_cli_parse_u32(optarg); + rtnl_qdisc_fq_codel_set_limit(qdisc, limit); + break; + + case ARG_QUANTUM: + quantum = nl_cli_parse_u32(optarg); + rtnl_qdisc_fq_codel_set_quantum(qdisc, quantum); + break; + + case ARG_FLOWS: + flows = nl_cli_parse_u32(optarg); + rtnl_qdisc_fq_codel_set_flows(qdisc, flows); + break; + + case ARG_INTERVAL: + interval = nl_cli_parse_u32(optarg); + rtnl_qdisc_fq_codel_set_interval(qdisc, interval); + break; + + case ARG_TARGET: + target = nl_cli_parse_u32(optarg); + rtnl_qdisc_fq_codel_set_target(qdisc, target); + break; + + } + } +} + +static struct nl_cli_tc_module fq_codel_module = +{ + .tm_name = "fq_codel", + .tm_type = RTNL_TC_TYPE_QDISC, + .tm_parse_argv = fq_codel_parse_argv, +}; + +static void _nl_init fq_codel_init(void) +{ + nl_cli_tc_register(&fq_codel_module); +} + +static void _nl_exit fq_codel_exit(void) +{ + nl_cli_tc_unregister(&fq_codel_module); +} diff --git a/libnl/lib/cli/qdisc/hfsc.c b/libnl/lib/cli/qdisc/hfsc.c new file mode 100644 index 0000000..9ab7f7e --- /dev/null +++ b/libnl/lib/cli/qdisc/hfsc.c @@ -0,0 +1,249 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Cong Wang + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +static void print_qdisc_usage(void) +{ + printf( +"Usage: nl-qdisc-add [...] hfsc [OPTIONS]...\n" +"\n" +"OPTIONS\n" +" --help Show this help text.\n" +" --default=ID Default class for unclassified traffic.\n" +"\n" +"EXAMPLE" +" # Create hfsc root qdisc 1: and direct unclassified traffic to class 1:10\n" +" nl-qdisc-add --dev=eth1 --parent=root --handle=1: hfsc --default=10\n"); +} + +static void hfsc_parse_qdisc_argv(struct rtnl_tc *tc, int argc, char **argv) +{ + struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc; + + for (;;) { + int c, optidx = 0; + enum { + ARG_DEFAULT = 257, + }; + static struct option long_opts[] = { + { "help", 0, 0, 'h' }, + { "default", 1, 0, ARG_DEFAULT }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "hv", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'h': + print_qdisc_usage(); + return; + + case ARG_DEFAULT: + rtnl_qdisc_hfsc_set_defcls(qdisc, nl_cli_parse_u32(optarg)); + break; + } + } +} + +static void print_class_usage(void) +{ + printf( +"Usage: nl-class-add [...] hfsc [OPTIONS]...\n" +"\n" +"OPTIONS\n" +" --help Show this help text.\n" +" --ls=SC Link-sharing service curve\n" +" --rt=SC Real-time service curve\n" +" --sc=SC Specifiy both of the above\n" +" --ul=SC Upper limit\n" +" where SC := [ [ m1 bits ] d usec ] m2 bits\n" +"\n" +"EXAMPLE" +" # Attach class 1:1 to hfsc qdisc 1: and use rt and ls curve\n" +" nl-class-add --dev=eth1 --parent=1: --classid=1:1 hfsc --sc=m1:250,d:8,m2:100\n"); +} + +static int +hfsc_get_sc(char *optarg, struct tc_service_curve *sc) +{ + unsigned int m1 = 0, d = 0, m2 = 0; + char *tmp = strdup(optarg); + char *p, *endptr; + char *pp = tmp; + + if (!tmp) + return -ENOMEM; + + p = strstr(pp, "m1:"); + if (p) { + char *q; + p += 3; + if (*p == 0) + goto err; + q = strchr(p, ','); + if (!q) + goto err; + *q = 0; + m1 = strtoul(p, &endptr, 10); + if (endptr == p) + goto err; + pp = q + 1; + } + + p = strstr(pp, "d:"); + if (p) { + char *q; + p += 2; + if (*p == 0) + goto err; + q = strchr(p, ','); + if (!q) + goto err; + *q = 0; + d = strtoul(p, &endptr, 10); + if (endptr == p) + goto err; + pp = q + 1; + } + + p = strstr(pp, "m2:"); + if (p) { + p += 3; + if (*p == 0) + goto err; + m2 = strtoul(p, &endptr, 10); + if (endptr == p) + goto err; + } else + goto err; + + free(tmp); + sc->m1 = m1; + sc->d = d; + sc->m2 = m2; + return 0; + +err: + free(tmp); + return -EINVAL; +} + +static void hfsc_parse_class_argv(struct rtnl_tc *tc, int argc, char **argv) +{ + struct rtnl_class *class = (struct rtnl_class *) tc; + int arg_ok = 0, ret = -EINVAL; + + for (;;) { + int c, optidx = 0; + enum { + ARG_RT = 257, + ARG_LS = 258, + ARG_SC, + ARG_UL, + }; + static struct option long_opts[] = { + { "help", 0, 0, 'h' }, + { "rt", 1, 0, ARG_RT }, + { "ls", 1, 0, ARG_LS }, + { "sc", 1, 0, ARG_SC }, + { "ul", 1, 0, ARG_UL }, + { 0, 0, 0, 0 } + }; + struct tc_service_curve tsc; + + c = getopt_long(argc, argv, "h", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'h': + print_class_usage(); + return; + + case ARG_RT: + ret = hfsc_get_sc(optarg, &tsc); + if (ret < 0) { + nl_cli_fatal(ret, "Unable to parse sc " + "\"%s\": Invalid format.", optarg); + } + + rtnl_class_hfsc_set_rsc(class, &tsc); + arg_ok++; + break; + + case ARG_LS: + ret = hfsc_get_sc(optarg, &tsc); + if (ret < 0) { + nl_cli_fatal(ret, "Unable to parse sc " + "\"%s\": Invalid format.", optarg); + } + + rtnl_class_hfsc_set_fsc(class, &tsc); + arg_ok++; + break; + + case ARG_SC: + ret = hfsc_get_sc(optarg, &tsc); + if (ret < 0) { + nl_cli_fatal(ret, "Unable to parse sc " + "\"%s\": Invalid format.", optarg); + } + + rtnl_class_hfsc_set_rsc(class, &tsc); + rtnl_class_hfsc_set_fsc(class, &tsc); + arg_ok++; + break; + + case ARG_UL: + ret = hfsc_get_sc(optarg, &tsc); + if (ret < 0) { + nl_cli_fatal(ret, "Unable to parse sc " + "\"%s\": Invalid format.", optarg); + } + + rtnl_class_hfsc_set_usc(class, &tsc); + arg_ok++; + break; + } + } + + if (!arg_ok) + nl_cli_fatal(ret, "Invalid arguments"); +} + +static struct nl_cli_tc_module hfsc_qdisc_module = +{ + .tm_name = "hfsc", + .tm_type = RTNL_TC_TYPE_QDISC, + .tm_parse_argv = hfsc_parse_qdisc_argv, +}; + +static struct nl_cli_tc_module hfsc_class_module = +{ + .tm_name = "hfsc", + .tm_type = RTNL_TC_TYPE_CLASS, + .tm_parse_argv = hfsc_parse_class_argv, +}; + +static void _nl_init hfsc_init(void) +{ + nl_cli_tc_register(&hfsc_qdisc_module); + nl_cli_tc_register(&hfsc_class_module); +} + +static void _nl_exit hfsc_exit(void) +{ + nl_cli_tc_unregister(&hfsc_class_module); + nl_cli_tc_unregister(&hfsc_qdisc_module); +} diff --git a/libnl/lib/cli/qdisc/htb.c b/libnl/lib/cli/qdisc/htb.c new file mode 100644 index 0000000..5ca6ae5 --- /dev/null +++ b/libnl/lib/cli/qdisc/htb.c @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2011 Thomas Graf + */ + +#include "nl-default.h" + +#include +#include +#include + +static void print_qdisc_usage(void) +{ + printf( +"Usage: nl-qdisc-add [...] htb [OPTIONS]...\n" +"\n" +"OPTIONS\n" +" --help Show this help text.\n" +" --r2q=DIV Rate to quantum divisor (default: 10)\n" +" --default=ID Default class for unclassified traffic.\n" +"\n" +"EXAMPLE" +" # Create htb root qdisc 1: and direct unclassified traffic to class 1:10\n" +" nl-qdisc-add --dev=eth1 --parent=root --handle=1: htb --default=10\n"); +} + +static void htb_parse_qdisc_argv(struct rtnl_tc *tc, int argc, char **argv) +{ + struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc; + + for (;;) { + int c, optidx = 0; + enum { + ARG_R2Q = 257, + ARG_DEFAULT = 258, + }; + static struct option long_opts[] = { + { "help", 0, 0, 'h' }, + { "r2q", 1, 0, ARG_R2Q }, + { "default", 1, 0, ARG_DEFAULT }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "hv", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'h': + print_qdisc_usage(); + return; + + case ARG_R2Q: + rtnl_htb_set_rate2quantum(qdisc, nl_cli_parse_u32(optarg)); + break; + + case ARG_DEFAULT: + rtnl_htb_set_defcls(qdisc, nl_cli_parse_u32(optarg)); + break; + } + } +} + +static void print_class_usage(void) +{ + printf( +"Usage: nl-class-add [...] htb [OPTIONS]...\n" +"\n" +"OPTIONS\n" +" --help Show this help text.\n" +" --rate=RATE Rate limit.\n" +" --ceil=RATE Rate limit while borrowing (default: equal to --rate).\n" +" --prio=PRIO Priority, lower is served first (default: 0).\n" +" --quantum=SIZE Amount of bytes to serve at once (default: rate/r2q).\n" +" --burst=SIZE Max charge size of rate burst buffer (default: auto).\n" +" --cburst=SIZE Max charge size of ceil rate burst buffer (default: auto)\n" +"\n" +"EXAMPLE" +" # Attach class 1:1 to htb qdisc 1: and rate limit it to 20mbit\n" +" nl-class-add --dev=eth1 --parent=1: --classid=1:1 htb --rate=20mbit\n"); +} + +static void htb_parse_class_argv(struct rtnl_tc *tc, int argc, char **argv) +{ + struct rtnl_class *class = (struct rtnl_class *) tc; + long rate; + + for (;;) { + int c, optidx = 0; + enum { + ARG_RATE = 257, + ARG_QUANTUM = 258, + ARG_CEIL, + ARG_PRIO, + ARG_BURST, + ARG_CBURST, + }; + static struct option long_opts[] = { + { "help", 0, 0, 'h' }, + { "rate", 1, 0, ARG_RATE }, + { "quantum", 1, 0, ARG_QUANTUM }, + { "ceil", 1, 0, ARG_CEIL }, + { "prio", 1, 0, ARG_PRIO }, + { "burst", 1, 0, ARG_BURST }, + { "cburst", 1, 0, ARG_CBURST }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "h", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'h': + print_class_usage(); + return; + + case ARG_RATE: + rate = nl_size2int(optarg); + if (rate < 0) { + nl_cli_fatal(rate, "Unable to parse htb rate " + "\"%s\": Invalid format.", optarg); + } + + rtnl_htb_set_rate(class, rate); + break; + + case ARG_CEIL: + rate = nl_size2int(optarg); + if (rate < 0) { + nl_cli_fatal(rate, "Unable to parse htb ceil rate " + "\"%s\": Invalid format.", optarg); + } + + rtnl_htb_set_ceil(class, rate); + break; + + case ARG_PRIO: + rtnl_htb_set_prio(class, nl_cli_parse_u32(optarg)); + break; + + case ARG_QUANTUM: + rate = nl_size2int(optarg); + if (rate < 0) { + nl_cli_fatal(rate, "Unable to parse quantum " + "\"%s\": Invalid format.", optarg); + } + + rtnl_htb_set_quantum(class, rate); + break; + + case ARG_BURST: + rate = nl_size2int(optarg); + if (rate < 0) { + nl_cli_fatal(rate, "Unable to parse burst " + "\"%s\": Invalid format.", optarg); + } + + rtnl_htb_set_rbuffer(class, rate); + break; + + case ARG_CBURST: + rate = nl_size2int(optarg); + if (rate < 0) { + nl_cli_fatal(rate, "Unable to parse cburst " + "\"%s\": Invalid format.", optarg); + } + + rtnl_htb_set_cbuffer(class, rate); + break; + } + } +} + +static struct nl_cli_tc_module htb_qdisc_module = +{ + .tm_name = "htb", + .tm_type = RTNL_TC_TYPE_QDISC, + .tm_parse_argv = htb_parse_qdisc_argv, +}; + +static struct nl_cli_tc_module htb_class_module = +{ + .tm_name = "htb", + .tm_type = RTNL_TC_TYPE_CLASS, + .tm_parse_argv = htb_parse_class_argv, +}; + +static void _nl_init htb_init(void) +{ + nl_cli_tc_register(&htb_qdisc_module); + nl_cli_tc_register(&htb_class_module); +} + +static void _nl_exit htb_exit(void) +{ + nl_cli_tc_unregister(&htb_class_module); + nl_cli_tc_unregister(&htb_qdisc_module); +} diff --git a/libnl/lib/cli/qdisc/ingress.c b/libnl/lib/cli/qdisc/ingress.c new file mode 100644 index 0000000..9c09c4f --- /dev/null +++ b/libnl/lib/cli/qdisc/ingress.c @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +#include "nl-default.h" + +#include +#include + +static void print_usage(void) +{ + printf( +"Usage: nl-qdisc-add [...] ingress\n" +"\n" +"OPTIONS\n" +" --help Show this help text.\n" +"\n" +"EXAMPLE" +" # Attach ingress to eth1\n" +" nl-qdisc-add --dev=eth1 --parent=root ingress\n"); +} + +static void ingress_parse_argv(struct rtnl_tc *tc, int argc, char **argv) +{ + for (;;) { + int c, optidx = 0; + static struct option long_opts[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "h", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(); + return; + } + } +} + +static struct nl_cli_tc_module ingress_module = +{ + .tm_name = "ingress", + .tm_type = RTNL_TC_TYPE_QDISC, + .tm_parse_argv = ingress_parse_argv, +}; + +static void _nl_init ingress_init(void) +{ + nl_cli_tc_register(&ingress_module); +} + +static void _nl_exit ingress_exit(void) +{ + nl_cli_tc_unregister(&ingress_module); +} diff --git a/libnl/lib/cli/qdisc/pfifo.c b/libnl/lib/cli/qdisc/pfifo.c new file mode 100644 index 0000000..fbaa872 --- /dev/null +++ b/libnl/lib/cli/qdisc/pfifo.c @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2011 Thomas Graf + */ + +#include "nl-default.h" + +#include +#include +#include + +static void print_usage(void) +{ + printf( +"Usage: nl-qdisc-add [...] pfifo [OPTIONS]...\n" +"\n" +"OPTIONS\n" +" --help Show this help text.\n" +" --limit=LIMIT Maximum queue length in number of packets.\n" +"\n" +"EXAMPLE" +" # Attach pfifo with a 32 packet limit to eth1\n" +" nl-qdisc-add --dev=eth1 --parent=root pfifo --limit=32\n"); +} + +static void pfifo_parse_argv(struct rtnl_tc *tc, int argc, char **argv) +{ + struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc; + + for (;;) { + int c, optidx = 0; + enum { + ARG_LIMIT = 257, + }; + static struct option long_opts[] = { + { "help", 0, 0, 'h' }, + { "limit", 1, 0, ARG_LIMIT }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "h", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(); + return; + + case ARG_LIMIT: + rtnl_qdisc_fifo_set_limit(qdisc, nl_cli_parse_u32(optarg)); + break; + } + } +} + +static struct nl_cli_tc_module pfifo_module = +{ + .tm_name = "pfifo", + .tm_type = RTNL_TC_TYPE_QDISC, + .tm_parse_argv = pfifo_parse_argv, +}; + +static void _nl_init pfifo_init(void) +{ + nl_cli_tc_register(&pfifo_module); +} + +static void _nl_exit pfifo_exit(void) +{ + nl_cli_tc_unregister(&pfifo_module); +} diff --git a/libnl/lib/cli/qdisc/plug.c b/libnl/lib/cli/qdisc/plug.c new file mode 100644 index 0000000..212a6cf --- /dev/null +++ b/libnl/lib/cli/qdisc/plug.c @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2012 Shriram Rajagopalan + */ + +#include "nl-default.h" + +#include +#include +#include + +static void print_usage(void) +{ + printf( +"Usage: nl-qdisc-add [...] plug [OPTIONS]...\n" +"\n" +"OPTIONS\n" +" --help Show this help text.\n" +" --limit Maximum queue length in bytes.\n" +" --buffer create a new buffer(plug) and queue incoming traffic into it.\n" +" --release-one release traffic from previous buffer.\n" +" --release-indefinite stop buffering and release all (buffered and new) packets.\n" +"\n" +"EXAMPLE" +" # Attach plug qdisc with 32KB queue size to ifb0\n" +" nl-qdisc-add --dev=ifb0 --parent=root plug --limit=32768\n" +" # Plug network traffic arriving at ifb0\n" +" nl-qdisc-add --dev=ifb0 --parent=root --update plug --buffer\n" +" # Unplug traffic arriving at ifb0 indefinitely\n" +" nl-qdisc-add --dev=ifb0 --parent=root --update plug --release-indefinite\n\n" +" # If operating in output buffering mode:\n" +" # at time t=t0, create a new output buffer b0 to hold network output\n" +" nl-qdisc-add --dev=ifb0 --parent=root --update plug --buffer\n\n" +" # at time t=t1, take a checkpoint c0, create a new output buffer b1\n" +" nl-qdisc-add --dev=ifb0 --parent=root --update plug --buffer\n" +" # at time t=t1+r, after c0 is committed, release b0\n" +" nl-qdisc-add --dev=ifb0 --parent=root --update plug --release-one\n\n" +" # at time t=t2, take a checkpoint c1, create a new output buffer b2\n" +" nl-qdisc-add --dev=ifb0 --parent=root --update plug --buffer\n" +" # at time t=t2+r, after c1 is committed, release b1\n" +" nl-qdisc-add --dev=ifb0 --parent=root --update plug --release-one\n"); +} + +static void plug_parse_argv(struct rtnl_tc *tc, int argc, char **argv) +{ + struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc; + + for (;;) { + int c, optidx = 0; + enum { + ARG_LIMIT = 257, + ARG_BUFFER = 258, + ARG_RELEASE_ONE = 259, + ARG_RELEASE_INDEFINITE = 260, + }; + static struct option long_opts[] = { + { "help", 0, 0, 'h' }, + { "limit", 1, 0, ARG_LIMIT }, + { "buffer", 0, 0, ARG_BUFFER }, + { "release-one", 0, 0, ARG_RELEASE_ONE }, + { "release-indefinite", 0, 0, ARG_RELEASE_INDEFINITE }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "h", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(); + return; + + case ARG_LIMIT: + rtnl_qdisc_plug_set_limit(qdisc, nl_cli_parse_u32(optarg)); + break; + + case ARG_BUFFER: + rtnl_qdisc_plug_buffer(qdisc); + break; + + case ARG_RELEASE_ONE: + rtnl_qdisc_plug_release_one(qdisc); + break; + + case ARG_RELEASE_INDEFINITE: + rtnl_qdisc_plug_release_indefinite(qdisc); + break; + } + } +} + +static struct nl_cli_tc_module plug_module = +{ + .tm_name = "plug", + .tm_type = RTNL_TC_TYPE_QDISC, + .tm_parse_argv = plug_parse_argv, +}; + +static void _nl_init plug_init(void) +{ + nl_cli_tc_register(&plug_module); +} + +static void _nl_exit plug_exit(void) +{ + nl_cli_tc_unregister(&plug_module); +} diff --git a/libnl/lib/data.c b/libnl/lib/data.c new file mode 100644 index 0000000..1052578 --- /dev/null +++ b/libnl/lib/data.c @@ -0,0 +1,198 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup core_types + * @defgroup data Abstract Data + * + * Abstract data type representing a binary data blob. + * + * Related sections in the development guide: + * - @core_doc{_abstract_data, Abstract Data} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include + +#include "nl-priv-dynamic-core/nl-core.h" + +/** + * @name General + * @{ + */ + +/** + * Allocate a new abstract data object. + * @arg buf Data buffer containing the actual data. + * @arg size Size of data buffer. + * + * Allocates a new abstract data and copies the specified data + * buffer into the new handle. + * + * @return Newly allocated data handle or NULL + */ +struct nl_data *nl_data_alloc(const void *buf, size_t size) +{ + struct nl_data *data; + + data = calloc(1, sizeof(*data)); + if (!data) + goto errout; + + data->d_data = calloc(1, size); + if (!data->d_data) { + free(data); + goto errout; + } + + data->d_size = size; + + if (buf) + memcpy(data->d_data, buf, size); + + return data; +errout: + return NULL; +} + +/** + * Allocate abstract data object based on netlink attribute. + * @arg nla Netlink attribute of unspecific type. + * + * Allocates a new abstract data and copies the payload of the + * attribute to the abstract data object. + * + * @see nla_data_alloc + * @return Newly allocated data handle or NULL + */ +struct nl_data *nl_data_alloc_attr(const struct nlattr *nla) +{ + return nl_data_alloc(nla_data(nla), nla_len(nla)); +} + +/** + * Clone an abstract data object. + * @arg src Abstract data object + * + * @return Cloned object or NULL + */ +struct nl_data *nl_data_clone(const struct nl_data *src) +{ + return nl_data_alloc(src->d_data, src->d_size); +} + +/** + * Append data to an abstract data object. + * @arg data Abstract data object. + * @arg buf Data buffer containing the data to be appended. + * @arg size Size of data to be apppended. + * + * Reallocates an abstract data and copies the specified data + * buffer into the new handle. + * + * @return 0 on success or a negative error code + */ +int nl_data_append(struct nl_data *data, const void *buf, size_t size) +{ + if (size > 0) { + char *d_data = realloc(data->d_data, data->d_size + size); + if (!d_data) + return -NLE_NOMEM; + + if (buf) + memcpy(d_data + data->d_size, buf, size); + else + memset(d_data + data->d_size, 0, size); + + data->d_data = d_data; + data->d_size += size; + } + + return 0; +} + +/** + * Free an abstract data object. + * @arg data Abstract data object. + */ +void nl_data_free(struct nl_data *data) +{ + if (data) + free(data->d_data); + + free(data); +} + +/** @} */ + +/** + * @name Attribute Access + * @{ + */ + +/** + * Get data buffer of abstract data object. + * @arg data Abstract data object. + * @return Data buffer or NULL if empty. + */ +void *nl_data_get(const struct nl_data *data) +{ + if (data->d_size > 0) + return (void*)data->d_data; + return NULL; +} + +/** + * Get size of data buffer of abstract data object. + * @arg data Abstract data object. + * @return Size of data buffer. + */ +size_t nl_data_get_size(const struct nl_data *data) +{ + return data->d_size; +} + +/** @} */ + +/** + * @name Misc + * @{ + */ + +/** + * Compare two abstract data objects. + * @arg a Abstract data object. + * @arg b Another abstract data object. + * @return An integer less than, equal to, or greater than zero if + * a is found, respectively, to be less than, to match, or + * be greater than b. + */ +int nl_data_cmp(const struct nl_data *a, const struct nl_data *b) +{ + const void *a_ = nl_data_get(a); + const void *b_ = nl_data_get(b); + + if (a_ && b_) + return memcmp(a_, b_, nl_data_get_size(a)); + else + return -1; +} + +/** @} */ +/** @} */ diff --git a/libnl/lib/error.c b/libnl/lib/error.c new file mode 100644 index 0000000..9b6d806 --- /dev/null +++ b/libnl/lib/error.c @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008 Thomas Graf + */ + +#include "nl-default.h" + +#include + +static const char *errmsg[NLE_MAX+1] = { +[NLE_SUCCESS] = "Success", +[NLE_FAILURE] = "Unspecific failure", +[NLE_INTR] = "Interrupted system call", +[NLE_BAD_SOCK] = "Bad socket", +[NLE_AGAIN] = "Try again", +[NLE_NOMEM] = "Out of memory", +[NLE_EXIST] = "Object exists", +[NLE_INVAL] = "Invalid input data or parameter", +[NLE_RANGE] = "Input data out of range", +[NLE_MSGSIZE] = "Message size not sufficient", +[NLE_OPNOTSUPP] = "Operation not supported", +[NLE_AF_NOSUPPORT] = "Address family not supported", +[NLE_OBJ_NOTFOUND] = "Object not found", +[NLE_NOATTR] = "Attribute not available", +[NLE_MISSING_ATTR] = "Missing attribute", +[NLE_AF_MISMATCH] = "Address family mismatch", +[NLE_SEQ_MISMATCH] = "Message sequence number mismatch", +[NLE_MSG_OVERFLOW] = "Kernel reported message overflow", +[NLE_MSG_TRUNC] = "Kernel reported truncated message", +[NLE_NOADDR] = "Invalid address for specified address family", +[NLE_SRCRT_NOSUPPORT] = "Source based routing not supported", +[NLE_MSG_TOOSHORT] = "Netlink message is too short", +[NLE_MSGTYPE_NOSUPPORT] = "Netlink message type is not supported", +[NLE_OBJ_MISMATCH] = "Object type does not match cache", +[NLE_NOCACHE] = "Unknown or invalid cache type", +[NLE_BUSY] = "Object busy", +[NLE_PROTO_MISMATCH] = "Protocol mismatch", +[NLE_NOACCESS] = "No Access", +[NLE_PERM] = "Operation not permitted", +[NLE_PKTLOC_FILE] = "Unable to open packet location file", +[NLE_PARSE_ERR] = "Unable to parse object", +[NLE_NODEV] = "No such device", +[NLE_IMMUTABLE] = "Immutable attribute", +[NLE_DUMP_INTR] = "Dump inconsistency detected, interrupted", +[NLE_ATTRSIZE] = "Attribute max length exceeded", +}; + +/** + * Return error message for an error code + * @return error message + */ +const char *nl_geterror(int error) +{ + error = abs(error); + + if (error > NLE_MAX) + error = NLE_FAILURE; + + return errmsg[error]; +} + +/** + * Print a libnl error message + * @arg s error message prefix + * + * Prints the error message of the call that failed last. + * + * If s is not NULL and *s is not a null byte the argument + * string is printed, followed by a colon and a blank. Then + * the error message and a new-line. + */ +void nl_perror(int error, const char *s) +{ + if (s && *s) + fprintf(stderr, "%s: %s\n", s, nl_geterror(error)); + else + fprintf(stderr, "%s\n", nl_geterror(error)); +} + +int nl_syserr2nlerr(int error) +{ + error = abs(error); + + switch (error) { + case EBADF: return NLE_BAD_SOCK; + case EADDRINUSE: return NLE_EXIST; + case EEXIST: return NLE_EXIST; + case EADDRNOTAVAIL: return NLE_NOADDR; + case ESRCH: /* fall through */ + case ENOENT: return NLE_OBJ_NOTFOUND; + case EINTR: return NLE_INTR; + case EAGAIN: return NLE_AGAIN; + case ENOTSOCK: return NLE_BAD_SOCK; + case ENOPROTOOPT: return NLE_INVAL; + case EFAULT: return NLE_INVAL; + case EACCES: return NLE_NOACCESS; + case EINVAL: return NLE_INVAL; + case ENOBUFS: return NLE_NOMEM; + case ENOMEM: return NLE_NOMEM; + case EAFNOSUPPORT: return NLE_AF_NOSUPPORT; + case EPROTONOSUPPORT: return NLE_PROTO_MISMATCH; + case EOPNOTSUPP: return NLE_OPNOTSUPP; + case EPERM: return NLE_PERM; + case EBUSY: return NLE_BUSY; + case ERANGE: return NLE_RANGE; + case ENODEV: return NLE_NODEV; + default: return NLE_FAILURE; + } +} + +/** @} */ + diff --git a/libnl/lib/fib_lookup/lookup.c b/libnl/lib/fib_lookup/lookup.c new file mode 100644 index 0000000..260f1d9 --- /dev/null +++ b/libnl/lib/fib_lookup/lookup.c @@ -0,0 +1,363 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup fib_lookup FIB Lookup + * @brief + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nl-priv-dynamic-core/object-api.h" +#include "nl-priv-dynamic-core/cache-api.h" +#include "nl-priv-dynamic-core/nl-core.h" + +/** @cond SKIP */ +struct flnl_result +{ + NLHDR_COMMON + + struct flnl_request * fr_req; + uint8_t fr_table_id; + uint8_t fr_prefixlen; + uint8_t fr_nh_sel; + uint8_t fr_type; + uint8_t fr_scope; + uint32_t fr_error; +}; + +static struct nl_cache_ops fib_lookup_ops; +static struct nl_object_ops result_obj_ops; + +/* not exported so far */ +struct fib_result_nl { + uint32_t fl_addr; /* To be looked up*/ + uint32_t fl_fwmark; + unsigned char fl_tos; + unsigned char fl_scope; + unsigned char tb_id_in; + + unsigned char tb_id; /* Results */ + unsigned char prefixlen; + unsigned char nh_sel; + unsigned char type; + unsigned char scope; + int err; +}; +/** @endcond */ + +static void result_free_data(struct nl_object *obj) +{ + struct flnl_result *res = nl_object_priv(obj); + + if (res && res->fr_req) + nl_object_put(OBJ_CAST(res->fr_req)); +} + +static int result_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct flnl_result *dst = nl_object_priv(_dst); + struct flnl_result *src = nl_object_priv(_src); + + dst->fr_req = NULL; + + if (src->fr_req) { + if (!(dst->fr_req = (struct flnl_request *) nl_object_clone(OBJ_CAST(src->fr_req)))) + return -NLE_NOMEM; + } + + return 0; +} + +static int result_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *n, struct nl_parser_param *pp) +{ + struct flnl_result *res; + struct fib_result_nl *fr; + struct nl_addr *addr; + int err = -NLE_INVAL; + + res = flnl_result_alloc(); + if (!res) + goto errout; + + res->ce_msgtype = n->nlmsg_type; + + res->fr_req = flnl_request_alloc(); + if (!res->fr_req) + goto errout; + + fr = nlmsg_data(n); + addr = nl_addr_build(AF_INET, &fr->fl_addr, 4); + if (!addr) + goto errout; + err = flnl_request_set_addr(res->fr_req, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + + flnl_request_set_fwmark(res->fr_req, fr->fl_fwmark); + flnl_request_set_tos(res->fr_req, fr->fl_tos); + flnl_request_set_scope(res->fr_req, fr->fl_scope); + flnl_request_set_table(res->fr_req, fr->tb_id_in); + + res->fr_table_id = fr->tb_id; + res->fr_prefixlen = fr->prefixlen; + res->fr_nh_sel = fr->nh_sel; + res->fr_type = fr->type; + res->fr_scope = fr->scope; + res->fr_error = fr->err; + + err = pp->pp_cb((struct nl_object *) res, pp); + if (err < 0) + goto errout; + + /* REAL HACK, fib_lookup doesn't support ACK nor does it + * send a DONE message, enforce end of message stream + * after just the first message */ + err = NL_STOP; + +errout: + flnl_result_put(res); + return err; +} + +static void result_dump_line(struct nl_object *obj, struct nl_dump_params *p) +{ + struct flnl_result *res = (struct flnl_result *) obj; + char buf[256]; + + nl_dump_line(p, "table %s prefixlen %u next-hop-selector %u\n", + rtnl_route_table2str(res->fr_table_id, buf, sizeof(buf)), + res->fr_prefixlen, res->fr_nh_sel); + nl_dump_line(p, "type %s ", + nl_rtntype2str(res->fr_type, buf, sizeof(buf))); + nl_dump(p, "scope %s error %s (%d)\n", + rtnl_scope2str(res->fr_scope, buf, sizeof(buf)), + nl_strerror_l(-res->fr_error), res->fr_error); +} + +static void result_dump_details(struct nl_object *obj, struct nl_dump_params *p) +{ + result_dump_line(obj, p); +} + +static uint64_t result_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + return 0; +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct flnl_result *flnl_result_alloc(void) +{ + return (struct flnl_result *) nl_object_alloc(&result_obj_ops); +} + +void flnl_result_put(struct flnl_result *res) +{ + nl_object_put((struct nl_object *) res); +} + +/** @} */ + +/** + * @name Cache Management + * @{ + */ + +/** + * Allocate lookup result cache. + * + * Allocates a new lookup result cache and initializes it properly. + * + * @note Free the memory after usage using nl_cache_destroy_and_free(). + * @return Newly allocated cache or NULL if an error occured. + */ +struct nl_cache *flnl_result_alloc_cache(void) +{ + return nl_cache_alloc(&fib_lookup_ops); +} + +/** @} */ + +/** + * @name Lookup + * @{ + */ + +/** + * Builds a netlink request message to do a lookup + * @arg req Requested match. + * @arg flags additional netlink message flags + * @arg result Result pointer + * + * Builds a new netlink message requesting a change of link attributes. + * The netlink message header isn't fully equipped with all relevant + * fields and must be sent out via nl_send_auto_complete() or + * supplemented as needed. + * \a old must point to a link currently configured in the kernel + * and \a tmpl must contain the attributes to be changed set via + * \c rtnl_link_set_* functions. + * + * @return 0 on success or a negative error code. + */ +int flnl_lookup_build_request(struct flnl_request *req, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + struct nl_addr *addr; + uint64_t fwmark; + int tos, scope, table; + struct fib_result_nl fr = {0}; + + fwmark = flnl_request_get_fwmark(req); + tos = flnl_request_get_tos(req); + scope = flnl_request_get_scope(req); + table = flnl_request_get_table(req); + + fr.fl_fwmark = fwmark != UINT_LEAST64_MAX ? fwmark : 0; + fr.fl_tos = tos >= 0 ? tos : 0; + fr.fl_scope = scope >= 0 ? scope : RT_SCOPE_UNIVERSE; + fr.tb_id_in = table >= 0 ? (unsigned)table : (unsigned)RT_TABLE_UNSPEC; + + addr = flnl_request_get_addr(req); + if (!addr) + return -NLE_MISSING_ATTR; + + fr.fl_addr = *(uint32_t *) nl_addr_get_binary_addr(addr); + + msg = nlmsg_alloc_simple(0, flags); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &fr, sizeof(fr), NLMSG_ALIGNTO) < 0) + goto errout; + + *result = msg; + return 0; + +errout: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +/** + * Perform FIB Lookup + * @arg sk Netlink socket. + * @arg req Lookup request object. + * @arg cache Cache for result. + * + * Builds a netlink message to request a FIB lookup, waits for the + * reply and adds the result to the specified cache. + * + * @return 0 on success or a negative error code. + */ +int flnl_lookup(struct nl_sock *sk, struct flnl_request *req, + struct nl_cache *cache) +{ + struct nl_msg *msg; + int err; + + if ((err = flnl_lookup_build_request(req, 0, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return nl_cache_pickup_checkdup(sk, cache); +} + +/** @} */ + +/** + * @name Attribute Access + * @{ + */ + +int flnl_result_get_table_id(struct flnl_result *res) +{ + return res->fr_table_id; +} + +int flnl_result_get_prefixlen(struct flnl_result *res) +{ + return res->fr_prefixlen; +} + +int flnl_result_get_nexthop_sel(struct flnl_result *res) +{ + return res->fr_nh_sel; +} + +int flnl_result_get_type(struct flnl_result *res) +{ + return res->fr_type; +} + +int flnl_result_get_scope(struct flnl_result *res) +{ + return res->fr_scope; +} + +int flnl_result_get_error(struct flnl_result *res) +{ + return res->fr_error; +} + +/** @} */ + +static struct nl_object_ops result_obj_ops = { + .oo_name = "fib_lookup/result", + .oo_size = sizeof(struct flnl_result), + .oo_free_data = result_free_data, + .oo_clone = result_clone, + .oo_dump = { + [NL_DUMP_LINE] = result_dump_line, + [NL_DUMP_DETAILS] = result_dump_details, + }, + .oo_compare = result_compare, +}; + +static struct nl_cache_ops fib_lookup_ops = { + .co_name = "fib_lookup/fib_lookup", + .co_hdrsize = sizeof(struct fib_result_nl), + .co_msgtypes = { + { 0, NL_ACT_UNSPEC, "any" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_FIB_LOOKUP, + .co_msg_parser = result_msg_parser, + .co_obj_ops = &result_obj_ops, +}; + +static void _nl_init fib_lookup_init(void) +{ + nl_cache_mngt_register(&fib_lookup_ops); +} + +static void _nl_exit fib_lookup_exit(void) +{ + nl_cache_mngt_unregister(&fib_lookup_ops); +} + +/** @} */ diff --git a/libnl/lib/fib_lookup/request.c b/libnl/lib/fib_lookup/request.c new file mode 100644 index 0000000..81b7ba2 --- /dev/null +++ b/libnl/lib/fib_lookup/request.c @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +/** + * @ingroup fib_lookup + * @defgroup flreq Request + * @brief + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include + +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/object-api.h" + +static struct nl_object_ops request_obj_ops; + +/** @cond SKIP */ +struct flnl_request +{ + NLHDR_COMMON + + struct nl_addr * lr_addr; + uint32_t lr_fwmark; + uint8_t lr_tos; + uint8_t lr_scope; + uint8_t lr_table; +}; + +#define REQUEST_ATTR_ADDR 0x01 +#define REQUEST_ATTR_FWMARK 0x02 +#define REQUEST_ATTR_TOS 0x04 +#define REQUEST_ATTR_SCOPE 0x08 +#define REQUEST_ATTR_TABLE 0x10 +/** @endcond */ + +static void request_free_data(struct nl_object *obj) +{ + struct flnl_request *req = REQUEST_CAST(obj); + + if (req) + nl_addr_put(req->lr_addr); +} + +static int request_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct flnl_request *dst = nl_object_priv(_dst); + struct flnl_request *src = nl_object_priv(_src); + + dst->lr_addr = NULL; + + if (src->lr_addr) { + if (!(dst->lr_addr = nl_addr_clone(src->lr_addr))) + return -NLE_NOMEM; + } + + return 0; +} + +static uint64_t request_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct flnl_request *a = (struct flnl_request *) _a; + struct flnl_request *b = (struct flnl_request *) _b; + uint64_t diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(REQUEST_ATTR_FWMARK, a->lr_fwmark != b->lr_fwmark); + diff |= _DIFF(REQUEST_ATTR_TOS, a->lr_tos != b->lr_tos); + diff |= _DIFF(REQUEST_ATTR_SCOPE, a->lr_scope != b->lr_scope); + diff |= _DIFF(REQUEST_ATTR_TABLE, a->lr_table != b->lr_table); + diff |= _DIFF(REQUEST_ATTR_ADDR, nl_addr_cmp(a->lr_addr, b->lr_addr)); +#undef _DIFF + + return diff; +} + + +/** + * @name Lookup Request Creation/Deletion + * @{ + */ + +struct flnl_request *flnl_request_alloc(void) +{ + return REQUEST_CAST(nl_object_alloc(&request_obj_ops)); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void flnl_request_set_fwmark(struct flnl_request *req, uint64_t fwmark) +{ + req->lr_fwmark = fwmark; + req->ce_mask |= REQUEST_ATTR_FWMARK; +} + +uint64_t flnl_request_get_fwmark(struct flnl_request *req) +{ + if (req->ce_mask & REQUEST_ATTR_FWMARK) + return req->lr_fwmark; + else + return UINT_LEAST64_MAX; +} + +void flnl_request_set_tos(struct flnl_request *req, int tos) +{ + req->lr_tos = tos; + req->ce_mask |= REQUEST_ATTR_TOS; +} + +int flnl_request_get_tos(struct flnl_request *req) +{ + if (req->ce_mask & REQUEST_ATTR_TOS) + return req->lr_tos; + else + return -1; +} + +void flnl_request_set_scope(struct flnl_request *req, int scope) +{ + req->lr_scope = scope; + req->ce_mask |= REQUEST_ATTR_SCOPE; +} + +int flnl_request_get_scope(struct flnl_request *req) +{ + if (req->ce_mask & REQUEST_ATTR_SCOPE) + return req->lr_scope; + else + return -1; +} + +void flnl_request_set_table(struct flnl_request *req, int table) +{ + req->lr_table = table; + req->ce_mask |= REQUEST_ATTR_TABLE; +} + +int flnl_request_get_table(struct flnl_request *req) +{ + if (req->ce_mask & REQUEST_ATTR_TABLE) + return req->lr_table; + else + return -1; +} + +int flnl_request_set_addr(struct flnl_request *req, struct nl_addr *addr) +{ + if (addr->a_family != AF_INET) + return -NLE_AF_NOSUPPORT; + + if (req->lr_addr) + nl_addr_put(req->lr_addr); + + nl_addr_get(addr); + req->lr_addr = addr; + + req->ce_mask |= REQUEST_ATTR_ADDR; + + return 0; +} + +struct nl_addr *flnl_request_get_addr(struct flnl_request *req) +{ + if (req->ce_mask & REQUEST_ATTR_ADDR) + return req->lr_addr; + else + return NULL; +} + +/** @} */ + +static struct nl_object_ops request_obj_ops = { + .oo_name = "fib_lookup/request", + .oo_size = sizeof(struct flnl_request), + .oo_free_data = request_free_data, + .oo_clone = request_clone, + .oo_compare = request_compare, + .oo_id_attrs = ~0, +}; + +/** @} */ diff --git a/libnl/lib/genl/ctrl.c b/libnl/lib/genl/ctrl.c new file mode 100644 index 0000000..1747076 --- /dev/null +++ b/libnl/lib/genl/ctrl.c @@ -0,0 +1,552 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup genl + * @defgroup genl_ctrl Controller (Resolver) + * + * Resolves Generic Netlink family names to numeric identifiers. + * + * The controller is a component in the kernel that resolves Generic Netlink + * family names to their numeric identifiers. This module provides functions + * to query the controller to access the resolving functionality. + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include +#include + +#include "nl-genl.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/object-api.h" + +/** @cond SKIP */ +#define CTRL_VERSION 0x0001 + +static struct nl_cache_ops genl_ctrl_ops; + +static int ctrl_request_update(struct nl_cache *c, struct nl_sock *h) +{ + return genl_send_simple(h, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, + CTRL_VERSION, NLM_F_DUMP); +} + +static struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = { + [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, + [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_STRING, + .maxlen = GENL_NAMSIZ }, + [CTRL_ATTR_VERSION] = { .type = NLA_U32 }, + [CTRL_ATTR_HDRSIZE] = { .type = NLA_U32 }, + [CTRL_ATTR_MAXATTR] = { .type = NLA_U32 }, + [CTRL_ATTR_OPS] = { .type = NLA_NESTED }, + [CTRL_ATTR_MCAST_GROUPS] = { .type = NLA_NESTED }, +}; + +static struct nla_policy family_op_policy[CTRL_ATTR_OP_MAX+1] = { + [CTRL_ATTR_OP_ID] = { .type = NLA_U32 }, + [CTRL_ATTR_OP_FLAGS] = { .type = NLA_U32 }, +}; + +static struct nla_policy family_grp_policy[CTRL_ATTR_MCAST_GRP_MAX+1] = { + [CTRL_ATTR_MCAST_GRP_NAME] = { .type = NLA_STRING }, + [CTRL_ATTR_MCAST_GRP_ID] = { .type = NLA_U32 }, +}; + +static int parse_mcast_grps(struct genl_family *family, struct nlattr *grp_attr) +{ + struct nlattr *nla; + int remaining, err; + + if (!grp_attr) + BUG(); + + nla_for_each_nested(nla, grp_attr, remaining) { + struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX+1]; + int id; + const char * name; + + err = nla_parse_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, nla, + family_grp_policy); + if (err < 0) + goto errout; + + if (tb[CTRL_ATTR_MCAST_GRP_ID] == NULL) { + err = -NLE_MISSING_ATTR; + goto errout; + } + id = nla_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]); + + if (tb[CTRL_ATTR_MCAST_GRP_NAME] == NULL) { + err = -NLE_MISSING_ATTR; + goto errout; + } + name = nla_get_string(tb[CTRL_ATTR_MCAST_GRP_NAME]); + + err = genl_family_add_grp(family, id, name); + if (err < 0) + goto errout; + } + + err = 0; + +errout: + return err; +} + +static int ctrl_msg_parser(struct nl_cache_ops *ops, struct genl_cmd *cmd, + struct genl_info *info, void *arg) +{ + struct genl_family *family; + struct nl_parser_param *pp = arg; + int err; + + family = genl_family_alloc(); + if (family == NULL) { + err = -NLE_NOMEM; + goto errout; + } + + if (info->attrs[CTRL_ATTR_FAMILY_NAME] == NULL) { + err = -NLE_MISSING_ATTR; + goto errout; + } + + if (info->attrs[CTRL_ATTR_FAMILY_ID] == NULL) { + err = -NLE_MISSING_ATTR; + goto errout; + } + + family->ce_msgtype = info->nlh->nlmsg_type; + genl_family_set_id(family, + nla_get_u16(info->attrs[CTRL_ATTR_FAMILY_ID])); + genl_family_set_name(family, + nla_get_string(info->attrs[CTRL_ATTR_FAMILY_NAME])); + + if (info->attrs[CTRL_ATTR_VERSION]) { + uint32_t version = nla_get_u32(info->attrs[CTRL_ATTR_VERSION]); + genl_family_set_version(family, version); + } + + if (info->attrs[CTRL_ATTR_HDRSIZE]) { + uint32_t hdrsize = nla_get_u32(info->attrs[CTRL_ATTR_HDRSIZE]); + genl_family_set_hdrsize(family, hdrsize); + } + + if (info->attrs[CTRL_ATTR_MAXATTR]) { + uint32_t maxattr = nla_get_u32(info->attrs[CTRL_ATTR_MAXATTR]); + genl_family_set_maxattr(family, maxattr); + } + + if (info->attrs[CTRL_ATTR_OPS]) { + struct nlattr *nla, *nla_ops; + int remaining; + + nla_ops = info->attrs[CTRL_ATTR_OPS]; + nla_for_each_nested(nla, nla_ops, remaining) { + struct nlattr *tb[CTRL_ATTR_OP_MAX+1]; + int flags = 0, id; + + err = nla_parse_nested(tb, CTRL_ATTR_OP_MAX, nla, + family_op_policy); + if (err < 0) + goto errout; + + if (tb[CTRL_ATTR_OP_ID] == NULL) { + err = -NLE_MISSING_ATTR; + goto errout; + } + + id = nla_get_u32(tb[CTRL_ATTR_OP_ID]); + + if (tb[CTRL_ATTR_OP_FLAGS]) + flags = nla_get_u32(tb[CTRL_ATTR_OP_FLAGS]); + + err = genl_family_add_op(family, id, flags); + if (err < 0) + goto errout; + + } + } + + if (info->attrs[CTRL_ATTR_MCAST_GROUPS]) { + err = parse_mcast_grps(family, info->attrs[CTRL_ATTR_MCAST_GROUPS]); + if (err < 0) + goto errout; + } + + err = pp->pp_cb((struct nl_object *) family, pp); +errout: + genl_family_put(family); + return err; +} + +/** + * process responses from from the query sent by genl_ctrl_probe_by_name + * @arg nl_msg Returned message. + * @arg name genl_family structure to fill out. + * + * Process returned messages, filling out the missing informatino in the + * genl_family structure + * + * @return Indicator to keep processing frames or not + * + */ +static int probe_response(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[CTRL_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct genl_family *ret = (struct genl_family *)arg; + + if (genlmsg_parse(nlh, 0, tb, CTRL_ATTR_MAX, ctrl_policy)) + return NL_SKIP; + + if (tb[CTRL_ATTR_FAMILY_ID]) + genl_family_set_id(ret, nla_get_u16(tb[CTRL_ATTR_FAMILY_ID])); + + if (tb[CTRL_ATTR_MCAST_GROUPS]) + if (parse_mcast_grps(ret, tb[CTRL_ATTR_MCAST_GROUPS]) < 0) + return NL_SKIP; + + return NL_STOP; +} + +/** + * Look up generic netlink family by family name querying the kernel directly + * @arg sk Socket. + * @arg name Family name. + * + * Directly query's the kernel for a given family name. The caller will own a + * reference on the returned object which needsd to be given back after usage + * using genl_family_put. + * + * Note: This API call differs from genl_ctrl_search_by_name in that it querys + * the kernel directly, alowing for module autoload to take place to resolve the + * family request. Using an nl_cache prevents that operation + * + * @return Generic netlink family object or NULL if no match was found. + */ +static struct genl_family *genl_ctrl_probe_by_name(struct nl_sock *sk, + const char *name) +{ + struct nl_msg *msg; + struct genl_family *ret; + struct nl_cb *cb, *orig; + int rc; + + ret = genl_family_alloc(); + if (!ret) + goto out; + + genl_family_set_name(ret, name); + + msg = nlmsg_alloc(); + if (!msg) + goto out_fam_free; + + if (!(orig = nl_socket_get_cb(sk))) + goto out_msg_free; + + cb = nl_cb_clone(orig); + nl_cb_put(orig); + if (!cb) + goto out_msg_free; + + if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, + 0, 0, CTRL_CMD_GETFAMILY, 1)) { + BUG(); + goto out_cb_free; + } + + if (nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, name) < 0) + goto out_cb_free; + + rc = nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, probe_response, + (void *) ret); + if (rc < 0) + goto out_cb_free; + + rc = nl_send_auto_complete(sk, msg); + if (rc < 0) + goto out_cb_free; + + rc = nl_recvmsgs(sk, cb); + if (rc < 0) + goto out_cb_free; + + /* If search was successful, request may be ACKed after data */ + rc = wait_for_ack(sk); + if (rc < 0) + goto out_cb_free; + + if (genl_family_get_id(ret) != 0) { + nlmsg_free(msg); + nl_cb_put(cb); + return ret; + } + +out_cb_free: + nl_cb_put(cb); +out_msg_free: + nlmsg_free(msg); +out_fam_free: + genl_family_put(ret); + ret = NULL; +out: + return ret; +} + + +/** @endcond */ + +/** + * @name Controller Cache + * + * The controller cache allows to keep a local copy of the list of all + * kernel side registered Generic Netlink families to quickly resolve + * multiple Generic Netlink family names without requiring to communicate + * with the kernel for each resolving iteration. + * + * @{ + */ + +/** + * Allocate a new controller cache + * @arg sk Generic Netlink socket + * @arg result Pointer to store resulting cache + * + * Allocates a new cache mirroring the state of the controller and stores it + * in \c *result. The allocated cache will contain a list of all currently + * registered kernel side Generic Netlink families. The cache is meant to be + * used to resolve family names locally. + * + * @return 0 on success or a negative error code. + */ +int genl_ctrl_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&genl_ctrl_ops, sk, result); +} + +/** + * Search controller cache for a numeric address match + * @arg cache Controller cache + * @arg id Numeric family identifier. + * + * Searches a previously allocated controller cache and looks for an entry + * that matches the specified numeric family identifier \c id. If a match + * is found successfully, the reference count of the matching object is + * increased by one before the objet is returned. + * + * @see genl_ctrl_alloc_cache() + * @see genl_ctrl_search_by_name() + * @see genl_family_put() + * + * @return Generic Netlink family object or NULL if no match was found. + */ +struct genl_family *genl_ctrl_search(struct nl_cache *cache, int id) +{ + struct genl_family *fam; + + if (cache->c_ops != &genl_ctrl_ops) + BUG(); + + nl_list_for_each_entry(fam, &cache->c_items, ce_list) { + if (fam->gf_id == id) { + nl_object_get((struct nl_object *) fam); + return fam; + } + } + + return NULL; +} + +/** + * Search controller cache for a family name match + * @arg cache Controller cache + * @arg name Name of Generic Netlink family + * + * Searches a previously allocated controller cache and looks for an entry + * that matches the specified family \c name. If a match is found successfully, + * the reference count of the matching object is increased by one before the + * objet is returned. + * + * @see genl_ctrl_alloc_cache() + * @see genl_ctrl_search() + * @see genl_family_put() + * + * @return Generic Netlink family object or NULL if no match was found. + */ +struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, + const char *name) +{ + struct genl_family *fam; + + if (cache->c_ops != &genl_ctrl_ops) + BUG(); + + nl_list_for_each_entry(fam, &cache->c_items, ce_list) { + if (!strcmp(name, fam->gf_name)) { + nl_object_get((struct nl_object *) fam); + return fam; + } + } + + return NULL; +} + +/** @} */ + +/** + * @name Direct Resolvers + * + * These functions communicate directly with the kernel and do not require + * a cache to be kept up to date. + * + * @{ + */ + +/** + * Resolve Generic Netlink family name to numeric identifier + * @arg sk Generic Netlink socket. + * @arg name Name of Generic Netlink family + * + * Resolves the Generic Netlink family name to the corresponding numeric + * family identifier. This function queries the kernel directly, use + * genl_ctrl_search_by_name() if you need to resolve multiple names. + * + * @see genl_ctrl_search_by_name() + * + * @return The numeric family identifier or a negative error code. + */ +int genl_ctrl_resolve(struct nl_sock *sk, const char *name) +{ + struct genl_family *family; + int err; + + family = genl_ctrl_probe_by_name(sk, name); + if (family == NULL) { + err = -NLE_OBJ_NOTFOUND; + goto errout; + } + + err = genl_family_get_id(family); + genl_family_put(family); +errout: + return err; +} + +static int genl_ctrl_grp_by_name(const struct genl_family *family, + const char *grp_name) +{ + struct genl_family_grp *grp; + + nl_list_for_each_entry(grp, &family->gf_mc_grps, list) { + if (!strcmp(grp->name, grp_name)) { + return grp->id; + } + } + + return -NLE_OBJ_NOTFOUND; +} + +/** + * Resolve Generic Netlink family group name + * @arg sk Generic Netlink socket + * @arg family_name Name of Generic Netlink family + * @arg grp_name Name of group to resolve + * + * Looks up the family object and resolves the group name to the numeric + * group identifier. + * + * @return Numeric group identifier or a negative error code. + */ +int genl_ctrl_resolve_grp(struct nl_sock *sk, const char *family_name, + const char *grp_name) +{ + + struct genl_family *family; + int err; + + family = genl_ctrl_probe_by_name(sk, family_name); + if (family == NULL) { + err = -NLE_OBJ_NOTFOUND; + goto errout; + } + + err = genl_ctrl_grp_by_name(family, grp_name); + genl_family_put(family); +errout: + return err; +} + +/** @} */ + +/** @cond SKIP */ +static struct genl_cmd genl_cmds[] = { + { + .c_id = CTRL_CMD_NEWFAMILY, + .c_name = "NEWFAMILY" , + .c_maxattr = CTRL_ATTR_MAX, + .c_attr_policy = ctrl_policy, + .c_msg_parser = ctrl_msg_parser, + }, + { + .c_id = CTRL_CMD_DELFAMILY, + .c_name = "DELFAMILY" , + }, + { + .c_id = CTRL_CMD_GETFAMILY, + .c_name = "GETFAMILY" , + }, + { + .c_id = CTRL_CMD_NEWOPS, + .c_name = "NEWOPS" , + }, + { + .c_id = CTRL_CMD_DELOPS, + .c_name = "DELOPS" , + }, +}; + +static struct genl_ops genl_ops = { + .o_cmds = genl_cmds, + .o_ncmds = ARRAY_SIZE(genl_cmds), +}; + +extern struct nl_object_ops genl_family_ops; + +#define GENL_FAMILY(id, name) \ + { \ + { id, NL_ACT_UNSPEC, name }, \ + END_OF_MSGTYPES_LIST, \ + } + +static struct nl_cache_ops genl_ctrl_ops = { + .co_name = "genl/family", + .co_hdrsize = GENL_HDRSIZE(0), + .co_msgtypes = GENL_FAMILY(GENL_ID_CTRL, "nlctrl"), + .co_genl = &genl_ops, + .co_protocol = NETLINK_GENERIC, + .co_request_update = ctrl_request_update, + .co_obj_ops = &genl_family_ops, +}; + +static void _nl_init ctrl_init(void) +{ + genl_register(&genl_ctrl_ops); +} + +static void _nl_exit ctrl_exit(void) +{ + genl_unregister(&genl_ctrl_ops); +} +/** @endcond */ + +/** @} */ diff --git a/libnl/lib/genl/family.c b/libnl/lib/genl/family.c new file mode 100644 index 0000000..1749472 --- /dev/null +++ b/libnl/lib/genl/family.c @@ -0,0 +1,414 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup genl_ctrl + * @defgroup genl_family Generic Netlink Family Object + * + * Object representing a kernel side registered Generic Netlink family + * + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include + +#include "nl-genl.h" +#include "nl-priv-dynamic-core/object-api.h" +#include "nl-priv-dynamic-core/nl-core.h" + +/** @cond SKIP */ +struct genl_family_op +{ + uint32_t o_id; + uint32_t o_flags; + + struct nl_list_head o_list; +}; + +#define FAMILY_ATTR_ID 0x01 +#define FAMILY_ATTR_NAME 0x02 +#define FAMILY_ATTR_VERSION 0x04 +#define FAMILY_ATTR_HDRSIZE 0x08 +#define FAMILY_ATTR_MAXATTR 0x10 +#define FAMILY_ATTR_OPS 0x20 + +struct nl_object_ops genl_family_ops; + +static void family_constructor(struct nl_object *c) +{ + struct genl_family *family = (struct genl_family *) c; + + nl_init_list_head(&family->gf_ops); + nl_init_list_head(&family->gf_mc_grps); +} + +static void family_free_data(struct nl_object *c) +{ + struct genl_family *family = (struct genl_family *) c; + struct genl_family_op *ops, *tmp; + struct genl_family_grp *grp, *t_grp; + + if (family == NULL) + return; + + nl_list_for_each_entry_safe(ops, tmp, &family->gf_ops, o_list) { + nl_list_del(&ops->o_list); + free(ops); + } + + nl_list_for_each_entry_safe(grp, t_grp, &family->gf_mc_grps, list) { + nl_list_del(&grp->list); + free(grp); + } + +} + +static int family_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct genl_family *dst = nl_object_priv(_dst); + struct genl_family *src = nl_object_priv(_src); + struct genl_family_op *ops; + struct genl_family_grp *grp; + int err; + + nl_init_list_head(&dst->gf_ops); + nl_init_list_head(&dst->gf_mc_grps); + + nl_list_for_each_entry(ops, &src->gf_ops, o_list) { + err = genl_family_add_op(dst, ops->o_id, ops->o_flags); + if (err < 0) + return err; + } + + nl_list_for_each_entry(grp, &src->gf_mc_grps, list) { + err = genl_family_add_grp(dst, grp->id, grp->name); + if (err < 0) + return err; + } + + + return 0; +} + +static void family_dump_line(struct nl_object *obj, struct nl_dump_params *p) +{ + struct genl_family *family = (struct genl_family *) obj; + + nl_dump(p, "0x%04x %s version %u\n", + family->gf_id, family->gf_name, family->gf_version); +} + +static const struct trans_tbl ops_flags[] = { + __ADD(GENL_ADMIN_PERM, admin_perm), + __ADD(GENL_CMD_CAP_DO, has_doit), + __ADD(GENL_CMD_CAP_DUMP, has_dump), + __ADD(GENL_CMD_CAP_HASPOL, has_policy), +}; + +static char *ops_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, ops_flags, ARRAY_SIZE(ops_flags)); +} + +static void family_dump_details(struct nl_object *obj, struct nl_dump_params *p) +{ + struct genl_family_grp *grp; + struct genl_family *family = (struct genl_family *) obj; + + family_dump_line(obj, p); + nl_dump_line(p, " hdrsize %u maxattr %u\n", + family->gf_hdrsize, family->gf_maxattr); + + if (family->ce_mask & FAMILY_ATTR_OPS) { + struct genl_family_op *op; + char buf[64]; + + nl_list_for_each_entry(op, &family->gf_ops, o_list) { + ops_flags2str(op->o_flags, buf, sizeof(buf)); + + genl_op2name(family->gf_id, op->o_id, buf, sizeof(buf)); + + nl_dump_line(p, " op %s (0x%02x)", buf, op->o_id); + + if (op->o_flags) + nl_dump(p, " <%s>", + ops_flags2str(op->o_flags, buf, + sizeof(buf))); + + nl_dump(p, "\n"); + } + } + + nl_list_for_each_entry(grp, &family->gf_mc_grps, list) { + nl_dump_line(p, " grp %s (0x%02x)\n", grp->name, grp->id); + } + +} + +static void family_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + family_dump_details(obj, p); +} + +static uint64_t family_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct genl_family *a = (struct genl_family *) _a; + struct genl_family *b = (struct genl_family *) _b; + uint64_t diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(FAMILY_ATTR_ID, a->gf_id != b->gf_id); + diff |= _DIFF(FAMILY_ATTR_VERSION, a->gf_version != b->gf_version); + diff |= _DIFF(FAMILY_ATTR_HDRSIZE, a->gf_hdrsize != b->gf_hdrsize); + diff |= _DIFF(FAMILY_ATTR_MAXATTR, a->gf_maxattr != b->gf_maxattr); + diff |= _DIFF(FAMILY_ATTR_NAME, strcmp(a->gf_name, b->gf_name)); +#undef _DIFF + + return diff; +} +/** @endcond */ + +/** + * @name Object Allocation + * @{ + */ + +/** + * Allocate new Generic Netlink family object + * + * @return Newly allocated Generic Netlink family object or NULL. + */ +struct genl_family *genl_family_alloc(void) +{ + return (struct genl_family *) nl_object_alloc(&genl_family_ops); +} + +/** + * Release reference on Generic Netlink family object + * @arg family Generic Netlink family object + * + * Reduces the reference counter of a Generic Netlink family object by one. + * The object is freed after the last user has returned its reference. + * + * @see nl_object_put() + */ +void genl_family_put(struct genl_family *family) +{ + nl_object_put((struct nl_object *) family); +} + +/** @} */ + +/** + * @name Numeric Identifier + * @{ + */ + +/** + * Return numeric identifier + * @arg family Generic Netlink family object + * + * @return Numeric identifier or 0 if not available. + */ +unsigned int genl_family_get_id(struct genl_family *family) +{ + if (family->ce_mask & FAMILY_ATTR_ID) + return family->gf_id; + else + return 0; +} + +/** + * Set the numeric identifier + * @arg family Generic Netlink family object + * @arg id New numeric identifier + */ +void genl_family_set_id(struct genl_family *family, unsigned int id) +{ + family->gf_id = id; + family->ce_mask |= FAMILY_ATTR_ID; +} + +/** @} */ + +/** + * @name Human Readable Name + * @{ + */ + +/** + * Return human readable name + * @arg family Generic Netlink family object + * + * @return Name of family or NULL if not available + */ +char *genl_family_get_name(struct genl_family *family) +{ + if (family->ce_mask & FAMILY_ATTR_NAME) + return family->gf_name; + else + return NULL; +} + +/** + * Set human readable name + * @arg family Generic Netlink family object + * @arg name New human readable name + */ +void genl_family_set_name(struct genl_family *family, const char *name) +{ + _nl_strncpy_trunc(family->gf_name, name, GENL_NAMSIZ); + family->ce_mask |= FAMILY_ATTR_NAME; +} + +/** + * @name Interface Version + * @{ + */ + +/** + * Return interface version + * @arg family Generic Netlink family object + * + * @return Interface version or 0 if not available. + */ +uint8_t genl_family_get_version(struct genl_family *family) +{ + if (family->ce_mask & FAMILY_ATTR_VERSION) + return family->gf_version; + else + return 0; +} + +/** + * Set interface version + * @arg family Generic Netlink family object + * @arg version New interface version + */ +void genl_family_set_version(struct genl_family *family, uint8_t version) +{ + family->gf_version = version; + family->ce_mask |= FAMILY_ATTR_VERSION; +} + +/** @} */ + +/** + * @name Header Size + * @{ + */ + +/** + * Return user header size expected by kernel component + * @arg family Generic Netlink family object + * + * @return Expected header length or 0 if not available. + */ +uint32_t genl_family_get_hdrsize(struct genl_family *family) +{ + if (family->ce_mask & FAMILY_ATTR_HDRSIZE) + return family->gf_hdrsize; + else + return 0; +} + +void genl_family_set_hdrsize(struct genl_family *family, uint32_t hdrsize) +{ + family->gf_hdrsize = hdrsize; + family->ce_mask |= FAMILY_ATTR_HDRSIZE; +} + +/** @} */ + +/** + * @name Maximum Expected Attribute + * @{ + */ + +uint32_t genl_family_get_maxattr(struct genl_family *family) +{ + if (family->ce_mask & FAMILY_ATTR_MAXATTR) + return family->gf_maxattr; + else + return 0; +} + +void genl_family_set_maxattr(struct genl_family *family, uint32_t maxattr) +{ + family->gf_maxattr = maxattr; + family->ce_mask |= FAMILY_ATTR_MAXATTR; +} + +/** @} */ + +/** + * @name Operations + * @{ + */ + +int genl_family_add_op(struct genl_family *family, int id, int flags) +{ + struct genl_family_op *op; + + op = calloc(1, sizeof(*op)); + if (op == NULL) + return -NLE_NOMEM; + + op->o_id = id; + op->o_flags = flags; + + nl_list_add_tail(&op->o_list, &family->gf_ops); + family->ce_mask |= FAMILY_ATTR_OPS; + + return 0; +} + +int genl_family_add_grp(struct genl_family *family, uint32_t id, + const char *name) +{ + struct genl_family_grp *grp; + + if ( !name + || strlen (name) >= GENL_NAMSIZ) + return -NLE_INVAL; + + grp = calloc(1, sizeof(*grp)); + if (grp == NULL) + return -NLE_NOMEM; + + grp->id = id; + _nl_strncpy_assert(grp->name, name, GENL_NAMSIZ); + + nl_list_add_tail(&grp->list, &family->gf_mc_grps); + + return 0; +} + +/** @} */ + +/** @cond SKIP */ +struct nl_object_ops genl_family_ops = { + .oo_name = "genl/family", + .oo_size = sizeof(struct genl_family), + .oo_constructor = family_constructor, + .oo_free_data = family_free_data, + .oo_clone = family_clone, + .oo_dump = { + [NL_DUMP_LINE] = family_dump_line, + [NL_DUMP_DETAILS] = family_dump_details, + [NL_DUMP_STATS] = family_dump_stats, + }, + .oo_compare = family_compare, + .oo_id_attrs = FAMILY_ATTR_ID, +}; +/** @endcond */ + +/** @} */ diff --git a/libnl/lib/genl/genl.c b/libnl/lib/genl/genl.c new file mode 100644 index 0000000..41add3a --- /dev/null +++ b/libnl/lib/genl/genl.c @@ -0,0 +1,392 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @defgroup genl Generic Netlink Library (libnl-genl) + * + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include + +#include "nl-genl.h" +#include "nl-aux-core/nl-core.h" + +/** + * @name Generic Netlink Socket + * @{ + */ + +/** + * Connect a Generic Netlink socket + * @arg sk Unconnected Netlink socket + * + * This function expects a struct nl_socket object previously allocated via + * nl_socket_alloc(). It calls nl_connect() to create the local socket file + * descriptor and binds the socket to the \c NETLINK_GENERIC Netlink protocol. + * + * Using this function is equivalent to: + * @code + * nl_connect(sk, NETLINK_GENERIC); + * @endcode + * + * @see nl_connect() + * + * @return 0 on success or a negative error code. + */ +int genl_connect(struct nl_sock *sk) +{ + return nl_connect(sk, NETLINK_GENERIC); +} + +/** @} */ + +/** + * @name Sending Data + * @{ + */ + +/** + * Send a Generic Netlink message consisting only of a header + * @arg sk Generic Netlink socket + * @arg family Numeric family identifier + * @arg cmd Numeric command identifier + * @arg version Interface version + * @arg flags Additional Netlink message flags (optional) + * + * This function is a shortcut for sending a Generic Netlink message without + * any message payload. The message will only consist of the Netlink and + * Generic Netlink headers. The header is constructed based on the specified + * parameters and passed on to nl_send_simple() to send it on the specified + * socket. + * + * @par Example: + * @code + * #include + * #include + * + * err = genl_send_simple(sk, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, CTRL_VERSION, + * NLM_F_DUMP); + * @endcode + * + * @see nl_send_simple() + * + * @return 0 on success or a negative error code. Due to a bug, this function + * returns the number of bytes sent. Treat any non-negative number as success. + */ +int genl_send_simple(struct nl_sock *sk, int family, int cmd, + int version, int flags) +{ + struct genlmsghdr hdr = { + .cmd = cmd, + .version = version, + }; + + return nl_send_simple(sk, family, flags, &hdr, sizeof(hdr)); +} + +/** @} */ + +/** + * @name Message Parsing + * @{ + */ + +/** + * Validate Generic Netlink message headers + * @arg nlh Pointer to Netlink message header + * @arg hdrlen Length of user header + * + * Verifies the integrity of the Netlink and Generic Netlink headers by + * enforcing the following requirements: + * - Valid Netlink message header (nlmsg_valid_hdr()) + * - Presence of a complete Generic Netlink header + * - At least \c hdrlen bytes of payload included after the generic + * netlink header. + * + * @return A positive integer (true) if the headers are valid or + * 0 (false) if not. + */ +int genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen) +{ + struct genlmsghdr *ghdr; + int l; + + if (!nlmsg_valid_hdr(nlh, GENL_HDRLEN)) + return 0; + + ghdr = nlmsg_data(nlh); + l = genlmsg_len(ghdr); + if (l < 0 || ((unsigned)l) < NLMSG_ALIGN(hdrlen)) + return 0; + + return 1; +} + +/** + * Validate Generic Netlink message including attributes + * @arg nlh Pointer to Netlink message header + * @arg hdrlen Length of user header + * @arg maxtype Maximum attribtue id expected + * @arg policy Attribute validation policy + * + * Verifies the validity of the Netlink and Generic Netlink headers using + * genlmsg_valid_hdr() and calls nla_validate() on the message payload to + * verify the integrity of eventual attributes. + * + * @note You may call genlmsg_parse() directly to perform validation and + * parsing in a single step. + * + * @see genlmsg_valid_hdr() + * @see nla_validate() + * @see genlmsg_parse() + * + * @return 0 on success or a negative error code. + */ +int genlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype, + const struct nla_policy *policy) +{ + struct genlmsghdr *ghdr; + + if (!genlmsg_valid_hdr(nlh, hdrlen)) + return -NLE_MSG_TOOSHORT; + + ghdr = nlmsg_data(nlh); + return nla_validate(genlmsg_attrdata(ghdr, hdrlen), + genlmsg_attrlen(ghdr, hdrlen), maxtype, policy); +} + +/** + * Parse Generic Netlink message including attributes + * @arg nlh Pointer to Netlink message header + * @arg hdrlen Length of user header + * @arg tb Array to store parsed attributes + * @arg maxtype Maximum attribute id expected + * @arg policy Attribute validation policy + * + * Verifies the validity of the Netlink and Generic Netlink headers using + * genlmsg_valid_hdr() and calls nla_parse() on the message payload to + * parse eventual attributes. + * + * @par Example: + * @code + * struct nlattr *attrs[MY_TYPE_MAX+1]; + * + * if ((err = genlmsg_parse(nlmsg_hdr(msg), sizeof(struct my_hdr), attrs, + * MY_TYPE_MAX, attr_policy)) < 0) + * // ERROR + * @endcode + * + * @see genlmsg_valid_hdr() + * @see genlmsg_validate() + * @see nla_parse() + * + * @return 0 on success or a negative error code. + */ +int genlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], + int maxtype, const struct nla_policy *policy) +{ + struct genlmsghdr *ghdr; + + if (!genlmsg_valid_hdr(nlh, hdrlen)) + return -NLE_MSG_TOOSHORT; + + ghdr = nlmsg_data(nlh); + return nla_parse(tb, maxtype, genlmsg_attrdata(ghdr, hdrlen), + genlmsg_attrlen(ghdr, hdrlen), policy); +} + +/** + * Return pointer to Generic Netlink header + * @arg nlh Netlink message header + * + * @return Pointer to Generic Netlink message header + */ +struct genlmsghdr *genlmsg_hdr(struct nlmsghdr *nlh) +{ + return nlmsg_data(nlh); +} + +/** + * Return length of message payload including user header + * @arg gnlh Generic Netlink message header + * + * @see genlmsg_data() + * + * @return Length of user payload including an eventual user header in + * number of bytes. + */ +int genlmsg_len(const struct genlmsghdr *gnlh) +{ + const struct nlmsghdr *nlh; + + nlh = (const struct nlmsghdr *)((const unsigned char *) gnlh - NLMSG_HDRLEN); + return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN); +} + + +/** + * Return pointer to user header + * @arg gnlh Generic Netlink message header + * + * Calculates the pointer to the user header based on the pointer to + * the Generic Netlink message header. + * + * @return Pointer to the user header + */ +void *genlmsg_user_hdr(const struct genlmsghdr *gnlh) +{ + return genlmsg_data(gnlh); +} + +/** + * Return pointer to user data + * @arg gnlh Generic netlink message header + * @arg hdrlen Length of user header + * + * Calculates the pointer to the user data based on the pointer to + * the Generic Netlink message header. + * + * @see genlmsg_user_datalen() + * + * @return Pointer to the user data + */ +void *genlmsg_user_data(const struct genlmsghdr *gnlh, const int hdrlen) +{ + return (char *) genlmsg_user_hdr(gnlh) + NLMSG_ALIGN(hdrlen); +} + +/** + * Return length of user data + * @arg gnlh Generic Netlink message header + * @arg hdrlen Length of user header + * + * @see genlmsg_user_data() + * + * @return Length of user data in bytes + */ +int genlmsg_user_datalen(const struct genlmsghdr *gnlh, const int hdrlen) +{ + return genlmsg_len(gnlh) - NLMSG_ALIGN(hdrlen); +} + +/** + * Return pointer to message attributes + * @arg gnlh Generic Netlink message header + * @arg hdrlen Length of user header + * + * @see genlmsg_attrlen() + * + * @return Pointer to the start of the message's attributes section. + */ +struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen) +{ + return genlmsg_user_data(gnlh, hdrlen); +} + +/** + * Return length of message attributes + * @arg gnlh Generic Netlink message header + * @arg hdrlen Length of user header + * + * @see genlmsg_attrdata() + * + * @return Length of the message section containing attributes in number + * of bytes. + */ +int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen) +{ + return genlmsg_len(gnlh) - NLMSG_ALIGN(hdrlen); +} + +/** @} */ + +/** + * @name Message Construction + * @{ + */ + +/** + * Add Generic Netlink headers to Netlink message + * @arg msg Netlink message object + * @arg port Netlink port or NL_AUTO_PORT + * @arg seq Sequence number of message or NL_AUTO_SEQ + * @arg family Numeric family identifier + * @arg hdrlen Length of user header + * @arg flags Additional Netlink message flags (optional) + * @arg cmd Numeric command identifier + * @arg version Interface version + * + * Calls nlmsg_put() on the specified message object to reserve space for + * the Netlink header, the Generic Netlink header, and a user header of + * specified length. Fills out the header fields with the specified + * parameters. + * + * @par Example: + * @code + * struct nl_msg *msg; + * struct my_hdr *user_hdr; + * + * if (!(msg = nlmsg_alloc())) + * // ERROR + * + * user_hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, family_id, + * sizeof(struct my_hdr), 0, MY_CMD_FOO, 0); + * if (!user_hdr) + * // ERROR + * @endcode + * + * @see nlmsg_put() + * + * Returns Pointer to user header or NULL if an error occurred. + */ +void *genlmsg_put(struct nl_msg *msg, uint32_t port, uint32_t seq, int family, + int hdrlen, int flags, uint8_t cmd, uint8_t version) +{ + struct nlmsghdr *nlh; + struct genlmsghdr hdr = { + .cmd = cmd, + .version = version, + }; + + nlh = nlmsg_put(msg, port, seq, family, GENL_HDRLEN + hdrlen, flags); + if (nlh == NULL) + return NULL; + + memcpy(nlmsg_data(nlh), &hdr, sizeof(hdr)); + NL_DBG(2, "msg %p: Added generic netlink header cmd=%d version=%d\n", + msg, cmd, version); + + return (char *) nlmsg_data(nlh) + GENL_HDRLEN; +} + +/** @} */ + +/** + * @name Deprecated + * @{ + */ + +/** + * Return pointer to message payload + * @arg gnlh Generic Netlink message header + * + * @deprecated This function has been deprecated due to inability to specify + * the length of the user header. Use genlmsg_user_hdr() + * respectively genlmsg_user_data(). + * + * @return Pointer to payload section + */ +void *genlmsg_data(const struct genlmsghdr *gnlh) +{ + return ((unsigned char *) gnlh + GENL_HDRLEN); +} + +/** @} */ +/** @} */ diff --git a/libnl/lib/genl/mngt.c b/libnl/lib/genl/mngt.c new file mode 100644 index 0000000..06a0253 --- /dev/null +++ b/libnl/lib/genl/mngt.c @@ -0,0 +1,401 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup genl + * @defgroup genl_mngt Family and Command Registration + * + * Registering Generic Netlink Families and Commands + * + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include +#include + +#include "nl-genl.h" + +/** @cond SKIP */ + +static NL_LIST_HEAD(genl_ops_list); + +static struct genl_cmd *lookup_cmd(struct genl_ops *ops, int cmd_id) +{ + struct genl_cmd *cmd; + int i; + + for (i = 0; i < ops->o_ncmds; i++) { + cmd = &ops->o_cmds[i]; + if (cmd->c_id == cmd_id) + return cmd; + } + + return NULL; +} + +static int cmd_msg_parser(struct sockaddr_nl *who, struct nlmsghdr *nlh, + struct genl_ops *ops, struct nl_cache_ops *cache_ops, void *arg) +{ + _nl_auto_free struct nlattr **tb_free = NULL; + int err; + struct genlmsghdr *ghdr; + struct genl_cmd *cmd; + struct nlattr **tb; + + ghdr = genlmsg_hdr(nlh); + + if (!(cmd = lookup_cmd(ops, ghdr->cmd))) + return -NLE_MSGTYPE_NOSUPPORT; + + if (cmd->c_msg_parser == NULL) + return -NLE_OPNOTSUPP; + + tb = _nl_malloc_maybe_a (300, (((size_t) cmd->c_maxattr) + 1u) * sizeof (struct nlattr *), &tb_free); + if (!tb) + return -NLE_NOMEM; + + err = nlmsg_parse(nlh, + GENL_HDRSIZE(ops->o_hdrsize), + tb, + cmd->c_maxattr, + cmd->c_attr_policy); + if (err < 0) + return err; + + { + struct genl_info info = { + .who = who, + .nlh = nlh, + .genlhdr = ghdr, + .userhdr = genlmsg_user_hdr(ghdr), + .attrs = tb, + }; + + return cmd->c_msg_parser(cache_ops, cmd, &info, arg); + } +} + +static int genl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + if (ops->co_genl == NULL) + BUG(); + + return cmd_msg_parser(who, nlh, ops->co_genl, ops, pp); +} + +static struct genl_ops *lookup_family(int family) +{ + struct genl_ops *ops; + + nl_list_for_each_entry(ops, &genl_ops_list, o_list) { + if (ops->o_id == family) + return ops; + } + + return NULL; +} + +static struct genl_ops *lookup_family_by_name(const char *name) +{ + struct genl_ops *ops; + + nl_list_for_each_entry(ops, &genl_ops_list, o_list) { + if (!strcmp(ops->o_name, name)) + return ops; + } + + return NULL; +} + +char *genl_op2name(int family, int op, char *buf, size_t len) +{ + struct genl_ops *ops; + int i; + + if ((ops = lookup_family(family))) { + for (i = 0; i < ops->o_ncmds; i++) { + struct genl_cmd *cmd; + cmd = &ops->o_cmds[i]; + + if (cmd->c_id == op) { + _nl_strncpy_trunc(buf, cmd->c_name, len); + return buf; + } + } + } + + _nl_strncpy_trunc(buf, "unknown", len); + return NULL; +} + +/** @endcond */ + +/** + * @name Registration + * @{ + */ + +/** + * Register Generic Netlink family and associated commands + * @arg ops Generic Netlink family definition + * + * Registers the specified Generic Netlink family definition together with + * all associated commands. After registration, received Generic Netlink + * messages can be passed to genl_handle_msg() which will validate the + * messages, look for a matching command and call the respective callback + * function automatically. + * + * @note Consider using genl_register() if the family is used to implement a + * cacheable type. + * + * @see genl_unregister_family(); + * @see genl_register(); + * + * @return 0 on success or a negative error code. + */ +int genl_register_family(struct genl_ops *ops) +{ + if (!ops->o_name) + return -NLE_INVAL; + + if (ops->o_cmds && ops->o_ncmds <= 0) + return -NLE_INVAL; + + if (ops->o_id && lookup_family(ops->o_id)) + return -NLE_EXIST; + + if (lookup_family_by_name(ops->o_name)) + return -NLE_EXIST; + + nl_list_add_tail(&ops->o_list, &genl_ops_list); + + return 0; +} + +/** + * Unregister Generic Netlink family + * @arg ops Generic Netlink family definition + * + * Unregisters a family and all associated commands that were previously + * registered using genl_register_family(). + * + * @see genl_register_family() + * + * @return 0 on success or a negative error code. + */ +int genl_unregister_family(struct genl_ops *ops) +{ + nl_list_del(&ops->o_list); + + return 0; +} + +/** + * Run a received message through the demultiplexer + * @arg msg Generic Netlink message + * @arg arg Argument passed on to the message handler callback + * + * @return 0 on success or a negative error code. + */ +int genl_handle_msg(struct nl_msg *msg, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct genl_ops *ops; + + if (!genlmsg_valid_hdr(nlh, 0)) + return -NLE_INVAL; + + if (!(ops = lookup_family(nlh->nlmsg_type))) + return -NLE_MSGTYPE_NOSUPPORT; + + return cmd_msg_parser(nlmsg_get_src(msg), nlh, ops, NULL, arg); +} + +/** @} */ + +/** + * @name Registration of Cache Operations + * @{ + */ + +/** + * Register Generic Netlink family backed cache + * @arg ops Cache operations definition + * + * Same as genl_register_family() but additionally registers the specified + * cache operations using nl_cache_mngt_register() and associates it with + * the Generic Netlink family. + * + * @see genl_register_family() + * + * @return 0 on success or a negative error code. + */ +int genl_register(struct nl_cache_ops *ops) +{ + int err; + + if (ops->co_protocol != NETLINK_GENERIC) { + err = -NLE_PROTO_MISMATCH; + goto errout; + } + + if (ops->co_hdrsize < (int)GENL_HDRSIZE(0)) { + err = -NLE_INVAL; + goto errout; + } + + if (ops->co_genl == NULL) { + err = -NLE_INVAL; + goto errout; + } + + ops->co_genl->o_cache_ops = ops; + ops->co_genl->o_hdrsize = ops->co_hdrsize - GENL_HDRLEN; + ops->co_genl->o_name = ops->co_msgtypes[0].mt_name; + ops->co_genl->o_id = ops->co_msgtypes[0].mt_id; + ops->co_msg_parser = genl_msg_parser; + + if ((err = genl_register_family(ops->co_genl)) < 0) + goto errout; + + err = nl_cache_mngt_register(ops); +errout: + return err; +} + +/** + * Unregister cache based Generic Netlink family + * @arg ops Cache operations definition + */ +void genl_unregister(struct nl_cache_ops *ops) +{ + if (!ops) + return; + + nl_cache_mngt_unregister(ops); + + genl_unregister_family(ops->co_genl); +} + +/** @} */ + +/** @cond SKIP */ +static int __genl_ops_resolve(struct nl_cache *ctrl, struct genl_ops *ops) +{ + struct genl_family *family; + + family = genl_ctrl_search_by_name(ctrl, ops->o_name); + if (family != NULL) { + ops->o_id = genl_family_get_id(family); + + if (ops->o_cache_ops) + ops->o_cache_ops->co_msgtypes[0].mt_id = ops->o_id; + + genl_family_put(family); + + return 0; + } + + return -NLE_OBJ_NOTFOUND; +} + +/* WARNING: this symbol is wrongly exported in libnl-genl-3.sym. */ +int genl_resolve_id(struct genl_ops *ops) +{ + struct nl_sock *sk; + int err = 0; + + /* Check if resolved already */ + if (ops->o_id != 0) + return 0; + + if (!ops->o_name) + return -NLE_INVAL; + + if (!(sk = nl_socket_alloc())) + return -NLE_NOMEM; + + if ((err = genl_connect(sk)) < 0) + goto errout_free; + + err = genl_ops_resolve(sk, ops); + +errout_free: + nl_socket_free(sk); + + return err; +} +/** @endcond */ + +/** + * @name Resolving the name of registered families + * @{ + */ + +/** + * Resolve a single Generic Netlink family + * @arg sk Generic Netlink socket + * @arg ops Generic Netlink family definition + * + * Resolves the family name to its numeric identifier. + * + * @return 0 on success or a negative error code. + */ +int genl_ops_resolve(struct nl_sock *sk, struct genl_ops *ops) +{ + struct nl_cache *ctrl; + int err; + + if ((err = genl_ctrl_alloc_cache(sk, &ctrl)) < 0) + goto errout; + + err = __genl_ops_resolve(ctrl, ops); + + nl_cache_free(ctrl); +errout: + return err; +} + +/** + * Resolve all registered Generic Netlink families + * @arg sk Generic Netlink socket + * + * Walks through all local Generic Netlink families that have been registered + * using genl_register() and resolves the name of each family to the + * corresponding numeric identifier. + * + * @see genl_register() + * @see genl_ops_resolve() + * + * @return 0 on success or a negative error code. + */ +int genl_mngt_resolve(struct nl_sock *sk) +{ + struct nl_cache *ctrl; + struct genl_ops *ops; + int err = 0; + + if ((err = genl_ctrl_alloc_cache(sk, &ctrl)) < 0) + goto errout; + + nl_list_for_each_entry(ops, &genl_ops_list, o_list) { + err = __genl_ops_resolve(ctrl, ops); + } + + nl_cache_free(ctrl); +errout: + return err; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/genl/nl-genl.h b/libnl/lib/genl/nl-genl.h new file mode 100644 index 0000000..2ad7cd0 --- /dev/null +++ b/libnl/lib/genl/nl-genl.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_GENL_PRIV_H_ +#define NETLINK_GENL_PRIV_H_ + +#include + +#include "nl-priv-dynamic-core/object-api.h" +#include "nl-priv-dynamic-core/cache-api.h" + +#define GENL_HDRSIZE(hdrlen) (GENL_HDRLEN + (hdrlen)) + +/*****************************************************************************/ + +/* WARNING: this symbol is wrongly exported in libnl-genl-3.sym. */ +extern int genl_resolve_id(struct genl_ops *ops); + +/*****************************************************************************/ + +#define GENL_OP_HAS_POLICY 1 +#define GENL_OP_HAS_DOIT 2 +#define GENL_OP_HAS_DUMPIT 4 + +struct genl_family_grp { + struct genl_family *family; /* private */ + struct nl_list_head list; /* private */ + char name[GENL_NAMSIZ]; + u_int32_t id; +}; + +struct genl_family +{ + NLHDR_COMMON + + uint16_t gf_id; + char gf_name[GENL_NAMSIZ]; + uint32_t gf_version; + uint32_t gf_hdrsize; + uint32_t gf_maxattr; + + struct nl_list_head gf_ops; + struct nl_list_head gf_mc_grps; +}; + +#endif diff --git a/libnl/lib/handlers.c b/libnl/lib/handlers.c new file mode 100644 index 0000000..14dffba --- /dev/null +++ b/libnl/lib/handlers.c @@ -0,0 +1,400 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +/** + * @ingroup core + * @defgroup cb Callbacks/Customization + * + * Related sections in the development guide: + * - @core_doc{core_cb, Callback Configuration} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#include "nl-default.h" + +#include +#include +#include +#include + +#include "nl-core.h" +#include "nl-priv-dynamic-core/nl-core.h" + +static void print_header_content(FILE *ofd, struct nlmsghdr *n) +{ + char flags[128]; + char type[32]; + + fprintf(ofd, "type=%s length=%u flags=<%s> sequence-nr=%u pid=%u", + nl_nlmsgtype2str(n->nlmsg_type, type, sizeof(type)), + n->nlmsg_len, nl_nlmsg_flags2str(n->nlmsg_flags, flags, + sizeof(flags)), n->nlmsg_seq, n->nlmsg_pid); +} + +static int nl_valid_handler_verbose(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stdout; + + fprintf(ofd, "-- Warning: unhandled valid message: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_OK; +} + +static int nl_invalid_handler_verbose(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Error: Invalid message: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_STOP; +} + +static int nl_overrun_handler_verbose(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Error: Netlink Overrun: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_STOP; +} + +static int nl_error_handler_verbose(struct sockaddr_nl *who, + struct nlmsgerr *e, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Error received: %s\n-- Original message: ", + nl_strerror_l(-e->error)); + print_header_content(ofd, &e->msg); + fprintf(ofd, "\n"); + + return -nl_syserr2nlerr(e->error); +} + +static int nl_valid_handler_debug(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Debug: Unhandled Valid message: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_OK; +} + +static int nl_finish_handler_debug(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Debug: End of multipart message block: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_STOP; +} + +static int nl_msg_in_handler_debug(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Debug: Received Message:\n"); + nl_msg_dump(msg, ofd); + + return NL_OK; +} + +static int nl_msg_out_handler_debug(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Debug: Sent Message:\n"); + nl_msg_dump(msg, ofd); + + return NL_OK; +} + +static int nl_skipped_handler_debug(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Debug: Skipped message: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_SKIP; +} + +static int nl_ack_handler_debug(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Debug: ACK: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_STOP; +} + +static nl_recvmsg_msg_cb_t cb_def[NL_CB_TYPE_MAX+1][NL_CB_KIND_MAX+1] = { + [NL_CB_VALID] = { + [NL_CB_VERBOSE] = nl_valid_handler_verbose, + [NL_CB_DEBUG] = nl_valid_handler_debug, + }, + [NL_CB_FINISH] = { + [NL_CB_DEBUG] = nl_finish_handler_debug, + }, + [NL_CB_INVALID] = { + [NL_CB_VERBOSE] = nl_invalid_handler_verbose, + [NL_CB_DEBUG] = nl_invalid_handler_verbose, + }, + [NL_CB_MSG_IN] = { + [NL_CB_DEBUG] = nl_msg_in_handler_debug, + }, + [NL_CB_MSG_OUT] = { + [NL_CB_DEBUG] = nl_msg_out_handler_debug, + }, + [NL_CB_OVERRUN] = { + [NL_CB_VERBOSE] = nl_overrun_handler_verbose, + [NL_CB_DEBUG] = nl_overrun_handler_verbose, + }, + [NL_CB_SKIPPED] = { + [NL_CB_DEBUG] = nl_skipped_handler_debug, + }, + [NL_CB_ACK] = { + [NL_CB_DEBUG] = nl_ack_handler_debug, + }, +}; + +static nl_recvmsg_err_cb_t cb_err_def[NL_CB_KIND_MAX+1] = { + [NL_CB_VERBOSE] = nl_error_handler_verbose, + [NL_CB_DEBUG] = nl_error_handler_verbose, +}; + +/** + * @name Callback Handle Management + * @{ + */ + +/** + * Allocate a new callback handle + * @arg kind callback kind to be used for initialization + * @return Newly allocated callback handle or NULL + */ +struct nl_cb *nl_cb_alloc(enum nl_cb_kind kind) +{ + int i; + struct nl_cb *cb; + + if ((unsigned int) kind > NL_CB_KIND_MAX) + return NULL; + + cb = calloc(1, sizeof(*cb)); + if (!cb) + return NULL; + + cb->cb_refcnt = 1; + cb->cb_active = NL_CB_TYPE_MAX + 1; + + for (i = 0; i <= NL_CB_TYPE_MAX; i++) + nl_cb_set(cb, i, kind, NULL, NULL); + + nl_cb_err(cb, kind, NULL, NULL); + + return cb; +} + +/** + * Clone an existing callback handle + * @arg orig original callback handle + * @return Newly allocated callback handle being a duplicate of + * orig or NULL + */ +struct nl_cb *nl_cb_clone(struct nl_cb *orig) +{ + struct nl_cb *cb; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!cb) + return NULL; + + memcpy(cb, orig, sizeof(*orig)); + cb->cb_refcnt = 1; + + return cb; +} + +struct nl_cb *nl_cb_get(struct nl_cb *cb) +{ + cb->cb_refcnt++; + + return cb; +} + +void nl_cb_put(struct nl_cb *cb) +{ + if (!cb) + return; + + cb->cb_refcnt--; + + if (cb->cb_refcnt < 0) + BUG(); + + if (cb->cb_refcnt <= 0) + free(cb); +} + +/** + * Obtain type of current active callback + * @arg cb callback to query + * + * @return type or __NL_CB_TYPE_MAX if none active + */ +enum nl_cb_type nl_cb_active_type(struct nl_cb *cb) +{ + return cb->cb_active; +} + +/** @} */ + +/** + * @name Callback Setup + * @{ + */ + +/** + * Set up a callback + * @arg cb callback set + * @arg type callback to modify + * @arg kind kind of implementation + * @arg func callback function (NL_CB_CUSTOM) + * @arg arg argument passed to callback + * + * @return 0 on success or a negative error code + */ +int nl_cb_set(struct nl_cb *cb, enum nl_cb_type type, enum nl_cb_kind kind, + nl_recvmsg_msg_cb_t func, void *arg) +{ + if ((unsigned int) type > NL_CB_TYPE_MAX) + return -NLE_RANGE; + + if ((unsigned int) kind > NL_CB_KIND_MAX) + return -NLE_RANGE; + + if (kind == NL_CB_CUSTOM) { + cb->cb_set[type] = func; + cb->cb_args[type] = arg; + } else { + cb->cb_set[type] = cb_def[type][kind]; + cb->cb_args[type] = arg; + } + + return 0; +} + +/** + * Set up a all callbacks + * @arg cb callback set + * @arg kind kind of callback + * @arg func callback function + * @arg arg argument to be passwd to callback function + * + * @return 0 on success or a negative error code + */ +int nl_cb_set_all(struct nl_cb *cb, enum nl_cb_kind kind, + nl_recvmsg_msg_cb_t func, void *arg) +{ + int i, err; + + for (i = 0; i <= NL_CB_TYPE_MAX; i++) { + err = nl_cb_set(cb, i, kind, func, arg); + if (err < 0) + return err; + } + + return 0; +} + +/** + * Set up an error callback + * @arg cb callback set + * @arg kind kind of callback + * @arg func callback function + * @arg arg argument to be passed to callback function + */ +int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind, + nl_recvmsg_err_cb_t func, void *arg) +{ + if ((unsigned int) kind > NL_CB_KIND_MAX) + return -NLE_RANGE; + + if (kind == NL_CB_CUSTOM) { + cb->cb_err = func; + cb->cb_err_arg = arg; + } else { + cb->cb_err = cb_err_def[kind]; + cb->cb_err_arg = arg; + } + + return 0; +} + +/** @} */ + +/** + * @name Overwriting + * @{ + */ + +/** + * Overwrite internal calls to nl_recvmsgs() + * @arg cb callback set + * @arg func replacement callback for nl_recvmsgs() + */ +void nl_cb_overwrite_recvmsgs(struct nl_cb *cb, + int (*func)(struct nl_sock *, struct nl_cb *)) +{ + cb->cb_recvmsgs_ow = func; +} + +/** + * Overwrite internal calls to nl_recv() + * @arg cb callback set + * @arg func replacement callback for nl_recv() + */ +void nl_cb_overwrite_recv(struct nl_cb *cb, + int (*func)(struct nl_sock *, struct sockaddr_nl *, + unsigned char **, struct ucred **)) +{ + cb->cb_recv_ow = func; +} + +/** + * Overwrite internal calls to nl_send() + * @arg cb callback set + * @arg func replacement callback for nl_send() + */ +void nl_cb_overwrite_send(struct nl_cb *cb, + int (*func)(struct nl_sock *, struct nl_msg *)) +{ + cb->cb_send_ow = func; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/hash.c b/libnl/lib/hash.c new file mode 100644 index 0000000..94a73f8 --- /dev/null +++ b/libnl/lib/hash.c @@ -0,0 +1,485 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * This code was taken from http://ccodearchive.net/info/hash.html + * The original file was modified to remove unwanted code + * and some changes to fit the current build environment + */ +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hash_word(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hash_word(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ +#include "nl-default.h" + +#include + +#if HAVE_LITTLE_ENDIAN +#define HASH_LITTLE_ENDIAN 1 +#define HASH_BIG_ENDIAN 0 +#elif HAVE_BIG_ENDIAN +#define HASH_LITTLE_ENDIAN 0 +#define HASH_BIG_ENDIAN 1 +#else +#error Unknown endian +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + val2 : IN: can be any 4-byte value OUT: second 32 bit hash. +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. Note that the return value is better +mixed than val2, so use that first. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + * + * Not on my testing with gcc 4.5 on an intel i5 CPU, at least --RR. + */ +#if 0 + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; /* fall through */ + case 11: c+=((uint32_t)k[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k[9])<<8; /* fall through */ + case 9 : c+=k[8]; /* fall through */ + case 8 : b+=((uint32_t)k[7])<<24; /* fall through */ + case 7 : b+=((uint32_t)k[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k[5])<<8; /* fall through */ + case 5 : b+=k[4]; /* fall through */ + case 4 : a+=((uint32_t)k[3])<<24; /* fall through */ + case 3 : a+=((uint32_t)k[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k[1])<<8; /* fall through */ + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + *val2 = b; + return c; +} + +/* + * hashbig(): + * This is the same as hash_word() on big-endian machines. It is different + * from hashlittle() on all machines. hashbig() takes advantage of + * big-endian byte ordering. + */ +static uint32_t hashbig( const void *key, size_t length, uint32_t *val2) +{ + uint32_t a,b,c; + union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + *val2; + + u.ptr = key; + if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + const uint8_t *k8; + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]<<8" actually reads beyond the end of the string, but + * then shifts out the part it's not allowed to read. Because the + * string is aligned, the illegal read is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + * + * Not on my testing with gcc 4.5 on an intel i5 CPU, at least --RR. + */ +#if 0 + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff00; a+=k[0]; break; + case 6 : b+=k[1]&0xffff0000; a+=k[0]; break; + case 5 : b+=k[1]&0xff000000; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff00; break; + case 2 : a+=k[0]&0xffff0000; break; + case 1 : a+=k[0]&0xff000000; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<8; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<16; /* fall through */ + case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */ + case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */ + case 1 : a+=((uint32_t)k8[0])<<24; break; + case 0 : return c; + } + +#endif /* !VALGRIND */ + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += ((uint32_t)k[0])<<24; + a += ((uint32_t)k[1])<<16; + a += ((uint32_t)k[2])<<8; + a += ((uint32_t)k[3]); + b += ((uint32_t)k[4])<<24; + b += ((uint32_t)k[5])<<16; + b += ((uint32_t)k[6])<<8; + b += ((uint32_t)k[7]); + c += ((uint32_t)k[8])<<24; + c += ((uint32_t)k[9])<<16; + c += ((uint32_t)k[10])<<8; + c += ((uint32_t)k[11]); + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[11]; /* fall through */ + case 11: c+=((uint32_t)k[10])<<8; /* fall through */ + case 10: c+=((uint32_t)k[9])<<16; /* fall through */ + case 9 : c+=((uint32_t)k[8])<<24; /* fall through */ + case 8 : b+=k[7]; /* fall through */ + case 7 : b+=((uint32_t)k[6])<<8; /* fall through */ + case 6 : b+=((uint32_t)k[5])<<16; /* fall through */ + case 5 : b+=((uint32_t)k[4])<<24; /* fall through */ + case 4 : a+=k[3]; /* fall through */ + case 3 : a+=((uint32_t)k[2])<<8; /* fall through */ + case 2 : a+=((uint32_t)k[1])<<16; /* fall through */ + case 1 : a+=((uint32_t)k[0])<<24; /* fall through */ + break; + case 0 : return c; + } + } + + final(a,b,c); + *val2 = b; + return c; +} + +uint32_t nl_hash_any(const void *key, size_t length, uint32_t base) +{ + if (HASH_BIG_ENDIAN) + return hashbig(key, length, &base); + else + return hashlittle(key, length, &base); +} diff --git a/libnl/lib/hashtable.c b/libnl/lib/hashtable.c new file mode 100644 index 0000000..7cb6984 --- /dev/null +++ b/libnl/lib/hashtable.c @@ -0,0 +1,194 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2012 Cumulus Networks, Inc + */ + +#include "nl-default.h" + +#include +#include +#include + +#include "nl-aux-core/nl-core.h" + +/** + * @ingroup core_types + * @defgroup hashtable Hashtable + * @{ + */ + +/** + * Allocate hashtable + * @arg size Size of hashtable in number of elements + * + * @return Allocated hashtable or NULL. + */ +nl_hash_table_t *nl_hash_table_alloc(int size) +{ + nl_hash_table_t *ht; + + ht = calloc(1, sizeof (*ht)); + if (!ht) + goto errout; + + ht->nodes = calloc(size, sizeof (*ht->nodes)); + if (!ht->nodes) { + free(ht); + goto errout; + } + + ht->size = size; + + return ht; +errout: + return NULL; +} + +/** + * Free hashtable including all nodes + * @arg ht Hashtable + * + * @note Reference counter of all objects in the hashtable will be decremented. + */ +void nl_hash_table_free(nl_hash_table_t *ht) +{ + int i; + + for(i = 0; i < ht->size; i++) { + nl_hash_node_t *node = ht->nodes[i]; + nl_hash_node_t *saved_node; + + while (node) { + saved_node = node; + node = node->next; + nl_object_put(saved_node->obj); + free(saved_node); + } + } + + free(ht->nodes); + free(ht); +} + +/** + * Lookup identical object in hashtable + * @arg ht Hashtable + * @arg obj Object to lookup + * + * Generates hashkey for `obj` and traverses the corresponding chain calling + * `nl_object_identical()` on each trying to find a match. + * + * @return Pointer to object if match was found or NULL. + */ +struct nl_object* nl_hash_table_lookup(nl_hash_table_t *ht, + struct nl_object *obj) +{ + nl_hash_node_t *node; + uint32_t key_hash; + + nl_object_keygen(obj, &key_hash, ht->size); + node = ht->nodes[key_hash]; + + while (node) { + if (nl_object_identical(node->obj, obj)) + return node->obj; + node = node->next; + } + + return NULL; +} + +/** + * Add object to hashtable + * @arg ht Hashtable + * @arg obj Object to add + * + * Adds `obj` to the hashtable. Object type must support hashing, otherwise all + * objects will be added to the chain `0`. + * + * @note The reference counter of the object is incremented. + * + * @return 0 on success or a negative error code + * @retval -NLE_EXIST Identical object already present in hashtable + */ +int nl_hash_table_add(nl_hash_table_t *ht, struct nl_object *obj) +{ + nl_hash_node_t *node; + uint32_t key_hash; + + nl_object_keygen(obj, &key_hash, ht->size); + node = ht->nodes[key_hash]; + + while (node) { + if (nl_object_identical(node->obj, obj)) { + NL_DBG(2, "Warning: Add to hashtable found duplicate...\n"); + return -NLE_EXIST; + } + node = node->next; + } + + NL_DBG (5, "adding cache entry of obj %p in table %p, with hash 0x%x\n", + obj, ht, key_hash); + + node = malloc(sizeof(nl_hash_node_t)); + if (!node) + return -NLE_NOMEM; + nl_object_get(obj); + node->obj = obj; + node->key = key_hash; + node->key_size = sizeof(uint32_t); + node->next = ht->nodes[key_hash]; + ht->nodes[key_hash] = node; + + return 0; +} + +/** + * Remove object from hashtable + * @arg ht Hashtable + * @arg obj Object to remove + * + * Remove `obj` from hashtable if it exists. + * + * @note Reference counter of object will be decremented. + * + * @return 0 on success or a negative error code. + * @retval -NLE_OBJ_NOTFOUND Object not present in hashtable. + */ +int nl_hash_table_del(nl_hash_table_t *ht, struct nl_object *obj) +{ + nl_hash_node_t *node, *prev; + uint32_t key_hash; + + nl_object_keygen(obj, &key_hash, ht->size); + prev = node = ht->nodes[key_hash]; + + while (node) { + if (nl_object_identical(node->obj, obj)) { + nl_object_put(obj); + + NL_DBG (5, "deleting cache entry of obj %p in table %p, with" + " hash 0x%x\n", obj, ht, key_hash); + + if (node == ht->nodes[key_hash]) + ht->nodes[key_hash] = node->next; + else + prev->next = node->next; + + free(node); + + return 0; + } + prev = node; + node = node->next; + } + + return -NLE_OBJ_NOTFOUND; +} + +uint32_t nl_hash(void *k, size_t length, uint32_t initval) +{ + return(__nl_hash((char *) k, length, initval)); +} + +/** @} */ diff --git a/libnl/lib/idiag/idiag.c b/libnl/lib/idiag/idiag.c new file mode 100644 index 0000000..4f5d9f9 --- /dev/null +++ b/libnl/lib/idiag/idiag.c @@ -0,0 +1,276 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Sassano Systems LLC + */ + +/** + * @defgroup idiag Inet Diag library (libnl-idiag) + * @brief + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +#include "nl-priv-dynamic-core/nl-core.h" + +/** + * @name Socket Creation + * @{ + */ + +/** + * Create and connect idiag netlink socket. + * @arg sk Netlink socket. + * + * Creates a NETLINK_INET_DIAG socket, binds the socket, and issues a connection + * attemp. + * + * @see nl_connect() + * + * @return 0 on success or a negative error code. + */ +int idiagnl_connect(struct nl_sock *sk) +{ + return nl_connect(sk, NETLINK_INET_DIAG); +} + +/** @} */ + +/** + * @name Sending + * @{ + */ + +/** + * Send trivial idiag netlink message + * @arg sk Netlink socket. + * @arg flags Message flags + * @arg family Address family + * @arg states Socket states to query + * @arg ext Inet Diag attribute extensions to query. Note that this only supports + * 8 bit arguments. Flags outside uint8_t range are silently ignored. + * + * @return 0 on success or a negative error code. Due to a bug, this function + * returns the number of bytes sent. Treat any non-negative number as success. + */ +int idiagnl_send_simple(struct nl_sock *sk, int flags, uint8_t family, + uint16_t states, uint16_t ext) +{ + struct inet_diag_req req; + memset(&req, 0, sizeof(req)); + + flags |= NLM_F_ROOT; + + req.idiag_family = family; + req.idiag_states = states; + req.idiag_ext = ext; + + return nl_send_simple(sk, TCPDIAG_GETSOCK, flags, &req, sizeof(req)); +} + +/** @} */ + +/** + * @name Inet Diag flag and attribute conversions + * @{ + */ + +static const struct trans_tbl idiag_states[] = { + __ADD(TCP_ESTABLISHED, established), + __ADD(TCP_SYN_SENT, syn_sent), + __ADD(TCP_SYN_RECV, syn_recv), + __ADD(TCP_FIN_WAIT1, fin_wait), + __ADD(TCP_FIN_WAIT2, fin_wait2), + __ADD(TCP_TIME_WAIT, time_wait), + __ADD(TCP_CLOSE, close), + __ADD(TCP_CLOSE_WAIT, close_wait), + __ADD(TCP_LAST_ACK, last_ack), + __ADD(TCP_LISTEN, listen), + __ADD(TCP_CLOSING, closing), +}; + +/** + * Convert inet diag socket states to strings. + * @arg state inetdiag socket state (e.g., TCP_ESTABLISHED) + * @arg buf output buffer which will hold string result + * @arg len length in bytes of the output buffer + * + * @return string representation of the inetdiag socket state or an empty + * string. + */ +char * idiagnl_state2str(int state, char *buf, size_t len) +{ + return __type2str(state, buf, len, idiag_states, + ARRAY_SIZE(idiag_states)); +} + +/** + * Convert inet diag socket state string to int. + * @arg name inetdiag socket state string + * + * @return the int representation of the socket state strign or a negative error + * code. + */ +int idiagnl_str2state(const char *name) +{ + return __str2type(name, idiag_states, ARRAY_SIZE(idiag_states)); +} + +static const struct trans_tbl idiag_timers[] = { + __ADD(IDIAGNL_TIMER_OFF, off), + __ADD(IDIAGNL_TIMER_ON, on), + __ADD(IDIAGNL_TIMER_KEEPALIVE, keepalive), + __ADD(IDIAGNL_TIMER_TIMEWAIT, timewait), + __ADD(IDIAGNL_TIMER_PERSIST, persist), + __ADD(IDIAGNL_TIMER_UNKNOWN, unknown), +}; + +/** + * Convert inet diag timer types to strings. + * @arg timer inetdiag timer (e.g., IDIAGNL_TIMER_ON) + * @arg buf output buffer which will hold string result + * @arg len length in bytes of the output buffer + * + * @return string representation of the inetdiag timer type or an empty string. + */ +char * idiagnl_timer2str(int timer, char *buf, size_t len) +{ + return __type2str(timer, buf, len, idiag_timers, + ARRAY_SIZE(idiag_timers)); +} + +/** + * Convert inet diag timer string to int. + * @arg name inetdiag timer string + * + * @return the int representation of the timer string or a negative error code. + */ +int idiagnl_str2timer(const char *name) +{ + return __str2type(name, idiag_timers, ARRAY_SIZE(idiag_timers)); +} + +static const struct trans_tbl idiag_attrs[] = { + __ADD(INET_DIAG_NONE, none), + __ADD(INET_DIAG_MEMINFO, meminfo), + __ADD(INET_DIAG_INFO, info), + __ADD(INET_DIAG_VEGASINFO, vegasinfo), + __ADD(INET_DIAG_CONG, congestion), + __ADD(INET_DIAG_TOS, tos), + __ADD(INET_DIAG_TCLASS, tclass), + __ADD(INET_DIAG_SKMEMINFO, skmeminfo), + __ADD(INET_DIAG_SHUTDOWN, shutdown), +}; + +/** + * Convert inet diag extension type to a string. + * @arg attrs inet diag extension type (e.g. INET_DIAG_MEMINFO) + * @arg buf output buffer which will hold string result + * @arg len length in bytes of the output buffer + * + * @return string representation of inet diag extension type or an empty string. + * @deprecated: don't use this function. It is not very useful and should + * never have been exposed as public API. + */ +char *idiagnl_attrs2str(int attrs, char *buf, size_t len) +{ + return __type2str(attrs, buf, len, idiag_attrs, ARRAY_SIZE(idiag_attrs)); +} + +static const struct trans_tbl idiag_exts[] = { + __ADD((1 << (INET_DIAG_MEMINFO - 1)), meminfo), + __ADD((1 << (INET_DIAG_INFO - 1)), info), + __ADD((1 << (INET_DIAG_VEGASINFO - 1)), vegasinfo), + __ADD((1 << (INET_DIAG_CONG - 1)), congestion), + __ADD((1 << (INET_DIAG_TOS - 1)), tos), + __ADD((1 << (INET_DIAG_TCLASS - 1)), tclass), + __ADD((1 << (INET_DIAG_SKMEMINFO - 1)), skmeminfo), + __ADD((1 << (INET_DIAG_SHUTDOWN - 1)), shutdown), +}; + +/** + * Convert inet diag extension flags to a string. + * @arg attrs inet diag extension flags (e.g. + * ( (1<<(INET_DIAG_MEMINFO-1)) | (1<<(INET_DIAG_CONG-1)) | (1<<(INET_DIAG_TOS-1)) ) ) + * @arg buf Output buffer to hold string representation + * @arg len length in bytes of the output buffer + */ +char *idiagnl_exts2str(uint8_t attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, idiag_exts, ARRAY_SIZE(idiag_exts)); +} + +static const struct trans_tbl idiagnl_tcpstates[] = { + __ADD(TCP_CA_Open, open), + __ADD(TCP_CA_Disorder, disorder), + __ADD(TCP_CA_CWR, cwr), + __ADD(TCP_CA_Recovery, recovery), + __ADD(TCP_CA_Loss, loss), +}; + +/** + * Convert inetdiag tcp states to strings. + * @arg state TCP state (e.g., TCP_CA_Open) + * @arg buf output buffer which will hold string result + * @arg len length in bytes of the output buffer + */ +char *idiagnl_tcpstate2str(uint8_t state, char *buf, size_t len) +{ + return __type2str(state, buf, len, idiagnl_tcpstates, + ARRAY_SIZE(idiagnl_tcpstates)); +} + +static const struct trans_tbl idiagnl_tcpopt_attrs[] = { + __ADD(TCPI_OPT_TIMESTAMPS, timestamps), + __ADD(TCPI_OPT_SACK, sACK), + __ADD(TCPI_OPT_WSCALE, wscale), + __ADD(TCPI_OPT_ECN, ecn), +}; + +/** + * Convert TCP option attributes to string + * @arg attrs TCP option attributes to convert (e.g., TCPI_OPT_SACK | + * TCPI_OPT_WSCALE) + * @arg buf Output buffer for string + * @arg len Length in bytes of output buffer + * + * @return buffer with string representation or empty string + */ +char *idiagnl_tcpopts2str(uint8_t attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, idiagnl_tcpopt_attrs, + ARRAY_SIZE(idiagnl_tcpopt_attrs)); +} + +/** + * Convert shutdown state to string. + * @arg shutdown Shutdown state (e.g., idiag_msg->shutdown) + * @arg buf Ouput buffer to hold string representation + * @arg len Length in bytes of output buffer + * + * @return string representation of shutdown state or NULL + */ +char * idiagnl_shutdown2str(uint8_t shutdown, char *buf, size_t len) +{ + if (shutdown == 0) { + snprintf(buf, len, " "); + return buf; + } else if (shutdown == 1) { + snprintf(buf, len, "receive shutdown"); + return buf; + } else if (shutdown == 2) { + snprintf(buf, len, "send shutdown"); + return buf; + } + + return NULL; +} + +/** @} */ +/** @} */ diff --git a/libnl/lib/idiag/idiag_meminfo_obj.c b/libnl/lib/idiag/idiag_meminfo_obj.c new file mode 100644 index 0000000..ebb9767 --- /dev/null +++ b/libnl/lib/idiag/idiag_meminfo_obj.c @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Sassano Systems LLC + */ + +#include "nl-default.h" + +#include + +#include "nl-idiag.h" + +/** + * @ingroup idiag + * @defgroup idiagnl_meminfo Inet Diag Memory Info + * + * @details + * @idiagnl_doc{idiagnl_meminfo, Inet Diag Memory Info Documentation} + * @{ + */ +struct idiagnl_meminfo *idiagnl_meminfo_alloc(void) +{ + return (struct idiagnl_meminfo *) nl_object_alloc(&idiagnl_meminfo_obj_ops); +} + +void idiagnl_meminfo_get(struct idiagnl_meminfo *minfo) +{ + nl_object_get((struct nl_object *) minfo); +} + +void idiagnl_meminfo_put(struct idiagnl_meminfo *minfo) +{ + nl_object_put((struct nl_object *) minfo); +} + +/** + * @name Attributes + * @{ + */ +uint32_t idiagnl_meminfo_get_rmem(const struct idiagnl_meminfo *minfo) +{ + return minfo->idiag_rmem; +} + +void idiagnl_meminfo_set_rmem(struct idiagnl_meminfo *minfo, uint32_t rmem) +{ + minfo->idiag_rmem = rmem; +} + +uint32_t idiagnl_meminfo_get_wmem(const struct idiagnl_meminfo *minfo) +{ + return minfo->idiag_wmem; +} + +void idiagnl_meminfo_set_wmem(struct idiagnl_meminfo *minfo, uint32_t wmem) +{ + minfo->idiag_wmem = wmem; +} + +uint32_t idiagnl_meminfo_get_fmem(const struct idiagnl_meminfo *minfo) +{ + return minfo->idiag_fmem; +} + +void idiagnl_meminfo_set_fmem(struct idiagnl_meminfo *minfo, uint32_t fmem) +{ + minfo->idiag_fmem = fmem; +} + +uint32_t idiagnl_meminfo_get_tmem(const struct idiagnl_meminfo *minfo) +{ + return minfo->idiag_tmem; +} + +void idiagnl_meminfo_set_tmem(struct idiagnl_meminfo *minfo, uint32_t tmem) +{ + minfo->idiag_tmem = tmem; +} +/** @} */ + +/** @cond SKIP */ +static uint64_t idiagnl_meminfo_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct idiagnl_meminfo *a = (struct idiagnl_meminfo *) _a; + struct idiagnl_meminfo *b = (struct idiagnl_meminfo *) _b; + + /* meminfo is a very simple object. It has no attribe flags (ce_mask), + * hence compare just returns 0 or 1, not a bit mask of attributes. */ + return a->idiag_rmem != b->idiag_rmem || + a->idiag_wmem != b->idiag_wmem || + a->idiag_fmem != b->idiag_fmem || + a->idiag_tmem != b->idiag_tmem; +} + +struct nl_object_ops idiagnl_meminfo_obj_ops = { + .oo_name = "idiag/idiag_meminfo", + .oo_size = sizeof(struct idiagnl_meminfo), + .oo_compare = idiagnl_meminfo_compare, +}; +/** @endcond */ +/** @} */ diff --git a/libnl/lib/idiag/idiag_msg_obj.c b/libnl/lib/idiag/idiag_msg_obj.c new file mode 100644 index 0000000..2c78eb1 --- /dev/null +++ b/libnl/lib/idiag/idiag_msg_obj.c @@ -0,0 +1,1003 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Sassano Systems LLC + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include + +#include "nl-idiag.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-aux-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" + +/** @cond SKIP */ +struct idiagnl_msg { + NLHDR_COMMON + + uint8_t idiag_family; + uint8_t idiag_state; + uint8_t idiag_timer; + uint8_t idiag_retrans; + uint16_t idiag_sport; + uint16_t idiag_dport; + struct nl_addr * idiag_src; + struct nl_addr * idiag_dst; + uint32_t idiag_ifindex; + uint32_t idiag_expires; + uint32_t idiag_rqueue; + uint32_t idiag_wqueue; + uint32_t idiag_uid; + uint32_t idiag_inode; + + uint8_t idiag_tos; + uint8_t idiag_tclass; + uint8_t idiag_shutdown; + char * idiag_cong; + struct idiagnl_meminfo * idiag_meminfo; + struct idiagnl_vegasinfo * idiag_vegasinfo; + struct tcp_info idiag_tcpinfo; + uint32_t idiag_skmeminfo[SK_MEMINFO_VARS]; +}; + +#define IDIAGNL_ATTR_FAMILY (0x1 << 1) +#define IDIAGNL_ATTR_STATE (0x1 << 2) +#define IDIAGNL_ATTR_TIMER (0x1 << 3) +#define IDIAGNL_ATTR_RETRANS (0x1 << 4) +#define IDIAGNL_ATTR_SPORT (0x1 << 5) +#define IDIAGNL_ATTR_DPORT (0x1 << 6) +#define IDIAGNL_ATTR_SRC (0x1 << 7) +#define IDIAGNL_ATTR_DST (0x1 << 8) +#define IDIAGNL_ATTR_IFINDEX (0x1 << 9) +#define IDIAGNL_ATTR_EXPIRES (0x1 << 10) +#define IDIAGNL_ATTR_RQUEUE (0x1 << 11) +#define IDIAGNL_ATTR_WQUEUE (0x1 << 12) +#define IDIAGNL_ATTR_UID (0x1 << 13) +#define IDIAGNL_ATTR_INODE (0x1 << 14) +#define IDIAGNL_ATTR_TOS (0x1 << 15) +#define IDIAGNL_ATTR_TCLASS (0x1 << 16) +#define IDIAGNL_ATTR_SHUTDOWN (0x1 << 17) +#define IDIAGNL_ATTR_CONG (0x1 << 18) +#define IDIAGNL_ATTR_MEMINFO (0x1 << 19) +#define IDIAGNL_ATTR_VEGASINFO (0x1 << 20) +#define IDIAGNL_ATTR_TCPINFO (0x1 << 21) +#define IDIAGNL_ATTR_SKMEMINFO (0x1 << 22) + +#define _INET_DIAG_ALL ((1<<(INET_DIAG_MAX+1))-1) +/** @endcond */ + +/** + * @ingroup idiag + * @defgroup idiagnl_msg Inet Diag Messages + * + * @details + * @idiagnl_doc{idiagnl_msg, Inet Diag Message Documentation} + * @{ + */ +struct idiagnl_msg *idiagnl_msg_alloc(void) +{ + return (struct idiagnl_msg *) nl_object_alloc(&idiagnl_msg_obj_ops); +} + +void idiagnl_msg_get(struct idiagnl_msg *msg) +{ + nl_object_get((struct nl_object *) msg); +} + +void idiagnl_msg_put(struct idiagnl_msg *msg) +{ + nl_object_put((struct nl_object *) msg); +} + +static struct nl_cache_ops idiagnl_msg_ops; + +static int idiagnl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct idiagnl_msg *msg = NULL; + int err = 0; + + if ((err = idiagnl_msg_parse(nlh, &msg)) < 0) + return err; + + err = pp->pp_cb((struct nl_object *) msg, pp); + idiagnl_msg_put(msg); + + return err; +} + +static int idiagnl_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + int family = cache->c_iarg1; + int states = cache->c_iarg2; + + /* idiagnl_send_simple()'s "ext" argument is u16, which is too small for _INET_DIAG_ALL, + * which is more than 16 bits on recent kernels. + * + * Actually, internally idiagnl_send_simple() sets "struct inet_diag_req"'s "idiag_ext" + * field, which is only 8 bits. So, it's even worse. + * + * FIXME: this probably should be fixed (by adding idiagnl_send_simple2() function), but for + * the moment it means we cannot request more than 0xFF. + */ + + return idiagnl_send_simple(sk, 0, family, states, (uint16_t) _INET_DIAG_ALL); +} + +static struct nl_cache_ops idiagnl_msg_ops = { + .co_name = "idiag/idiag", + .co_hdrsize = sizeof(struct inet_diag_msg), + .co_msgtypes = { + { TCPDIAG_GETSOCK, NL_ACT_NEW, "new" }, + { DCCPDIAG_GETSOCK, NL_ACT_NEW, "new" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_INET_DIAG, + .co_request_update = idiagnl_request_update, + .co_msg_parser = idiagnl_msg_parser, + .co_obj_ops = &idiagnl_msg_obj_ops, +}; + +static void _nl_init idiagnl_init(void) +{ + nl_cache_mngt_register(&idiagnl_msg_ops); +} + +static void _nl_exit idiagnl_exit(void) +{ + nl_cache_mngt_unregister(&idiagnl_msg_ops); +} + +/** + * @name Cache Management + * @{ + */ + +/** + * Build an inetdiag cache to hold socket state information. + * @arg sk Netlink socket + * @arg family The address family to query + * @arg states Socket states to query + * @arg result Result pointer + * + * @note The caller is responsible for destroying and free the cache after using + * it. + * @return 0 on success of a negative error code. + */ +int idiagnl_msg_alloc_cache(struct nl_sock *sk, int family, int states, + struct nl_cache **result) +{ + struct nl_cache *cache = NULL; + int err; + + if (!(cache = nl_cache_alloc(&idiagnl_msg_ops))) + return -NLE_NOMEM; + + cache->c_iarg1 = family; + cache->c_iarg2 = states; + + if (sk && (err = nl_cache_refill(sk, cache)) < 0) { + free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +uint8_t idiagnl_msg_get_family(const struct idiagnl_msg *msg) +{ + return msg->idiag_family; +} + +void idiagnl_msg_set_family(struct idiagnl_msg *msg, uint8_t family) +{ + msg->idiag_family = family; + msg->ce_mask |= IDIAGNL_ATTR_FAMILY; +} + +uint8_t idiagnl_msg_get_state(const struct idiagnl_msg *msg) +{ + return msg->idiag_state; +} + +void idiagnl_msg_set_state(struct idiagnl_msg *msg, uint8_t state) +{ + msg->idiag_state = state; + msg->ce_mask |= IDIAGNL_ATTR_STATE; +} + +uint8_t idiagnl_msg_get_timer(const struct idiagnl_msg *msg) +{ + return msg->idiag_timer; +} + +void idiagnl_msg_set_timer(struct idiagnl_msg *msg, uint8_t timer) +{ + msg->idiag_timer = timer; + msg->ce_mask |= IDIAGNL_ATTR_TIMER; +} + +uint8_t idiagnl_msg_get_retrans(const struct idiagnl_msg *msg) +{ + return msg->idiag_retrans; +} + +void idiagnl_msg_set_retrans(struct idiagnl_msg *msg, uint8_t retrans) +{ + msg->idiag_retrans = retrans; + msg->ce_mask |= IDIAGNL_ATTR_RETRANS; +} + +uint16_t idiagnl_msg_get_sport(struct idiagnl_msg *msg) +{ + return msg->idiag_sport; +} + +void idiagnl_msg_set_sport(struct idiagnl_msg *msg, uint16_t port) +{ + msg->idiag_sport = port; + msg->ce_mask |= IDIAGNL_ATTR_SPORT; +} + +uint16_t idiagnl_msg_get_dport(struct idiagnl_msg *msg) +{ + return msg->idiag_dport; +} + +void idiagnl_msg_set_dport(struct idiagnl_msg *msg, uint16_t port) +{ + msg->idiag_dport = port; + msg->ce_mask |= IDIAGNL_ATTR_DPORT; +} + +struct nl_addr *idiagnl_msg_get_src(const struct idiagnl_msg *msg) +{ + return msg->idiag_src; +} + +int idiagnl_msg_set_src(struct idiagnl_msg *msg, struct nl_addr *addr) +{ + if (msg->idiag_src) + nl_addr_put(msg->idiag_src); + + nl_addr_get(addr); + msg->idiag_src = addr; + msg->ce_mask |= IDIAGNL_ATTR_SRC; + + return 0; +} + +struct nl_addr *idiagnl_msg_get_dst(const struct idiagnl_msg *msg) +{ + return msg->idiag_dst; +} + +int idiagnl_msg_set_dst(struct idiagnl_msg *msg, struct nl_addr *addr) +{ + if (msg->idiag_dst) + nl_addr_put(msg->idiag_dst); + + nl_addr_get(addr); + msg->idiag_dst = addr; + msg->ce_mask |= IDIAGNL_ATTR_DST; + + return 0; +} + +uint32_t idiagnl_msg_get_ifindex(const struct idiagnl_msg *msg) +{ + return msg->idiag_ifindex; +} + +void idiagnl_msg_set_ifindex(struct idiagnl_msg *msg, uint32_t ifindex) +{ + msg->idiag_ifindex = ifindex; + msg->ce_mask |= IDIAGNL_ATTR_IFINDEX; +} + +uint32_t idiagnl_msg_get_expires(const struct idiagnl_msg *msg) +{ + return msg->idiag_expires; +} + +void idiagnl_msg_set_expires(struct idiagnl_msg *msg, uint32_t expires) +{ + msg->idiag_expires = expires; + msg->ce_mask |= IDIAGNL_ATTR_EXPIRES; +} + +uint32_t idiagnl_msg_get_rqueue(const struct idiagnl_msg *msg) +{ + return msg->idiag_rqueue; +} + +void idiagnl_msg_set_rqueue(struct idiagnl_msg *msg, uint32_t rqueue) +{ + msg->idiag_rqueue = rqueue; + msg->ce_mask |= IDIAGNL_ATTR_RQUEUE; +} + +uint32_t idiagnl_msg_get_wqueue(const struct idiagnl_msg *msg) +{ + return msg->idiag_wqueue; +} + +void idiagnl_msg_set_wqueue(struct idiagnl_msg *msg, uint32_t wqueue) +{ + msg->idiag_wqueue = wqueue; + msg->ce_mask |= IDIAGNL_ATTR_WQUEUE; +} + +uint32_t idiagnl_msg_get_uid(const struct idiagnl_msg *msg) +{ + return msg->idiag_uid; +} + +void idiagnl_msg_set_uid(struct idiagnl_msg *msg, uint32_t uid) +{ + msg->idiag_uid = uid; + msg->ce_mask |= IDIAGNL_ATTR_UID; +} + +uint32_t idiagnl_msg_get_inode(const struct idiagnl_msg *msg) +{ + return msg->idiag_inode; +} + +void idiagnl_msg_set_inode(struct idiagnl_msg *msg, uint32_t inode) +{ + msg->idiag_inode = inode; + msg->ce_mask |= IDIAGNL_ATTR_INODE; +} + +uint8_t idiagnl_msg_get_tos(const struct idiagnl_msg *msg) +{ + return msg->idiag_tos; +} + +void idiagnl_msg_set_tos(struct idiagnl_msg *msg, uint8_t tos) +{ + msg->idiag_tos = tos; + msg->ce_mask |= IDIAGNL_ATTR_TOS; +} + +uint8_t idiagnl_msg_get_tclass(const struct idiagnl_msg *msg) +{ + return msg->idiag_tclass; +} + +void idiagnl_msg_set_tclass(struct idiagnl_msg *msg, uint8_t tclass) +{ + msg->idiag_tclass = tclass; + msg->ce_mask |= IDIAGNL_ATTR_TCLASS; +} + +uint8_t idiagnl_msg_get_shutdown(const struct idiagnl_msg *msg) +{ + return msg->idiag_shutdown; +} + +void idiagnl_msg_set_shutdown(struct idiagnl_msg *msg, uint8_t shutdown) +{ + msg->idiag_shutdown = shutdown; + msg->ce_mask |= IDIAGNL_ATTR_SHUTDOWN; +} + +char *idiagnl_msg_get_cong(const struct idiagnl_msg *msg) +{ + return msg->idiag_cong; +} + +void idiagnl_msg_set_cong(struct idiagnl_msg *msg, char *cong) +{ + free (msg->idiag_cong); + msg->idiag_cong = strdup(cong); + msg->ce_mask |= IDIAGNL_ATTR_CONG; +} + +struct idiagnl_meminfo *idiagnl_msg_get_meminfo(const struct idiagnl_msg *msg) +{ + return msg->idiag_meminfo; +} + +void idiagnl_msg_set_meminfo(struct idiagnl_msg *msg, struct idiagnl_meminfo *minfo) +{ + if (msg->idiag_meminfo) + idiagnl_meminfo_put(msg->idiag_meminfo); + + idiagnl_meminfo_get(minfo); + msg->idiag_meminfo = minfo; + msg->ce_mask |= IDIAGNL_ATTR_MEMINFO; +} + +struct idiagnl_vegasinfo *idiagnl_msg_get_vegasinfo(const struct idiagnl_msg *msg) +{ + return msg->idiag_vegasinfo; +} + +void idiagnl_msg_set_vegasinfo(struct idiagnl_msg *msg, struct idiagnl_vegasinfo *vinfo) +{ + if (msg->idiag_vegasinfo) + idiagnl_vegasinfo_put(msg->idiag_vegasinfo); + + idiagnl_vegasinfo_get(vinfo); + msg->idiag_vegasinfo = vinfo; + msg->ce_mask |= IDIAGNL_ATTR_VEGASINFO; +} + +struct tcp_info idiagnl_msg_get_tcpinfo(const struct idiagnl_msg *msg) +{ + return msg->idiag_tcpinfo; +} + +void idiagnl_msg_set_tcpinfo(struct idiagnl_msg *msg, struct tcp_info *tinfo) +{ + memcpy(&msg->idiag_tcpinfo, tinfo, sizeof(struct tcp_info)); + msg->ce_mask |= IDIAGNL_ATTR_TCPINFO; +} + +/** @} */ + +static void idiag_msg_dump_line(struct nl_object *a, struct nl_dump_params *p) +{ + struct idiagnl_msg *msg = (struct idiagnl_msg *) a; + char buf[64] = { 0 }; + + nl_dump_line(p, "family: %s ", nl_af2str(msg->idiag_family, buf, sizeof(buf))); + nl_dump(p, "src: %s:%d ", nl_addr2str(msg->idiag_src, buf, sizeof(buf)), + ntohs(msg->idiag_sport)); + nl_dump(p, "dst: %s:%d ", nl_addr2str(msg->idiag_dst, buf, sizeof(buf)), + ntohs(msg->idiag_dport)); + nl_dump(p, "iif: %d ", msg->idiag_ifindex); + nl_dump(p, "\n"); +} + +static void idiag_msg_dump_details(struct nl_object *a, struct nl_dump_params *p) +{ + struct idiagnl_msg *msg = (struct idiagnl_msg *) a; + char buf[64], buf2[64]; + + nl_dump(p, "\nfamily: %s\n", nl_af2str(msg->idiag_family, buf, sizeof(buf))); + nl_dump(p, "state: %s\n", + idiagnl_state2str(msg->idiag_state, buf, sizeof(buf))); + nl_dump(p, "timer (%s, %s, retransmits: %d)\n", + idiagnl_timer2str(msg->idiag_timer, buf, sizeof(buf)), + nl_msec2str(msg->idiag_expires, buf2, sizeof(buf2)), + msg->idiag_retrans); + + nl_dump(p, "source: %s:%d\n", nl_addr2str(msg->idiag_src, buf, sizeof(buf)), + ntohs(msg->idiag_sport)); + nl_dump(p, "destination: %s:%d\n", nl_addr2str(msg->idiag_dst, buf, sizeof(buf)), + ntohs(msg->idiag_dport)); + + nl_dump(p, "ifindex: %d\n", msg->idiag_ifindex); + nl_dump(p, "rqueue: %-6d wqueue: %-6d\n", msg->idiag_rqueue, msg->idiag_wqueue); + nl_dump(p, "uid %d\n", msg->idiag_uid); + nl_dump(p, "inode %d\n", msg->idiag_inode); + if (msg->idiag_shutdown) { + nl_dump(p, "socket shutdown: %s\n", + idiagnl_shutdown2str(msg->idiag_shutdown, + buf, sizeof(buf))); + } + + nl_dump(p, "tos: 0x%x\n", msg->idiag_tos); + nl_dump(p, "traffic class: %d\n", msg->idiag_tclass); + nl_dump(p, "congestion algorithm: %s\n", msg->idiag_cong ? msg->idiag_cong : ""); +} + +static void idiag_msg_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + struct idiagnl_msg *msg = (struct idiagnl_msg *) obj; + char buf[64]; + + idiag_msg_dump_details(obj, p); + + nl_dump(p, "tcp info: [\n"); + nl_dump(p, "\tsocket state: %s\n", + idiagnl_state2str(msg->idiag_tcpinfo.tcpi_state, + buf, sizeof(buf))); + nl_dump(p, "\ttcp state: %s\n", + idiagnl_tcpstate2str(msg->idiag_tcpinfo.tcpi_ca_state, + buf, sizeof(buf))); + nl_dump(p, "\tretransmits: %d\n", + msg->idiag_tcpinfo.tcpi_retransmits); + nl_dump(p, "\tprobes: %d\n", + msg->idiag_tcpinfo.tcpi_probes); + nl_dump(p, "\tbackoff: %d\n", + msg->idiag_tcpinfo.tcpi_backoff); + nl_dump(p, "\toptions: %s\n", + idiagnl_tcpopts2str(msg->idiag_tcpinfo.tcpi_options, + buf, sizeof(buf))); + nl_dump(p, "\tsnd_wscale: %d\n", msg->idiag_tcpinfo.tcpi_snd_wscale); + nl_dump(p, "\trcv_wscale: %d\n", msg->idiag_tcpinfo.tcpi_rcv_wscale); + nl_dump(p, "\trto: %d\n", msg->idiag_tcpinfo.tcpi_rto); + nl_dump(p, "\tato: %d\n", msg->idiag_tcpinfo.tcpi_ato); + nl_dump(p, "\tsnd_mss: %s\n", nl_size2str(msg->idiag_tcpinfo.tcpi_snd_mss, + buf, sizeof(buf))); + nl_dump(p, "\trcv_mss: %s\n", nl_size2str(msg->idiag_tcpinfo.tcpi_rcv_mss, + buf, sizeof(buf))); + nl_dump(p, "\tunacked: %d\n", msg->idiag_tcpinfo.tcpi_unacked); + nl_dump(p, "\tsacked: %d\n", msg->idiag_tcpinfo.tcpi_sacked); + + nl_dump(p, "\tlost: %d\n", msg->idiag_tcpinfo.tcpi_lost); + nl_dump(p, "\tretransmit segments: %d\n", + msg->idiag_tcpinfo.tcpi_retrans); + nl_dump(p, "\tfackets: %d\n", + msg->idiag_tcpinfo.tcpi_fackets); + nl_dump(p, "\tlast data sent: %s\n", + nl_msec2str(msg->idiag_tcpinfo.tcpi_last_data_sent, buf, + sizeof(buf))); + nl_dump(p, "\tlast ack sent: %s\n", + nl_msec2str(msg->idiag_tcpinfo.tcpi_last_ack_sent, buf, sizeof(buf))); + nl_dump(p, "\tlast data recv: %s\n", + nl_msec2str(msg->idiag_tcpinfo.tcpi_last_data_recv, buf, + sizeof(buf))); + nl_dump(p, "\tlast ack recv: %s\n", + nl_msec2str(msg->idiag_tcpinfo.tcpi_last_ack_recv, buf, + sizeof(buf))); + nl_dump(p, "\tpath mtu: %s\n", + nl_size2str(msg->idiag_tcpinfo.tcpi_pmtu, buf, + sizeof(buf))); + nl_dump(p, "\trcv ss threshold: %d\n", + msg->idiag_tcpinfo.tcpi_rcv_ssthresh); + nl_dump(p, "\tsmoothed round trip time: %d\n", + msg->idiag_tcpinfo.tcpi_rtt); + nl_dump(p, "\tround trip time variation: %d\n", + msg->idiag_tcpinfo.tcpi_rttvar); + nl_dump(p, "\tsnd ss threshold: %s\n", + nl_size2str(msg->idiag_tcpinfo.tcpi_snd_ssthresh, buf, + sizeof(buf))); + nl_dump(p, "\tsend congestion window: %d\n", + msg->idiag_tcpinfo.tcpi_snd_cwnd); + nl_dump(p, "\tadvertised mss: %s\n", + nl_size2str(msg->idiag_tcpinfo.tcpi_advmss, buf, + sizeof(buf))); + nl_dump(p, "\treordering: %d\n", + msg->idiag_tcpinfo.tcpi_reordering); + nl_dump(p, "\trcv rround trip time: %d\n", + msg->idiag_tcpinfo.tcpi_rcv_rtt); + nl_dump(p, "\treceive queue space: %s\n", + nl_size2str(msg->idiag_tcpinfo.tcpi_rcv_space, buf, + sizeof(buf))); + nl_dump(p, "\ttotal retransmits: %d\n", + msg->idiag_tcpinfo.tcpi_total_retrans); + nl_dump(p, "]\n"); + + if (msg->idiag_meminfo) { + nl_dump(p, "meminfo: [\n"); + nl_dump(p, "\trmem: %s\n", + nl_size2str(msg->idiag_meminfo->idiag_rmem, + buf, + sizeof(buf))); + nl_dump(p, "\twmem: %s\n", + nl_size2str(msg->idiag_meminfo->idiag_wmem, + buf, + sizeof(buf))); + nl_dump(p, "\tfmem: %s\n", + nl_size2str(msg->idiag_meminfo->idiag_fmem, + buf, + sizeof(buf))); + nl_dump(p, "\ttmem: %s\n", + nl_size2str(msg->idiag_meminfo->idiag_tmem, + buf, + sizeof(buf))); + nl_dump(p, "]\n"); + } + + if (msg->idiag_vegasinfo) { + nl_dump(p, "vegasinfo: [\n"); + nl_dump(p, "\tvegas enabled: %d\n", + msg->idiag_vegasinfo->tcpv_enabled); + if (msg->idiag_vegasinfo->tcpv_enabled) { + nl_dump(p, "\trtt cnt: %d", + msg->idiag_vegasinfo->tcpv_rttcnt); + nl_dump(p, "\trtt (propagation delay): %d", + msg->idiag_vegasinfo->tcpv_rtt); + nl_dump(p, "\tmin rtt: %d", + msg->idiag_vegasinfo->tcpv_minrtt); + } + nl_dump(p, "]\n"); + } + + if (msg->ce_mask & IDIAGNL_ATTR_MEMINFO) { + nl_dump(p, "skmeminfo: [\n"); + nl_dump(p, "\trmem alloc: %d\n", + msg->idiag_skmeminfo[SK_MEMINFO_RMEM_ALLOC]); + nl_dump(p, "\trcv buf: %s\n", + nl_size2str(msg->idiag_skmeminfo[SK_MEMINFO_RCVBUF], + buf, sizeof(buf))); + nl_dump(p, "\twmem alloc: %d\n", + msg->idiag_skmeminfo[SK_MEMINFO_WMEM_ALLOC]); + nl_dump(p, "\tsnd buf: %s\n", + nl_size2str(msg->idiag_skmeminfo[SK_MEMINFO_SNDBUF], + buf, sizeof(buf))); + nl_dump(p, "\tfwd alloc: %d\n", + msg->idiag_skmeminfo[SK_MEMINFO_FWD_ALLOC]); + nl_dump(p, "\twmem queued: %s\n", + nl_size2str(msg->idiag_skmeminfo[SK_MEMINFO_WMEM_QUEUED], + buf, sizeof(buf))); + nl_dump(p, "\topt mem: %d\n", + msg->idiag_skmeminfo[SK_MEMINFO_OPTMEM]); + nl_dump(p, "\tbacklog: %d\n", + msg->idiag_skmeminfo[SK_MEMINFO_BACKLOG]); + nl_dump(p, "]\n\n"); + } +} + +static void idiagnl_msg_free(struct nl_object *a) +{ + struct idiagnl_msg *msg = (struct idiagnl_msg *) a; + if (a == NULL) + return; + + free(msg->idiag_cong); + nl_addr_put(msg->idiag_src); + nl_addr_put(msg->idiag_dst); + idiagnl_meminfo_put(msg->idiag_meminfo); + idiagnl_vegasinfo_put(msg->idiag_vegasinfo); +} + +static int idiagnl_msg_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct idiagnl_msg *dst = (struct idiagnl_msg *) _dst; + struct idiagnl_msg *src = (struct idiagnl_msg *) _src; + + dst->idiag_src = NULL; + dst->idiag_dst = NULL; + dst->idiag_cong = NULL; + dst->idiag_meminfo = NULL; + dst->idiag_vegasinfo = NULL; + dst->ce_mask &= ~(IDIAGNL_ATTR_CONG | + IDIAGNL_ATTR_SRC | + IDIAGNL_ATTR_DST | + IDIAGNL_ATTR_MEMINFO | + IDIAGNL_ATTR_VEGASINFO); + + if (src->idiag_cong) { + if (!(dst->idiag_cong = strdup(src->idiag_cong))) + return -NLE_NOMEM; + dst->ce_mask |= IDIAGNL_ATTR_CONG; + } + + if (src->idiag_src) { + if (!(dst->idiag_src = nl_addr_clone(src->idiag_src))) + return -NLE_NOMEM; + dst->ce_mask |= IDIAGNL_ATTR_SRC; + } + + if (src->idiag_dst) { + if (!(dst->idiag_dst = nl_addr_clone(src->idiag_dst))) + return -NLE_NOMEM; + dst->ce_mask |= IDIAGNL_ATTR_DST; + } + + if (src->idiag_meminfo) { + if (!(dst->idiag_meminfo = (struct idiagnl_meminfo *) nl_object_clone((struct nl_object *) src->idiag_meminfo))) + return -NLE_NOMEM; + dst->ce_mask |= IDIAGNL_ATTR_MEMINFO; + } + + if (src->idiag_vegasinfo) { + if (!(dst->idiag_vegasinfo = (struct idiagnl_vegasinfo *) nl_object_clone((struct nl_object *) src->idiag_vegasinfo))) + return -NLE_NOMEM; + dst->ce_mask |= IDIAGNL_ATTR_VEGASINFO; + } + + return 0; +} + +static struct nla_policy ext_policy[INET_DIAG_MAX+1] = { + [INET_DIAG_MEMINFO] = { .minlen = sizeof(struct inet_diag_meminfo) }, + [INET_DIAG_INFO] = { .minlen = sizeof(struct tcp_info) }, + [INET_DIAG_VEGASINFO] = { .minlen = sizeof(struct tcpvegas_info) }, + [INET_DIAG_CONG] = { .type = NLA_STRING }, + [INET_DIAG_TOS] = { .type = NLA_U8 }, + [INET_DIAG_TCLASS] = { .type = NLA_U8 }, + /* Older kernel doesn't have SK_MEMINFO_BACKLOG */ + [INET_DIAG_SKMEMINFO] = { .minlen = (sizeof(uint32_t) * (SK_MEMINFO_OPTMEM + 1)) }, + [INET_DIAG_SHUTDOWN] = { .type = NLA_U8 }, +}; + +int idiagnl_msg_parse(struct nlmsghdr *nlh, struct idiagnl_msg **result) +{ + struct idiagnl_msg *msg = NULL; + struct inet_diag_msg *raw_msg = NULL; + struct nl_addr *src = NULL, *dst = NULL; + struct nlattr *tb[INET_DIAG_MAX+1]; + int err = 0; + + msg = idiagnl_msg_alloc(); + if (!msg) + goto errout_nomem; + + err = nlmsg_parse(nlh, sizeof(struct inet_diag_msg), tb, INET_DIAG_MAX, + ext_policy); + if (err < 0) + goto errout; + + raw_msg = nlmsg_data(nlh); + msg->idiag_family = raw_msg->idiag_family; + msg->idiag_state = raw_msg->idiag_state; + msg->idiag_timer = raw_msg->idiag_timer; + msg->idiag_retrans = raw_msg->idiag_retrans; + msg->idiag_expires = raw_msg->idiag_expires; + msg->idiag_rqueue = raw_msg->idiag_rqueue; + msg->idiag_wqueue = raw_msg->idiag_wqueue; + msg->idiag_uid = raw_msg->idiag_uid; + msg->idiag_inode = raw_msg->idiag_inode; + msg->idiag_sport = raw_msg->id.idiag_sport; + msg->idiag_dport = raw_msg->id.idiag_dport; + msg->idiag_ifindex = raw_msg->id.idiag_if; + + msg->ce_mask = (IDIAGNL_ATTR_FAMILY | + IDIAGNL_ATTR_STATE | + IDIAGNL_ATTR_TIMER | + IDIAGNL_ATTR_RETRANS | + IDIAGNL_ATTR_EXPIRES | + IDIAGNL_ATTR_RQUEUE | + IDIAGNL_ATTR_WQUEUE | + IDIAGNL_ATTR_UID | + IDIAGNL_ATTR_INODE | + IDIAGNL_ATTR_SPORT | + IDIAGNL_ATTR_DPORT | + IDIAGNL_ATTR_IFINDEX); + + dst = nl_addr_build(raw_msg->idiag_family, raw_msg->id.idiag_dst, + sizeof(raw_msg->id.idiag_dst)); + if (!dst) + goto errout_nomem; + + err = idiagnl_msg_set_dst(msg, dst); + if (err < 0) + goto errout; + + nl_addr_put(dst); + + src = nl_addr_build(raw_msg->idiag_family, raw_msg->id.idiag_src, + sizeof(raw_msg->id.idiag_src)); + if (!src) + goto errout_nomem; + + err = idiagnl_msg_set_src(msg, src); + if (err < 0) + goto errout; + + nl_addr_put(src); + + if (tb[INET_DIAG_TOS]) { + msg->idiag_tos = nla_get_u8(tb[INET_DIAG_TOS]); + msg->ce_mask |= IDIAGNL_ATTR_TOS; + } + + if (tb[INET_DIAG_TCLASS]) { + msg->idiag_tclass = nla_get_u8(tb[INET_DIAG_TCLASS]); + msg->ce_mask |= IDIAGNL_ATTR_TCLASS; + } + + if (tb[INET_DIAG_SHUTDOWN]) { + msg->idiag_shutdown = nla_get_u8(tb[INET_DIAG_SHUTDOWN]); + msg->ce_mask |= IDIAGNL_ATTR_SHUTDOWN; + } + + if (tb[INET_DIAG_CONG]) { + msg->idiag_cong = nla_strdup(tb[INET_DIAG_CONG]); + msg->ce_mask |= IDIAGNL_ATTR_CONG; + } + + if (tb[INET_DIAG_INFO]) { + nla_memcpy(&msg->idiag_tcpinfo, tb[INET_DIAG_INFO], + sizeof(msg->idiag_tcpinfo)); + msg->ce_mask |= IDIAGNL_ATTR_TCPINFO; + } + + if (tb[INET_DIAG_MEMINFO]) { + struct idiagnl_meminfo *minfo = idiagnl_meminfo_alloc(); + struct inet_diag_meminfo *raw_minfo = NULL; + + if (!minfo) + goto errout_nomem; + + raw_minfo = (struct inet_diag_meminfo *) + nla_data(tb[INET_DIAG_MEMINFO]); + + idiagnl_meminfo_set_rmem(minfo, raw_minfo->idiag_rmem); + idiagnl_meminfo_set_wmem(minfo, raw_minfo->idiag_wmem); + idiagnl_meminfo_set_fmem(minfo, raw_minfo->idiag_fmem); + idiagnl_meminfo_set_tmem(minfo, raw_minfo->idiag_tmem); + + msg->idiag_meminfo = minfo; + msg->ce_mask |= IDIAGNL_ATTR_MEMINFO; + } + + if (tb[INET_DIAG_VEGASINFO]) { + struct idiagnl_vegasinfo *vinfo = idiagnl_vegasinfo_alloc(); + struct tcpvegas_info *raw_vinfo = NULL; + + if (!vinfo) + goto errout_nomem; + + raw_vinfo = (struct tcpvegas_info *) + nla_data(tb[INET_DIAG_VEGASINFO]); + + idiagnl_vegasinfo_set_enabled(vinfo, raw_vinfo->tcpv_enabled); + idiagnl_vegasinfo_set_rttcnt(vinfo, raw_vinfo->tcpv_rttcnt); + idiagnl_vegasinfo_set_rtt(vinfo, raw_vinfo->tcpv_rtt); + idiagnl_vegasinfo_set_minrtt(vinfo, raw_vinfo->tcpv_minrtt); + + msg->idiag_vegasinfo = vinfo; + msg->ce_mask |= IDIAGNL_ATTR_VEGASINFO; + } + + if (tb[INET_DIAG_SKMEMINFO]) { + nla_memcpy(&msg->idiag_skmeminfo, tb[INET_DIAG_SKMEMINFO], + sizeof(msg->idiag_skmeminfo)); + msg->ce_mask |= IDIAGNL_ATTR_SKMEMINFO; + } + + *result = msg; + return 0; + +errout: + idiagnl_msg_put(msg); + return err; + +errout_nomem: + err = -NLE_NOMEM; + goto errout; +} + +static const struct trans_tbl idiagnl_attrs[] = { + __ADD(IDIAGNL_ATTR_FAMILY, family), + __ADD(IDIAGNL_ATTR_STATE, state), + __ADD(IDIAGNL_ATTR_TIMER, timer), + __ADD(IDIAGNL_ATTR_RETRANS, retrans), + __ADD(IDIAGNL_ATTR_SPORT, sport), + __ADD(IDIAGNL_ATTR_DPORT, dport), + __ADD(IDIAGNL_ATTR_SRC, src), + __ADD(IDIAGNL_ATTR_DST, dst), + __ADD(IDIAGNL_ATTR_IFINDEX, ifindex), + __ADD(IDIAGNL_ATTR_EXPIRES, expires), + __ADD(IDIAGNL_ATTR_RQUEUE, rqueue), + __ADD(IDIAGNL_ATTR_WQUEUE, wqueue), + __ADD(IDIAGNL_ATTR_UID, uid), + __ADD(IDIAGNL_ATTR_INODE, inode), + __ADD(IDIAGNL_ATTR_TOS, tos), + __ADD(IDIAGNL_ATTR_TCLASS, tclass), + __ADD(IDIAGNL_ATTR_SHUTDOWN, shutdown), + __ADD(IDIAGNL_ATTR_CONG, cong), + __ADD(IDIAGNL_ATTR_MEMINFO, meminfo), + __ADD(IDIAGNL_ATTR_VEGASINFO, vegasinfo), + __ADD(IDIAGNL_ATTR_TCPINFO, tcpinfo), + __ADD(IDIAGNL_ATTR_SKMEMINFO, skmeminfo), +}; + +static char *_idiagnl_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, idiagnl_attrs, + ARRAY_SIZE(idiagnl_attrs)); +} + +static uint64_t idiagnl_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct idiagnl_msg *a = (struct idiagnl_msg *) _a; + struct idiagnl_msg *b = (struct idiagnl_msg *) _b; + uint64_t diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(IDIAGNL_ATTR_FAMILY, a->idiag_family != b->idiag_family); + diff |= _DIFF(IDIAGNL_ATTR_STATE, a->idiag_state != b->idiag_state); + diff |= _DIFF(IDIAGNL_ATTR_TIMER, a->idiag_timer != b->idiag_timer); + diff |= _DIFF(IDIAGNL_ATTR_RETRANS, + a->idiag_retrans != b->idiag_retrans); + diff |= _DIFF(IDIAGNL_ATTR_SPORT, a->idiag_sport != b->idiag_sport); + diff |= _DIFF(IDIAGNL_ATTR_DPORT, a->idiag_dport != b->idiag_dport); + diff |= _DIFF(IDIAGNL_ATTR_SRC, + nl_addr_cmp(a->idiag_src, b->idiag_src)); + diff |= _DIFF(IDIAGNL_ATTR_DST, + nl_addr_cmp(a->idiag_dst, b->idiag_dst)); + diff |= _DIFF(IDIAGNL_ATTR_IFINDEX, + a->idiag_ifindex != b->idiag_ifindex); + diff |= _DIFF(IDIAGNL_ATTR_EXPIRES, + a->idiag_expires != b->idiag_expires); + diff |= _DIFF(IDIAGNL_ATTR_RQUEUE, a->idiag_rqueue != b->idiag_rqueue); + diff |= _DIFF(IDIAGNL_ATTR_WQUEUE, a->idiag_wqueue != b->idiag_wqueue); + diff |= _DIFF(IDIAGNL_ATTR_UID, a->idiag_uid != b->idiag_uid); + diff |= _DIFF(IDIAGNL_ATTR_INODE, a->idiag_inode != b->idiag_inode); + diff |= _DIFF(IDIAGNL_ATTR_TOS, a->idiag_tos != b->idiag_tos); + diff |= _DIFF(IDIAGNL_ATTR_TCLASS, a->idiag_tclass != b->idiag_tclass); + diff |= _DIFF(IDIAGNL_ATTR_SHUTDOWN, + a->idiag_shutdown != b->idiag_shutdown); + diff |= _DIFF(IDIAGNL_ATTR_CONG, strcmp(a->idiag_cong, b->idiag_cong)); + diff |= _DIFF(IDIAGNL_ATTR_MEMINFO, + nl_object_diff((struct nl_object *)a->idiag_meminfo, + (struct nl_object *)b->idiag_meminfo)); + diff |= _DIFF(IDIAGNL_ATTR_VEGASINFO, + nl_object_diff((struct nl_object *)a->idiag_vegasinfo, + (struct nl_object *)b->idiag_vegasinfo)); + diff |= _DIFF(IDIAGNL_ATTR_TCPINFO, + memcmp(&a->idiag_tcpinfo, &b->idiag_tcpinfo, + sizeof(a->idiag_tcpinfo))); + diff |= _DIFF(IDIAGNL_ATTR_SKMEMINFO, + memcmp(a->idiag_skmeminfo, b->idiag_skmeminfo, + sizeof(a->idiag_skmeminfo))); +#undef _DIFF + + return diff; +} + +static void idiagnl_keygen(struct nl_object *obj, uint32_t *hashkey, + uint32_t table_sz) +{ + struct idiagnl_msg *msg = (struct idiagnl_msg *)obj; + unsigned int key_sz; + struct idiagnl_hash_key { + uint8_t family; + uint32_t src_hash; + uint32_t dst_hash; + uint16_t sport; + uint16_t dport; + } _nl_packed key; + + key_sz = sizeof(key); + key.family = msg->idiag_family; + key.src_hash = 0; + key.dst_hash = 0; + key.sport = msg->idiag_sport; + key.dport = msg->idiag_dport; + + if (msg->idiag_src) { + key.src_hash = nl_hash (nl_addr_get_binary_addr(msg->idiag_src), + nl_addr_get_len(msg->idiag_src), 0); + } + if (msg->idiag_dst) { + key.dst_hash = nl_hash (nl_addr_get_binary_addr(msg->idiag_dst), + nl_addr_get_len(msg->idiag_dst), 0); + } + + *hashkey = nl_hash(&key, key_sz, 0) % table_sz; + + NL_DBG(5, "idiagnl %p key (fam %d src_hash %d dst_hash %d sport %d dport %d) keysz %d, hash 0x%x\n", + msg, key.family, key.src_hash, key.dst_hash, key.sport, key.dport, key_sz, *hashkey); + + return; +} + +/** @cond SKIP */ +struct nl_object_ops idiagnl_msg_obj_ops = { + .oo_name = "idiag/idiag_msg", + .oo_size = sizeof(struct idiagnl_msg), + .oo_free_data = idiagnl_msg_free, + .oo_clone = idiagnl_msg_clone, + .oo_dump = { + [NL_DUMP_LINE] = idiag_msg_dump_line, + [NL_DUMP_DETAILS] = idiag_msg_dump_details, + [NL_DUMP_STATS] = idiag_msg_dump_stats, + }, + .oo_compare = idiagnl_compare, + .oo_keygen = idiagnl_keygen, + .oo_attrs2str = _idiagnl_attrs2str, + .oo_id_attrs = (IDIAGNL_ATTR_FAMILY | + IDIAGNL_ATTR_SRC | + IDIAGNL_ATTR_DST | + IDIAGNL_ATTR_SPORT | + IDIAGNL_ATTR_DPORT), +}; +/** @endcond */ + +/** @} */ diff --git a/libnl/lib/idiag/idiag_req_obj.c b/libnl/lib/idiag/idiag_req_obj.c new file mode 100644 index 0000000..cc23a80 --- /dev/null +++ b/libnl/lib/idiag/idiag_req_obj.c @@ -0,0 +1,272 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Sassano Systems LLC + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include + +#include "nl-priv-dynamic-core/object-api.h" + +struct idiagnl_req { + NLHDR_COMMON + + uint8_t idiag_family; + uint8_t idiag_ext; + struct nl_addr * idiag_src; + struct nl_addr * idiag_dst; + uint32_t idiag_ifindex; + uint32_t idiag_states; + uint32_t idiag_dbs; +}; + +/** + * @ingroup idiag + * @defgroup idiagnl_req Inet Diag Requests + * + * @details + * @idiagnl_doc{idiagnl_req, Inet Diag Request Documentation} + * @{ + */ +struct idiagnl_req *idiagnl_req_alloc(void) +{ + return (struct idiagnl_req *) nl_object_alloc(&idiagnl_req_obj_ops); +} + +void idiagnl_req_get(struct idiagnl_req *req) +{ + nl_object_get((struct nl_object *) req); +} + +void idiagnl_req_put(struct idiagnl_req *req) +{ + nl_object_put((struct nl_object *) req); +} + +/** + * @name Attributes + * @{ + */ + +uint8_t idiagnl_req_get_family(const struct idiagnl_req *req) +{ + return req->idiag_family; +} + +void idiagnl_req_set_family(struct idiagnl_req *req, uint8_t family) +{ + req->idiag_family = family; +} + +uint8_t idiagnl_req_get_ext(const struct idiagnl_req *req) +{ + return req->idiag_ext; +} + +void idiagnl_req_set_ext(struct idiagnl_req *req, uint8_t ext) +{ + req->idiag_ext = ext; +} + +uint32_t idiagnl_req_get_ifindex(const struct idiagnl_req *req) +{ + return req->idiag_ifindex; +} + +void idiagnl_req_set_ifindex(struct idiagnl_req *req, uint32_t ifindex) +{ + req->idiag_ifindex = ifindex; +} + +uint32_t idiagnl_req_get_states(const struct idiagnl_req *req) +{ + return req->idiag_states; +} + +void idiagnl_req_set_states(struct idiagnl_req *req, uint32_t states) +{ + req->idiag_states = states; +} + +uint32_t idiagnl_req_get_dbs(const struct idiagnl_req *req) +{ + return req->idiag_dbs; +} + +void idiagnl_req_set_dbs(struct idiagnl_req *req, uint32_t dbs) +{ + req->idiag_dbs = dbs; +} + +struct nl_addr *idiagnl_req_get_src(const struct idiagnl_req *req) +{ + return req->idiag_src; +} + +int idiagnl_req_set_src(struct idiagnl_req *req, struct nl_addr *addr) +{ + if (req->idiag_src) + nl_addr_put(req->idiag_src); + + nl_addr_get(addr); + req->idiag_src = addr; + + return 0; +} + +struct nl_addr *idiagnl_req_get_dst(const struct idiagnl_req *req) +{ + return req->idiag_dst; +} + +int idiagnl_req_set_dst(struct idiagnl_req *req, struct nl_addr *addr) +{ + if (req->idiag_dst) + nl_addr_put(req->idiag_dst); + + nl_addr_get(addr); + req->idiag_dst = addr; + + return 0; +} + +/** @} */ + +static void idiag_req_dump_line(struct nl_object *a, struct nl_dump_params *p) +{ + struct idiagnl_req *req = (struct idiagnl_req *) a; + char buf[64] = { 0 }; + + nl_dump_line(p, "%s ", nl_af2str(req->idiag_family, buf, sizeof(buf))); + nl_dump(p, "src %s ", nl_addr2str(req->idiag_src, buf, sizeof(buf))); + nl_dump(p, "dst %s ", nl_addr2str(req->idiag_dst, buf, sizeof(buf))); + nl_dump(p, "iif %d ", req->idiag_ifindex); + nl_dump(p, "\n"); +} + +static void idiag_req_dump_details(struct nl_object *a, struct nl_dump_params *p) +{ + struct idiagnl_req *req = (struct idiagnl_req *) a; + char buf[64]; + + nl_dump_line(p, " "); + nl_dump(p, "%s ", nl_af2str(req->idiag_family, buf, sizeof(buf))); + nl_dump(p, "exts %s ", + idiagnl_exts2str(req->idiag_ext, buf, sizeof(buf))); + nl_dump(p, "src %s ", nl_addr2str(req->idiag_src, buf, sizeof(buf))); + nl_dump(p, "dst %s ", nl_addr2str(req->idiag_dst, buf, sizeof(buf))); + nl_dump(p, "iif %d ", req->idiag_ifindex); + nl_dump(p, "states %s ", idiagnl_state2str(req->idiag_states, buf, + sizeof(buf))); + nl_dump(p, "dbs %d", req->idiag_dbs); + nl_dump(p, "\n"); +} + +static void idiag_req_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + idiag_req_dump_details(obj, p); +} + +static void idiagnl_req_free(struct nl_object *a) +{ + struct idiagnl_req *req = (struct idiagnl_req *) a; + if (a == NULL) + return; + + nl_addr_put(req->idiag_src); + nl_addr_put(req->idiag_dst); +} + +static int idiagnl_req_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct idiagnl_req *dst = (struct idiagnl_req *) _dst; + struct idiagnl_req *src = (struct idiagnl_req *) _src; + + src->idiag_src = NULL; + src->idiag_dst = NULL; + + if (src->idiag_src) + if (!(dst->idiag_src = nl_addr_clone(src->idiag_src))) + return -NLE_NOMEM; + + if (src->idiag_dst) + if (!(dst->idiag_dst = nl_addr_clone(src->idiag_dst))) + return -NLE_NOMEM; + + return 0; +} + +int idiagnl_req_parse(struct nlmsghdr *nlh, struct idiagnl_req **result) +{ + struct idiagnl_req *req = NULL; + struct inet_diag_req *raw_req = NULL; + struct nl_addr *src = NULL, *dst = NULL; + int err = 0; + + req = idiagnl_req_alloc(); + if (!req) + goto errout_nomem; + + raw_req = nlmsg_data(nlh); + req->idiag_family = raw_req->idiag_family; + req->idiag_ext = raw_req->idiag_ext; + req->idiag_states = raw_req->idiag_states; + req->idiag_dbs = raw_req->idiag_dbs; + req->idiag_ifindex = raw_req->id.idiag_if; + + dst = nl_addr_build(raw_req->idiag_family, raw_req->id.idiag_dst, + sizeof(raw_req->id.idiag_dst)); + if (!dst) + goto errout_nomem; + + err = idiagnl_req_set_dst(req, dst); + if (err < 0) + goto errout; + + nl_addr_put(dst); + + src = nl_addr_build(raw_req->idiag_family, raw_req->id.idiag_src, + sizeof(raw_req->id.idiag_src)); + if (!src) + goto errout_nomem; + + err = idiagnl_req_set_src(req, src); + if (err < 0) + goto errout; + + nl_addr_put(src); + + *result = req; + return 0; + +errout: + idiagnl_req_put(req); + return err; + +errout_nomem: + err = -NLE_NOMEM; + goto errout; +} + +/** @cond SKIP */ +struct nl_object_ops idiagnl_req_obj_ops = { + .oo_name = "idiag/idiag_req", + .oo_size = sizeof(struct idiagnl_req), + .oo_free_data = idiagnl_req_free, + .oo_clone = idiagnl_req_clone, + .oo_dump = { + [NL_DUMP_LINE] = idiag_req_dump_line, + [NL_DUMP_DETAILS] = idiag_req_dump_details, + [NL_DUMP_STATS] = idiag_req_dump_stats, + }, +}; +/** @endcond */ + +/** @} */ diff --git a/libnl/lib/idiag/idiag_vegasinfo_obj.c b/libnl/lib/idiag/idiag_vegasinfo_obj.c new file mode 100644 index 0000000..f9e9408 --- /dev/null +++ b/libnl/lib/idiag/idiag_vegasinfo_obj.c @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Sassano Systems LLC + */ + +#include "nl-default.h" + +#include + +#include "nl-idiag.h" + +/** + * @ingroup idiag + * @defgroup idiagnl_vegasinfo Inet Diag TCP Vegas Info + * + * @details + * @idiagnl_doc{idiagnl_vegasinfo, Inet Diag TCP Vegas Info Documentation} + * @{ + */ +struct idiagnl_vegasinfo *idiagnl_vegasinfo_alloc(void) +{ + return (struct idiagnl_vegasinfo *) nl_object_alloc(&idiagnl_vegasinfo_obj_ops); +} + +void idiagnl_vegasinfo_get(struct idiagnl_vegasinfo *vinfo) +{ + nl_object_get((struct nl_object *) vinfo); +} + +void idiagnl_vegasinfo_put(struct idiagnl_vegasinfo *vinfo) +{ + nl_object_put((struct nl_object *) vinfo); +} + +/** + * @name Attributes + * @{ + */ +uint32_t idiagnl_vegasinfo_get_enabled(const struct idiagnl_vegasinfo *vinfo) +{ + return vinfo->tcpv_enabled; +} + +void idiagnl_vegasinfo_set_enabled(struct idiagnl_vegasinfo *vinfo, uint32_t + enabled) +{ + vinfo->tcpv_enabled = enabled; +} + +uint32_t idiagnl_vegasinfo_get_rttcnt(const struct idiagnl_vegasinfo *vinfo) +{ + return vinfo->tcpv_rttcnt; +} + +void idiagnl_vegasinfo_set_rttcnt(struct idiagnl_vegasinfo *vinfo, uint32_t + rttcnt) +{ + vinfo->tcpv_rttcnt = rttcnt; +} + +uint32_t idiagnl_vegasinfo_get_rtt(const struct idiagnl_vegasinfo *vinfo) +{ + return vinfo->tcpv_rtt; +} + +void idiagnl_vegasinfo_set_rtt(struct idiagnl_vegasinfo *vinfo, uint32_t rtt) +{ + vinfo->tcpv_rtt = rtt; +} + +uint32_t idiagnl_vegasinfo_get_minrtt(const struct idiagnl_vegasinfo *vinfo) +{ + return vinfo->tcpv_minrtt; +} + +void idiagnl_vegasinfo_set_minrtt(struct idiagnl_vegasinfo *vinfo, uint32_t + minrtt) +{ + vinfo->tcpv_minrtt = minrtt; +} +/** @} */ + +/** @cond SKIP */ +static uint64_t idiagnl_vegasinfo_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct idiagnl_vegasinfo *a = (struct idiagnl_vegasinfo *) _a; + struct idiagnl_vegasinfo *b = (struct idiagnl_vegasinfo *) _b; + + /* vegasinfo is a very simple object. It has no attribe flags (ce_mask), + * hence compare just returns 0 or 1, not a bit mask of attributes. */ + return a->tcpv_enabled != b->tcpv_enabled || + a->tcpv_rttcnt != b->tcpv_rttcnt || + a->tcpv_rtt != b->tcpv_rtt || + a->tcpv_minrtt != b->tcpv_minrtt; +} + +struct nl_object_ops idiagnl_vegasinfo_obj_ops = { + .oo_name = "idiag/idiag_vegasinfo", + .oo_size = sizeof(struct idiagnl_vegasinfo), + .oo_compare = idiagnl_vegasinfo_compare, +}; +/** @endcond */ +/** @} */ diff --git a/libnl/lib/idiag/nl-idiag.h b/libnl/lib/idiag/nl-idiag.h new file mode 100644 index 0000000..3615bb3 --- /dev/null +++ b/libnl/lib/idiag/nl-idiag.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + * Copyright (c) 2013 Sassano Systems LLC + */ + +#ifndef __LIB_IDIAG_NL_IDIAG_H__ +#define __LIB_IDIAG_NL_IDIAG_H__ + +#include "nl-priv-dynamic-core/object-api.h" + +struct idiagnl_meminfo { + NLHDR_COMMON + + uint32_t idiag_rmem; + uint32_t idiag_wmem; + uint32_t idiag_fmem; + uint32_t idiag_tmem; +}; + +struct idiagnl_vegasinfo { + NLHDR_COMMON + + uint32_t tcpv_enabled; + uint32_t tcpv_rttcnt; + uint32_t tcpv_rtt; + uint32_t tcpv_minrtt; +}; + +#endif /* __LIB_IDIAG_NL_IDIAG_H__ */ diff --git a/libnl/lib/mpls.c b/libnl/lib/mpls.c new file mode 100644 index 0000000..f5b4f27 --- /dev/null +++ b/libnl/lib/mpls.c @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Adapted from mpls_ntop and mpls_pton copied from iproute2, + * lib/mpls_ntop.c and lib/mpls_pton.c + */ + +#include "nl-default.h" + +#include + +#include + +#include + +#include "mpls.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 < 0 || (unsigned)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); + } + + errno = EINVAL; + return NULL; +} + +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 (count + 1) * sizeof(struct mpls_label); + } + + /* Bad character in the address */ + if (*endp != '/') + return 0; + + name = endp + 1; + addr += 1; + } + + /* The address was too long */ + 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/libnl/lib/mpls.h b/libnl/lib/mpls.h new file mode 100644 index 0000000..591b3cc --- /dev/null +++ b/libnl/lib/mpls.h @@ -0,0 +1,7 @@ +#ifndef MPLS_H_ +#define MPLS_H_ + +extern const char *mpls_ntop(int af, const void *addr, char *buf, size_t buflen); +extern int mpls_pton(int af, const char *src, void *addr, size_t alen); + +#endif diff --git a/libnl/lib/msg.c b/libnl/lib/msg.c new file mode 100644 index 0000000..64e6b2f --- /dev/null +++ b/libnl/lib/msg.c @@ -0,0 +1,1006 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup core + * @defgroup msg Message Construction & Parsing + * Netlink Message Construction/Parsing Interface + * + * Related sections in the development guide: + * - @core_doc{_message_parsing_amp_construction,Message Parsing & Construction} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#include "nl-default.h" + +#include + +#include + +#include +#include +#include +#include + +#include "nl-core.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" +#include "nl-aux-core/nl-core.h" + +static size_t default_msg_size; /* GLOBAL! */ + +static void _nl_init init_msg_size(void) +{ + default_msg_size = getpagesize(); +} + +/** + * @name Size Calculations + * @{ + */ + +/** + * Calculates size of netlink message based on payload length. + * @arg payload Length of payload + * + * @return size of netlink message without padding. + */ +int nlmsg_size(int payload) +{ + return NLMSG_HDRLEN + payload; +} + +static int nlmsg_msg_size(int payload) +{ + return nlmsg_size(payload); +} + +/** + * Calculates size of netlink message including padding based on payload length + * @arg payload Length of payload + * + * This function is idential to nlmsg_size() + nlmsg_padlen(). + * + * @return Size of netlink message including padding. + */ +int nlmsg_total_size(int payload) +{ + return NLMSG_ALIGN(nlmsg_msg_size(payload)); +} + +/** + * Size of padding that needs to be added at end of message + * @arg payload Length of payload + * + * Calculates the number of bytes of padding which is required to be added to + * the end of the message to ensure that the next netlink message header begins + * properly aligned to NLMSG_ALIGNTO. + * + * @return Number of bytes of padding needed. + */ +int nlmsg_padlen(int payload) +{ + return nlmsg_total_size(payload) - nlmsg_msg_size(payload); +} + +/** @} */ + +/** + * @name Access to Message Payload + * @{ + */ + +/** + * Return pointer to message payload + * @arg nlh Netlink message header + * + * @return Pointer to start of message payload. + */ +void *nlmsg_data(const struct nlmsghdr *nlh) +{ + return (unsigned char *) nlh + NLMSG_HDRLEN; +} + +void *nlmsg_tail(const struct nlmsghdr *nlh) +{ + return (unsigned char *) nlh + NLMSG_ALIGN(nlh->nlmsg_len); +} + +/** + * Return length of message payload + * @arg nlh Netlink message header + * + * @return Length of message payload in bytes. + */ +int nlmsg_datalen(const struct nlmsghdr *nlh) +{ + return nlh->nlmsg_len - NLMSG_HDRLEN; +} + +static int nlmsg_len(const struct nlmsghdr *nlh) +{ + return nlmsg_datalen(nlh); +} + +/** @} */ + +/** + * @name Attribute Access + * @{ + */ + +/** + * head of attributes data + * @arg nlh netlink message header + * @arg hdrlen length of family specific header + */ +struct nlattr *nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen) +{ + unsigned char *data = nlmsg_data(nlh); + return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen)); +} + +/** + * length of attributes data + * @arg nlh netlink message header + * @arg hdrlen length of family specific header + */ +int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen) +{ + return _NL_MAX(nlmsg_len(nlh) - NLMSG_ALIGN(hdrlen), 0u); +} + +/** @} */ + +/** + * @name Message Parsing + * @{ + */ + +int nlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen) +{ + int s; + + s = nlmsg_msg_size(hdrlen); + if (s < 0 || nlh->nlmsg_len < ((unsigned)s)) + return 0; + + return 1; +} + +/** + * check if the netlink message fits into the remaining bytes + * @arg nlh netlink message header + * @arg remaining number of bytes remaining in message stream + */ +int nlmsg_ok(const struct nlmsghdr *nlh, int remaining) +{ + return (remaining >= (int)sizeof(struct nlmsghdr) && + nlh->nlmsg_len >= sizeof(struct nlmsghdr) && + nlh->nlmsg_len <= ((unsigned)remaining)); +} + +/** + * next netlink message in message stream + * @arg nlh netlink message header + * @arg remaining number of bytes remaining in message stream + * + * @returns the next netlink message in the message stream and + * decrements remaining by the size of the current message. + */ +struct nlmsghdr *nlmsg_next(struct nlmsghdr *nlh, int *remaining) +{ + int totlen = NLMSG_ALIGN(nlh->nlmsg_len); + + *remaining -= totlen; + + return (struct nlmsghdr *) ((unsigned char *) nlh + totlen); +} + +/** + * parse attributes of a netlink message + * @arg nlh netlink message header + * @arg hdrlen length of family specific header + * @arg tb destination array with maxtype+1 elements + * @arg maxtype maximum attribute type to be expected + * @arg policy validation policy + * + * See nla_parse() + */ +int nlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], + int maxtype, const struct nla_policy *policy) +{ + if (!nlmsg_valid_hdr(nlh, hdrlen)) + return -NLE_MSG_TOOSHORT; + + return nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen), + nlmsg_attrlen(nlh, hdrlen), policy); +} + +/** + * nlmsg_find_attr - find a specific attribute in a netlink message + * @arg nlh netlink message header + * @arg hdrlen length of familiy specific header + * @arg attrtype type of attribute to look for + * + * Returns the first attribute which matches the specified type. + */ +struct nlattr *nlmsg_find_attr(struct nlmsghdr *nlh, int hdrlen, int attrtype) +{ + return nla_find(nlmsg_attrdata(nlh, hdrlen), + nlmsg_attrlen(nlh, hdrlen), attrtype); +} + +/** + * nlmsg_validate - validate a netlink message including attributes + * @arg nlh netlinket message header + * @arg hdrlen length of familiy specific header + * @arg maxtype maximum attribute type to be expected + * @arg policy validation policy + */ +int nlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype, + const struct nla_policy *policy) +{ + if (!nlmsg_valid_hdr(nlh, hdrlen)) + return -NLE_MSG_TOOSHORT; + + return nla_validate(nlmsg_attrdata(nlh, hdrlen), + nlmsg_attrlen(nlh, hdrlen), maxtype, policy); +} + +/** @} */ + +/** + * @name Message Building/Access + * @{ + */ + +static struct nl_msg *__nlmsg_alloc(size_t len) +{ + struct nl_msg *nm; + + if (len < sizeof(struct nlmsghdr)) + len = sizeof(struct nlmsghdr); + + nm = calloc(1, sizeof(*nm)); + if (!nm) + goto errout; + + nm->nm_refcnt = 1; + + nm->nm_nlh = calloc(1, len); + if (!nm->nm_nlh) + goto errout; + + nm->nm_protocol = -1; + nm->nm_size = len; + nm->nm_nlh->nlmsg_len = nlmsg_total_size(0); + + NL_DBG(2, "msg %p: Allocated new message, maxlen=%zu\n", nm, len); + + return nm; +errout: + free(nm); + return NULL; +} + +/** + * Allocate a new netlink message with the default maximum payload size. + * + * Allocates a new netlink message without any further payload. The + * maximum payload size defaults to PAGESIZE or as otherwise specified + * with nlmsg_set_default_size(). + * + * @return Newly allocated netlink message or NULL. + */ +struct nl_msg *nlmsg_alloc(void) +{ + return __nlmsg_alloc(default_msg_size); +} + +/** + * Allocate a new netlink message with maximum payload size specified. + */ +struct nl_msg *nlmsg_alloc_size(size_t max) +{ + return __nlmsg_alloc(max); +} + +/** + * Allocate a new netlink message and inherit netlink message header + * @arg hdr Netlink message header template + * + * Allocates a new netlink message and inherits the original message + * header. If \a hdr is not NULL it will be used as a template for + * the netlink message header, otherwise the header is left blank. + * + * @return Newly allocated netlink message or NULL + */ +struct nl_msg *nlmsg_inherit(struct nlmsghdr *hdr) +{ + struct nl_msg *nm; + + nm = nlmsg_alloc(); + if (nm && hdr) { + struct nlmsghdr *new = nm->nm_nlh; + + new->nlmsg_type = hdr->nlmsg_type; + new->nlmsg_flags = hdr->nlmsg_flags; + new->nlmsg_seq = hdr->nlmsg_seq; + new->nlmsg_pid = hdr->nlmsg_pid; + } + + return nm; +} + +/** + * Allocate a new netlink message + * @arg nlmsgtype Netlink message type + * @arg flags Message flags. + * + * @return Newly allocated netlink message or NULL. + */ +struct nl_msg *nlmsg_alloc_simple(int nlmsgtype, int flags) +{ + struct nl_msg *msg; + struct nlmsghdr nlh = { + .nlmsg_type = nlmsgtype, + .nlmsg_flags = flags, + .nlmsg_seq = NL_AUTO_SEQ, + .nlmsg_pid = NL_AUTO_PID, + }; + + msg = nlmsg_inherit(&nlh); + if (msg) + NL_DBG(2, "msg %p: Allocated new simple message\n", msg); + + return msg; +} + +/** + * Set the default maximum message payload size for allocated messages + * @arg max Size of payload in bytes. + */ +void nlmsg_set_default_size(size_t max) +{ + size_t s; + + s = nlmsg_total_size(0); + if (max < s) + max = s; + + default_msg_size = max; +} + +/** + * Convert a netlink message received from a netlink socket to a nl_msg + * @arg hdr Netlink message received from netlink socket. + * + * Allocates a new netlink message and copies all of the data pointed to + * by \a hdr into the new message object. + * + * @return Newly allocated netlink message or NULL. + */ +struct nl_msg *nlmsg_convert(struct nlmsghdr *hdr) +{ + struct nl_msg *nm; + + nm = __nlmsg_alloc(NLMSG_ALIGN(hdr->nlmsg_len)); + if (!nm) + return NULL; + + memcpy(nm->nm_nlh, hdr, hdr->nlmsg_len); + + return nm; +} + +/** + * Reserve room for additional data in a netlink message + * @arg n netlink message + * @arg len length of additional data to reserve room for + * @arg pad number of bytes to align data to + * + * Reserves room for additional data at the tail of the an + * existing netlink message. Eventual padding required will + * be zeroed out. + * + * @return Pointer to start of additional data tailroom or NULL. + */ +void *nlmsg_reserve(struct nl_msg *n, size_t len, int pad) +{ + char *buf = (char *) n->nm_nlh; + size_t nlmsg_len = n->nm_nlh->nlmsg_len; + size_t tlen; + + if (len > n->nm_size) + return NULL; + + tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len; + + if ((tlen + nlmsg_len) > n->nm_size) + return NULL; + + buf += nlmsg_len; + n->nm_nlh->nlmsg_len += tlen; + + if (tlen > len) + memset(buf + len, 0, tlen - len); + + NL_DBG(2, "msg %p: Reserved %zu (%zu) bytes, pad=%d, nlmsg_len=%d\n", + n, tlen, len, pad, n->nm_nlh->nlmsg_len); + + return buf; +} + +/** + * Append data to tail of a netlink message + * @arg n netlink message + * @arg data data to add + * @arg len length of data + * @arg pad Number of bytes to align data to. + * + * Extends the netlink message as needed and appends the data of given + * length to the message. + * + * @return 0 on success or a negative error code + */ +int nlmsg_append(struct nl_msg *n, void *data, size_t len, int pad) +{ + void *tmp; + + tmp = nlmsg_reserve(n, len, pad); + if (tmp == NULL) + return -NLE_NOMEM; + + memcpy(tmp, data, len); + NL_DBG(2, "msg %p: Appended %zu bytes with padding %d\n", n, len, pad); + + return 0; +} + +/** + * Expand maximum payload size of a netlink message + * @arg n Netlink message. + * @arg newlen New maximum payload size. + * + * Reallocates the payload section of a netlink message and increases + * the maximum payload size of the message. + * + * @note Any pointers pointing to old payload block will be stale and + * need to be refetched. Therfore, do not expand while constructing + * nested attributes or while reserved data blocks are held. + * + * @return 0 on success or a negative error code. + */ +int nlmsg_expand(struct nl_msg *n, size_t newlen) +{ + void *tmp; + + if (newlen <= n->nm_size) + return -NLE_INVAL; + + tmp = realloc(n->nm_nlh, newlen); + if (tmp == NULL) + return -NLE_NOMEM; + + n->nm_nlh = tmp; + n->nm_size = newlen; + + return 0; +} + +/** + * Add a netlink message header to a netlink message + * @arg n netlink message + * @arg pid netlink process id or NL_AUTO_PID + * @arg seq sequence number of message or NL_AUTO_SEQ + * @arg type message type + * @arg payload length of message payload + * @arg flags message flags + * + * Adds or overwrites the netlink message header in an existing message + * object. If \a payload is greater-than zero additional room will be + * reserved, f.e. for family specific headers. It can be accesed via + * nlmsg_data(). + * + * @return A pointer to the netlink message header or NULL. + */ +struct nlmsghdr *nlmsg_put(struct nl_msg *n, uint32_t pid, uint32_t seq, + int type, int payload, int flags) +{ + struct nlmsghdr *nlh; + + if (n->nm_nlh->nlmsg_len < NLMSG_HDRLEN) + BUG(); + + nlh = (struct nlmsghdr *) n->nm_nlh; + nlh->nlmsg_type = type; + nlh->nlmsg_flags = flags; + nlh->nlmsg_pid = pid; + nlh->nlmsg_seq = seq; + + NL_DBG(2, "msg %p: Added netlink header type=%d, flags=%d, pid=%d, " + "seq=%d\n", n, type, flags, pid, seq); + + if (payload > 0 && + nlmsg_reserve(n, payload, NLMSG_ALIGNTO) == NULL) + return NULL; + + return nlh; +} + +/** + * Return actual netlink message + * @arg n netlink message + * + * Returns the actual netlink message casted to the type of the netlink + * message header. + * + * @return A pointer to the netlink message. + */ +struct nlmsghdr *nlmsg_hdr(struct nl_msg *n) +{ + return n->nm_nlh; +} + +/** + * Acquire a reference on a netlink message + * @arg msg message to acquire reference from + */ +void nlmsg_get(struct nl_msg *msg) +{ + msg->nm_refcnt++; + NL_DBG(4, "New reference to message %p, total %d\n", + msg, msg->nm_refcnt); +} + +/** + * Release a reference from an netlink message + * @arg msg message to release reference from + * + * Frees memory after the last reference has been released. + */ +void nlmsg_free(struct nl_msg *msg) +{ + if (!msg) + return; + + msg->nm_refcnt--; + NL_DBG(4, "Returned message reference %p, %d remaining\n", + msg, msg->nm_refcnt); + + if (msg->nm_refcnt < 0) + BUG(); + + if (msg->nm_refcnt <= 0) { + free(msg->nm_nlh); + NL_DBG(2, "msg %p: Freed\n", msg); + free(msg); + } +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nlmsg_set_proto(struct nl_msg *msg, int protocol) +{ + msg->nm_protocol = protocol; +} + +int nlmsg_get_proto(struct nl_msg *msg) +{ + return msg->nm_protocol; +} + +size_t nlmsg_get_max_size(struct nl_msg *msg) +{ + return msg->nm_size; +} + +void nlmsg_set_src(struct nl_msg *msg, struct sockaddr_nl *addr) +{ + memcpy(&msg->nm_src, addr, sizeof(*addr)); +} + +struct sockaddr_nl *nlmsg_get_src(struct nl_msg *msg) +{ + return &msg->nm_src; +} + +void nlmsg_set_dst(struct nl_msg *msg, struct sockaddr_nl *addr) +{ + memcpy(&msg->nm_dst, addr, sizeof(*addr)); +} + +struct sockaddr_nl *nlmsg_get_dst(struct nl_msg *msg) +{ + return &msg->nm_dst; +} + +void nlmsg_set_creds(struct nl_msg *msg, struct ucred *creds) +{ + memcpy(&msg->nm_creds, creds, sizeof(*creds)); + msg->nm_flags |= NL_MSG_CRED_PRESENT; +} + +struct ucred *nlmsg_get_creds(struct nl_msg *msg) +{ + if (msg->nm_flags & NL_MSG_CRED_PRESENT) + return &msg->nm_creds; + return NULL; +} + +/** @} */ + +/** + * @name Netlink Message Type Translations + * @{ + */ + +static const struct trans_tbl nl_msgtypes[] = { + __ADD(NLMSG_NOOP,NOOP), + __ADD(NLMSG_ERROR,ERROR), + __ADD(NLMSG_DONE,DONE), + __ADD(NLMSG_OVERRUN,OVERRUN), +}; + +char *nl_nlmsgtype2str(int type, char *buf, size_t size) +{ + return __type2str(type, buf, size, nl_msgtypes, + ARRAY_SIZE(nl_msgtypes)); +} + +int nl_str2nlmsgtype(const char *name) +{ + return __str2type(name, nl_msgtypes, ARRAY_SIZE(nl_msgtypes)); +} + +/** @} */ + +/** + * @name Netlink Message Flags Translations + * @{ + */ + +char *nl_nlmsg_flags2str(int flags, char *buf, size_t len) +{ + memset(buf, 0, len); + +#define PRINT_FLAG(f) \ + if (flags & NLM_F_##f) { \ + flags &= ~NLM_F_##f; \ + strncat(buf, #f, len - strlen(buf) - 1); \ + if (flags) \ + strncat(buf, ",", len - strlen(buf) - 1); \ + } + + PRINT_FLAG(REQUEST); + PRINT_FLAG(MULTI); + PRINT_FLAG(ACK); + PRINT_FLAG(ECHO); + PRINT_FLAG(ROOT); + PRINT_FLAG(MATCH); + PRINT_FLAG(ATOMIC); + PRINT_FLAG(REPLACE); + PRINT_FLAG(EXCL); + PRINT_FLAG(CREATE); + PRINT_FLAG(APPEND); + + if (flags) { + char s[32]; + snprintf(s, sizeof(s), "0x%x", flags); + strncat(buf, s, len - strlen(buf) - 1); + } +#undef PRINT_FLAG + + return buf; +} + +/** @} */ + +/** + * @name Direct Parsing + * @{ + */ + +/** @cond SKIP */ +struct dp_xdata { + void (*cb)(struct nl_object *, void *); + void *arg; +}; +/** @endcond */ + +static int parse_cb(struct nl_object *obj, struct nl_parser_param *p) +{ + struct dp_xdata *x = p->pp_arg; + + x->cb(obj, x->arg); + return 0; +} + +int nl_msg_parse(struct nl_msg *msg, void (*cb)(struct nl_object *, void *), + void *arg) +{ + struct nl_cache_ops *ops; + struct nl_parser_param p = { + .pp_cb = parse_cb + }; + struct dp_xdata x = { + .cb = cb, + .arg = arg, + }; + int err; + + ops = nl_cache_ops_associate_safe(nlmsg_get_proto(msg), + nlmsg_hdr(msg)->nlmsg_type); + if (ops == NULL) + return -NLE_MSGTYPE_NOSUPPORT; + p.pp_arg = &x; + + err = nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p); + nl_cache_ops_put(ops); + + return err; +} + +/** @} */ + +/** + * @name Dumping + * @{ + */ + +static void prefix_line(FILE *ofd, int prefix) +{ + int i; + + for (i = 0; i < prefix; i++) + fprintf(ofd, " "); +} + +static inline void dump_hex(FILE *ofd, char *start, int len, int prefix) +{ + int i, a, c, limit; + char ascii[21] = {0}; + + limit = 16 - (prefix * 2); + prefix_line(ofd, prefix); + fprintf(ofd, " "); + + for (i = 0, a = 0, c = 0; i < len; i++) { + int v = *(uint8_t *) (start + i); + + fprintf(ofd, "%02x ", v); + ascii[a++] = isprint(v) ? v : '.'; + + if (++c >= limit) { + fprintf(ofd, "%s\n", ascii); + if (i < (len - 1)) { + prefix_line(ofd, prefix); + fprintf(ofd, " "); + } + a = c = 0; + memset(ascii, 0, sizeof(ascii)); + } + } + + if (c != 0) { + for (i = 0; i < (limit - c); i++) + fprintf(ofd, " "); + fprintf(ofd, "%s\n", ascii); + } +} + +static void print_hdr(FILE *ofd, struct nl_msg *msg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct nl_cache_ops *ops; + struct nl_msgtype *mt; + char buf[128]; + + fprintf(ofd, " .nlmsg_len = %d\n", nlh->nlmsg_len); + + ops = nl_cache_ops_associate_safe(nlmsg_get_proto(msg), nlh->nlmsg_type); + if (ops) { + mt = nl_msgtype_lookup(ops, nlh->nlmsg_type); + if (!mt) + BUG(); + + snprintf(buf, sizeof(buf), "%s::%s", ops->co_name, mt->mt_name); + nl_cache_ops_put(ops); + } else + nl_nlmsgtype2str(nlh->nlmsg_type, buf, sizeof(buf)); + + fprintf(ofd, " .type = %d <%s>\n", nlh->nlmsg_type, buf); + fprintf(ofd, " .flags = %d <%s>\n", nlh->nlmsg_flags, + nl_nlmsg_flags2str(nlh->nlmsg_flags, buf, sizeof(buf))); + fprintf(ofd, " .seq = %d\n", nlh->nlmsg_seq); + fprintf(ofd, " .port = %d\n", nlh->nlmsg_pid); + +} + +static void print_genl_hdr(FILE *ofd, void *start) +{ + struct genlmsghdr *ghdr = start; + + fprintf(ofd, " [GENERIC NETLINK HEADER] %zu octets\n", GENL_HDRLEN); + fprintf(ofd, " .cmd = %u\n", ghdr->cmd); + fprintf(ofd, " .version = %u\n", ghdr->version); + fprintf(ofd, " .unused = %#x\n", ghdr->reserved); +} + +static void *print_genl_msg(struct nl_msg *msg, FILE *ofd, struct nlmsghdr *hdr, + struct nl_cache_ops *ops, int *payloadlen) +{ + char *data = nlmsg_data(hdr); + + if (*payloadlen < (int)GENL_HDRLEN) + return data; + + print_genl_hdr(ofd, data); + + *payloadlen -= GENL_HDRLEN; + data += GENL_HDRLEN; + + if (ops) { + int hdrsize = ops->co_hdrsize - GENL_HDRLEN; + + if (hdrsize > 0) { + if (*payloadlen < hdrsize) + return data; + + fprintf(ofd, " [HEADER] %d octets\n", hdrsize); + dump_hex(ofd, data, hdrsize, 0); + + *payloadlen -= hdrsize; + data += hdrsize; + } + } + + return data; +} + +static void dump_attr(FILE *ofd, struct nlattr *attr, int prefix) +{ + int len = nla_len(attr); + + dump_hex(ofd, nla_data(attr), len, prefix); +} + +static void dump_attrs(FILE *ofd, struct nlattr *attrs, int attrlen, + int prefix) +{ + int rem; + struct nlattr *nla; + + nla_for_each_attr(nla, attrs, attrlen, rem) { + int padlen, alen = nla_len(nla); + + prefix_line(ofd, prefix); + + if (nla->nla_type == 0) + fprintf(ofd, " [ATTR PADDING] %d octets\n", alen); + else + fprintf(ofd, " [ATTR %02d%s] %d octets\n", nla_type(nla), + nla_is_nested(nla) ? " NESTED" : "", + alen); + + if (nla_is_nested(nla)) + dump_attrs(ofd, nla_data(nla), alen, prefix+1); + else + dump_attr(ofd, nla, prefix); + + padlen = nla_padlen(alen); + if (padlen > 0) { + prefix_line(ofd, prefix); + fprintf(ofd, " [PADDING] %d octets\n", + padlen); + dump_hex(ofd, (char *) nla_data(nla) + alen, + padlen, prefix); + } + } + + if (rem) { + prefix_line(ofd, prefix); + fprintf(ofd, " [LEFTOVER] %d octets\n", rem); + } +} + +static void dump_error_msg(struct nl_msg *msg, FILE *ofd) +{ + struct nlmsghdr *hdr = nlmsg_hdr(msg); + struct nlmsgerr *err = nlmsg_data(hdr); + int l; + + fprintf(ofd, " [ERRORMSG] %zu octets\n", sizeof(*err)); + + l = nlmsg_len(hdr); + if (l >= 0 && ((unsigned)l) >= sizeof(*err)) { + struct nl_msg *errmsg; + + fprintf(ofd, " .error = %d \"%s\"\n", err->error, + nl_strerror_l(-err->error)); + fprintf(ofd, " [ORIGINAL MESSAGE] %zu octets\n", sizeof(*hdr)); + + errmsg = nlmsg_inherit(&err->msg); + print_hdr(ofd, errmsg); + nlmsg_free(errmsg); + } +} + +static void print_msg(struct nl_msg *msg, FILE *ofd, struct nlmsghdr *hdr) +{ + struct nl_cache_ops *ops; + int payloadlen = nlmsg_len(hdr); + int attrlen = 0; + void *data; + + data = nlmsg_data(hdr); + ops = nl_cache_ops_associate_safe(nlmsg_get_proto(msg), + hdr->nlmsg_type); + if (ops) { + attrlen = nlmsg_attrlen(hdr, ops->co_hdrsize); + payloadlen -= attrlen; + } + + if (msg->nm_protocol == NETLINK_GENERIC) + data = print_genl_msg(msg, ofd, hdr, ops, &payloadlen); + + if (payloadlen) { + fprintf(ofd, " [PAYLOAD] %d octets\n", payloadlen); + dump_hex(ofd, data, payloadlen, 0); + } + + if (attrlen) { + struct nlattr *attrs; + int attrlen; + + attrs = nlmsg_attrdata(hdr, ops->co_hdrsize); + attrlen = nlmsg_attrlen(hdr, ops->co_hdrsize); + dump_attrs(ofd, attrs, attrlen, 0); + } + + if (ops) + nl_cache_ops_put(ops); +} + +/** + * Dump message in human readable format to file descriptor + * @arg msg Message to print + * @arg ofd File descriptor. + */ +void nl_msg_dump(struct nl_msg *msg, FILE *ofd) +{ + struct nlmsghdr *hdr = nlmsg_hdr(msg); + + fprintf(ofd, + "-------------------------- BEGIN NETLINK MESSAGE ---------------------------\n"); + + fprintf(ofd, " [NETLINK HEADER] %zu octets\n", sizeof(struct nlmsghdr)); + print_hdr(ofd, msg); + + if (hdr->nlmsg_type == NLMSG_ERROR) + dump_error_msg(msg, ofd); + else if (nlmsg_len(hdr) > 0) + print_msg(msg, ofd, hdr); + + fprintf(ofd, + "--------------------------- END NETLINK MESSAGE ---------------------------\n"); +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/netfilter/ct.c b/libnl/lib/netfilter/ct.c new file mode 100644 index 0000000..3a69a2d --- /dev/null +++ b/libnl/lib/netfilter/ct.c @@ -0,0 +1,721 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2008 Patrick McHardy + */ + +/** + * @ingroup nfnl + * @defgroup ct Conntrack + * @brief + * @{ + */ + +#include "nl-default.h" + +#include + +#include + +#include +#include +#include + +#include "nl-netfilter.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" + +static struct nl_cache_ops nfnl_ct_ops; + + +static struct nla_policy ct_policy[CTA_MAX+1] = { + [CTA_TUPLE_ORIG] = { .type = NLA_NESTED }, + [CTA_TUPLE_REPLY] = { .type = NLA_NESTED }, + [CTA_STATUS] = { .type = NLA_U32 }, + [CTA_PROTOINFO] = { .type = NLA_NESTED }, + //[CTA_HELP] + //[CTA_NAT_SRC] + [CTA_TIMEOUT] = { .type = NLA_U32 }, + [CTA_MARK] = { .type = NLA_U32 }, + [CTA_COUNTERS_ORIG] = { .type = NLA_NESTED }, + [CTA_COUNTERS_REPLY] = { .type = NLA_NESTED }, + [CTA_USE] = { .type = NLA_U32 }, + [CTA_ID] = { .type = NLA_U32 }, + [CTA_ZONE] = { .type = NLA_U16 }, + //[CTA_NAT_DST] +}; + +static struct nla_policy ct_tuple_policy[CTA_TUPLE_MAX+1] = { + [CTA_TUPLE_IP] = { .type = NLA_NESTED }, + [CTA_TUPLE_PROTO] = { .type = NLA_NESTED }, +}; + +static struct nla_policy ct_ip_policy[CTA_IP_MAX+1] = { + [CTA_IP_V4_SRC] = { .type = NLA_U32 }, + [CTA_IP_V4_DST] = { .type = NLA_U32 }, + [CTA_IP_V6_SRC] = { .minlen = 16 }, + [CTA_IP_V6_DST] = { .minlen = 16 }, +}; + +static struct nla_policy ct_proto_policy[CTA_PROTO_MAX+1] = { + [CTA_PROTO_NUM] = { .type = NLA_U8 }, + [CTA_PROTO_SRC_PORT] = { .type = NLA_U16 }, + [CTA_PROTO_DST_PORT] = { .type = NLA_U16 }, + [CTA_PROTO_ICMP_ID] = { .type = NLA_U16 }, + [CTA_PROTO_ICMP_TYPE] = { .type = NLA_U8 }, + [CTA_PROTO_ICMP_CODE] = { .type = NLA_U8 }, + [CTA_PROTO_ICMPV6_ID] = { .type = NLA_U16 }, + [CTA_PROTO_ICMPV6_TYPE] = { .type = NLA_U8 }, + [CTA_PROTO_ICMPV6_CODE] = { .type = NLA_U8 }, +}; + +static struct nla_policy ct_protoinfo_policy[CTA_PROTOINFO_MAX+1] = { + [CTA_PROTOINFO_TCP] = { .type = NLA_NESTED }, +}; + +static struct nla_policy ct_protoinfo_tcp_policy[CTA_PROTOINFO_TCP_MAX+1] = { + [CTA_PROTOINFO_TCP_STATE] = { .type = NLA_U8 }, + [CTA_PROTOINFO_TCP_WSCALE_ORIGINAL] = { .type = NLA_U8 }, + [CTA_PROTOINFO_TCP_WSCALE_REPLY] = { .type = NLA_U8 }, + [CTA_PROTOINFO_TCP_FLAGS_ORIGINAL] = { .minlen = 2 }, + [CTA_PROTOINFO_TCP_FLAGS_REPLY] = { .minlen = 2 }, + +}; + +static struct nla_policy ct_counters_policy[CTA_COUNTERS_MAX+1] = { + [CTA_COUNTERS_PACKETS] = { .type = NLA_U64 }, + [CTA_COUNTERS_BYTES] = { .type = NLA_U64 }, + [CTA_COUNTERS32_PACKETS]= { .type = NLA_U32 }, + [CTA_COUNTERS32_BYTES] = { .type = NLA_U32 }, +}; + +static struct nla_policy ct_timestamp_policy[CTA_TIMESTAMP_MAX + 1] = { + [CTA_TIMESTAMP_START] = { .type = NLA_U64 }, + [CTA_TIMESTAMP_STOP] = { .type = NLA_U64 }, +}; + +static int ct_parse_ip(struct nfnl_ct *ct, int repl, struct nlattr *attr) +{ + struct nlattr *tb[CTA_IP_MAX+1]; + struct nl_addr *addr; + int err; + + err = nla_parse_nested(tb, CTA_IP_MAX, attr, ct_ip_policy); + if (err < 0) + goto errout; + + if (tb[CTA_IP_V4_SRC]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V4_SRC], AF_INET); + if (addr == NULL) + goto errout_enomem; + err = nfnl_ct_set_src(ct, repl, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + if (tb[CTA_IP_V4_DST]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V4_DST], AF_INET); + if (addr == NULL) + goto errout_enomem; + err = nfnl_ct_set_dst(ct, repl, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + if (tb[CTA_IP_V6_SRC]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V6_SRC], AF_INET6); + if (addr == NULL) + goto errout_enomem; + err = nfnl_ct_set_src(ct, repl, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + if (tb[CTA_IP_V6_DST]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V6_DST], AF_INET6); + if (addr == NULL) + goto errout_enomem; + err = nfnl_ct_set_dst(ct, repl, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + + return 0; + +errout_enomem: + err = -NLE_NOMEM; +errout: + return err; +} + +static int ct_parse_proto(struct nfnl_ct *ct, int repl, struct nlattr *attr) +{ + struct nlattr *tb[CTA_PROTO_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_PROTO_MAX, attr, ct_proto_policy); + if (err < 0) + return err; + + if (!repl && tb[CTA_PROTO_NUM]) + nfnl_ct_set_proto(ct, nla_get_u8(tb[CTA_PROTO_NUM])); + if (tb[CTA_PROTO_SRC_PORT]) + nfnl_ct_set_src_port(ct, repl, + ntohs(nla_get_u16(tb[CTA_PROTO_SRC_PORT]))); + if (tb[CTA_PROTO_DST_PORT]) + nfnl_ct_set_dst_port(ct, repl, + ntohs(nla_get_u16(tb[CTA_PROTO_DST_PORT]))); + + if (ct->ct_family == AF_INET) { + if (tb[CTA_PROTO_ICMP_ID]) + nfnl_ct_set_icmp_id(ct, repl, + ntohs(nla_get_u16(tb[CTA_PROTO_ICMP_ID]))); + if (tb[CTA_PROTO_ICMP_TYPE]) + nfnl_ct_set_icmp_type(ct, repl, + nla_get_u8(tb[CTA_PROTO_ICMP_TYPE])); + if (tb[CTA_PROTO_ICMP_CODE]) + nfnl_ct_set_icmp_code(ct, repl, + nla_get_u8(tb[CTA_PROTO_ICMP_CODE])); + } else if (ct->ct_family == AF_INET6) { + if (tb[CTA_PROTO_ICMPV6_ID]) + nfnl_ct_set_icmp_id(ct, repl, + ntohs(nla_get_u16(tb[CTA_PROTO_ICMPV6_ID]))); + if (tb[CTA_PROTO_ICMPV6_TYPE]) + nfnl_ct_set_icmp_type(ct, repl, + nla_get_u8(tb[CTA_PROTO_ICMPV6_TYPE])); + if (tb[CTA_PROTO_ICMPV6_CODE]) + nfnl_ct_set_icmp_code(ct, repl, + nla_get_u8(tb[CTA_PROTO_ICMPV6_CODE])); + } + + return 0; +} + +static int ct_parse_tuple(struct nfnl_ct *ct, int repl, struct nlattr *attr) +{ + struct nlattr *tb[CTA_TUPLE_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_TUPLE_MAX, attr, ct_tuple_policy); + if (err < 0) + return err; + + if (tb[CTA_TUPLE_IP]) { + err = ct_parse_ip(ct, repl, tb[CTA_TUPLE_IP]); + if (err < 0) + return err; + } + + if (tb[CTA_TUPLE_PROTO]) { + err = ct_parse_proto(ct, repl, tb[CTA_TUPLE_PROTO]); + if (err < 0) + return err; + } + + return 0; +} + +static int ct_parse_protoinfo_tcp(struct nfnl_ct *ct, struct nlattr *attr) +{ + struct nlattr *tb[CTA_PROTOINFO_TCP_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, attr, + ct_protoinfo_tcp_policy); + if (err < 0) + return err; + + if (tb[CTA_PROTOINFO_TCP_STATE]) + nfnl_ct_set_tcp_state(ct, + nla_get_u8(tb[CTA_PROTOINFO_TCP_STATE])); + + return 0; +} + +static int ct_parse_protoinfo(struct nfnl_ct *ct, struct nlattr *attr) +{ + struct nlattr *tb[CTA_PROTOINFO_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_PROTOINFO_MAX, attr, + ct_protoinfo_policy); + if (err < 0) + return err; + + if (tb[CTA_PROTOINFO_TCP]) { + err = ct_parse_protoinfo_tcp(ct, tb[CTA_PROTOINFO_TCP]); + if (err < 0) + return err; + } + + return 0; +} + +static int ct_parse_counters(struct nfnl_ct *ct, int repl, struct nlattr *attr) +{ + struct nlattr *tb[CTA_COUNTERS_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_COUNTERS_MAX, attr, ct_counters_policy); + if (err < 0) + return err; + + if (tb[CTA_COUNTERS_PACKETS]) + nfnl_ct_set_packets(ct, repl, + ntohll(nla_get_u64(tb[CTA_COUNTERS_PACKETS]))); + if (tb[CTA_COUNTERS32_PACKETS]) + nfnl_ct_set_packets(ct, repl, + ntohl(nla_get_u32(tb[CTA_COUNTERS32_PACKETS]))); + if (tb[CTA_COUNTERS_BYTES]) + nfnl_ct_set_bytes(ct, repl, + ntohll(nla_get_u64(tb[CTA_COUNTERS_BYTES]))); + if (tb[CTA_COUNTERS32_BYTES]) + nfnl_ct_set_bytes(ct, repl, + ntohl(nla_get_u32(tb[CTA_COUNTERS32_BYTES]))); + + return 0; +} + +int nfnlmsg_ct_group(struct nlmsghdr *nlh) +{ + switch (nfnlmsg_subtype(nlh)) { + case IPCTNL_MSG_CT_NEW: + if (nlh->nlmsg_flags & (NLM_F_CREATE|NLM_F_EXCL)) + return NFNLGRP_CONNTRACK_NEW; + else + return NFNLGRP_CONNTRACK_UPDATE; + case IPCTNL_MSG_CT_DELETE: + return NFNLGRP_CONNTRACK_DESTROY; + default: + return NFNLGRP_NONE; + } +} + +static int ct_parse_timestamp(struct nfnl_ct *ct, struct nlattr *attr) +{ + struct nlattr *tb[CTA_TIMESTAMP_MAX + 1]; + int err; + + err = nla_parse_nested(tb, CTA_TIMESTAMP_MAX, attr, + ct_timestamp_policy); + if (err < 0) + return err; + + if (tb[CTA_TIMESTAMP_START] && tb[CTA_TIMESTAMP_STOP]) + nfnl_ct_set_timestamp(ct, + ntohll(nla_get_u64(tb[CTA_TIMESTAMP_START])), + ntohll(nla_get_u64(tb[CTA_TIMESTAMP_STOP]))); + + return 0; +} + +static int _nfnlmsg_ct_parse(struct nlattr **tb, struct nfnl_ct *ct) +{ + int err; + + if (tb[CTA_TUPLE_ORIG]) { + err = ct_parse_tuple(ct, 0, tb[CTA_TUPLE_ORIG]); + if (err < 0) + return err; + } + if (tb[CTA_TUPLE_REPLY]) { + err = ct_parse_tuple(ct, 1, tb[CTA_TUPLE_REPLY]); + if (err < 0) + return err; + } + + if (tb[CTA_PROTOINFO]) { + err = ct_parse_protoinfo(ct, tb[CTA_PROTOINFO]); + if (err < 0) + return err; + } + + if (tb[CTA_STATUS]) + nfnl_ct_set_status(ct, ntohl(nla_get_u32(tb[CTA_STATUS]))); + if (tb[CTA_TIMEOUT]) + nfnl_ct_set_timeout(ct, ntohl(nla_get_u32(tb[CTA_TIMEOUT]))); + if (tb[CTA_MARK]) + nfnl_ct_set_mark(ct, ntohl(nla_get_u32(tb[CTA_MARK]))); + if (tb[CTA_USE]) + nfnl_ct_set_use(ct, ntohl(nla_get_u32(tb[CTA_USE]))); + if (tb[CTA_ID]) + nfnl_ct_set_id(ct, ntohl(nla_get_u32(tb[CTA_ID]))); + if (tb[CTA_ZONE]) + nfnl_ct_set_zone(ct, ntohs(nla_get_u16(tb[CTA_ZONE]))); + + if (tb[CTA_COUNTERS_ORIG]) { + err = ct_parse_counters(ct, 0, tb[CTA_COUNTERS_ORIG]); + if (err < 0) + return err; + } + + if (tb[CTA_COUNTERS_REPLY]) { + err = ct_parse_counters(ct, 1, tb[CTA_COUNTERS_REPLY]); + if (err < 0) + return err; + } + + if (tb[CTA_TIMESTAMP]) { + err = ct_parse_timestamp(ct, tb[CTA_TIMESTAMP]); + if (err < 0) + return err; + } + + return 0; +} + +int nfnlmsg_ct_parse(struct nlmsghdr *nlh, struct nfnl_ct **result) +{ + struct nfnl_ct *ct; + struct nlattr *tb[CTA_MAX+1]; + int err; + + ct = nfnl_ct_alloc(); + if (!ct) + return -NLE_NOMEM; + + ct->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, CTA_MAX, + ct_policy); + if (err < 0) + goto errout; + + nfnl_ct_set_family(ct, nfnlmsg_family(nlh)); + + err = _nfnlmsg_ct_parse(tb, ct); + if (err < 0) + goto errout; + + *result = ct; + return 0; +errout: + nfnl_ct_put(ct); + return err; +} + +int nfnlmsg_ct_parse_nested(struct nlattr *attr, struct nfnl_ct **result) +{ + struct nfnl_ct *ct; + struct nlattr *tb[CTA_MAX+1]; + int err; + + ct = nfnl_ct_alloc(); + if (!ct) + return -NLE_NOMEM; + + // msgtype not given for nested + //ct->ce_msgtype = nlh->nlmsg_type; + + err = nla_parse_nested(tb, CTA_MAX, attr, ct_policy); + if (err < 0) + goto errout; + + // family not known + //nfnl_ct_set_family(ct, nfnlmsg_family(nlh)); + + err = _nfnlmsg_ct_parse(tb, ct); + if (err < 0) + goto errout; + + *result = ct; + return 0; +errout: + nfnl_ct_put(ct); + return err; +} + +static int ct_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct nfnl_ct *ct; + int err; + + if ((err = nfnlmsg_ct_parse(nlh, &ct)) < 0) + return err; + + err = pp->pp_cb((struct nl_object *) ct, pp); + nfnl_ct_put(ct); + return err; +} + +/** + * Send nfnl ct dump request + * @arg sk Netlink socket. + * + * @return 0 on success or a negative error code. Due to a bug, this function + * returns the number of bytes sent. Treat any non-negative number as success. + */ +int nfnl_ct_dump_request(struct nl_sock *sk) +{ + return nfnl_send_simple(sk, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET, + NLM_F_DUMP, AF_UNSPEC, 0); +} + +static int ct_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + return nfnl_ct_dump_request(sk); +} + +static int nfnl_ct_build_tuple(struct nl_msg *msg, const struct nfnl_ct *ct, + int repl) +{ + struct nlattr *tuple, *ip, *proto; + struct nl_addr *addr; + int family; + + family = nfnl_ct_get_family(ct); + + tuple = nla_nest_start(msg, repl ? CTA_TUPLE_REPLY : CTA_TUPLE_ORIG); + if (!tuple) + goto nla_put_failure; + + ip = nla_nest_start(msg, CTA_TUPLE_IP); + if (!ip) + goto nla_put_failure; + + addr = nfnl_ct_get_src(ct, repl); + if (addr) + NLA_PUT_ADDR(msg, + family == AF_INET ? CTA_IP_V4_SRC : CTA_IP_V6_SRC, + addr); + + addr = nfnl_ct_get_dst(ct, repl); + if (addr) + NLA_PUT_ADDR(msg, + family == AF_INET ? CTA_IP_V4_DST : CTA_IP_V6_DST, + addr); + + nla_nest_end(msg, ip); + + proto = nla_nest_start(msg, CTA_TUPLE_PROTO); + if (!proto) + goto nla_put_failure; + + if (nfnl_ct_test_proto(ct)) + NLA_PUT_U8(msg, CTA_PROTO_NUM, nfnl_ct_get_proto(ct)); + + if (nfnl_ct_test_src_port(ct, repl)) + NLA_PUT_U16(msg, CTA_PROTO_SRC_PORT, + htons(nfnl_ct_get_src_port(ct, repl))); + + if (nfnl_ct_test_dst_port(ct, repl)) + NLA_PUT_U16(msg, CTA_PROTO_DST_PORT, + htons(nfnl_ct_get_dst_port(ct, repl))); + + if (family == AF_INET) { + if (nfnl_ct_test_icmp_id(ct, repl)) + NLA_PUT_U16(msg, CTA_PROTO_ICMP_ID, + htons(nfnl_ct_get_icmp_id(ct, repl))); + + if (nfnl_ct_test_icmp_type(ct, repl)) + NLA_PUT_U8(msg, CTA_PROTO_ICMP_TYPE, + nfnl_ct_get_icmp_type(ct, repl)); + + if (nfnl_ct_test_icmp_code(ct, repl)) + NLA_PUT_U8(msg, CTA_PROTO_ICMP_CODE, + nfnl_ct_get_icmp_code(ct, repl)); + } else if (family == AF_INET6) { + if (nfnl_ct_test_icmp_id(ct, repl)) + NLA_PUT_U16(msg, CTA_PROTO_ICMPV6_ID, + htons(nfnl_ct_get_icmp_id(ct, repl))); + + if (nfnl_ct_test_icmp_type(ct, repl)) + NLA_PUT_U8(msg, CTA_PROTO_ICMPV6_TYPE, + nfnl_ct_get_icmp_type(ct, repl)); + + if (nfnl_ct_test_icmp_code(ct, repl)) + NLA_PUT_U8(msg, CTA_PROTO_ICMPV6_CODE, + nfnl_ct_get_icmp_code(ct, repl)); + } + + nla_nest_end(msg, proto); + + nla_nest_end(msg, tuple); + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int nfnl_ct_build_message(const struct nfnl_ct *ct, int cmd, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + int err; + int reply = 0; + + msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_CTNETLINK, cmd, flags, + nfnl_ct_get_family(ct), 0); + if (msg == NULL) + return -NLE_NOMEM; + + /* We use REPLY || ORIG, depending on requests. */ + if (nfnl_ct_get_src(ct, 1) || nfnl_ct_get_dst(ct, 1)) { + reply = 1; + if ((err = nfnl_ct_build_tuple(msg, ct, 1)) < 0) + goto err_out; + } + + if (!reply || nfnl_ct_get_src(ct, 0) || nfnl_ct_get_dst(ct, 0)) { + if ((err = nfnl_ct_build_tuple(msg, ct, 0)) < 0) + goto err_out; + } + + if (nfnl_ct_test_status(ct)) + NLA_PUT_U32(msg, CTA_STATUS, htonl(nfnl_ct_get_status(ct))); + + if (nfnl_ct_test_timeout(ct)) + NLA_PUT_U32(msg, CTA_TIMEOUT, htonl(nfnl_ct_get_timeout(ct))); + + if (nfnl_ct_test_mark(ct)) + NLA_PUT_U32(msg, CTA_MARK, htonl(nfnl_ct_get_mark(ct))); + + if (nfnl_ct_test_id(ct)) + NLA_PUT_U32(msg, CTA_ID, htonl(nfnl_ct_get_id(ct))); + + if (nfnl_ct_test_zone(ct)) + NLA_PUT_U16(msg, CTA_ZONE, htons(nfnl_ct_get_zone(ct))); + + *result = msg; + return 0; + +nla_put_failure: +err_out: + nlmsg_free(msg); + return err; +} + +int nfnl_ct_build_add_request(const struct nfnl_ct *ct, int flags, + struct nl_msg **result) +{ + return nfnl_ct_build_message(ct, IPCTNL_MSG_CT_NEW, flags, result); +} + +int nfnl_ct_add(struct nl_sock *sk, const struct nfnl_ct *ct, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_ct_build_add_request(ct, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +int nfnl_ct_build_delete_request(const struct nfnl_ct *ct, int flags, + struct nl_msg **result) +{ + return nfnl_ct_build_message(ct, IPCTNL_MSG_CT_DELETE, flags, result); +} + +int nfnl_ct_del(struct nl_sock *sk, const struct nfnl_ct *ct, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_ct_build_delete_request(ct, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +int nfnl_ct_build_query_request(const struct nfnl_ct *ct, int flags, + struct nl_msg **result) +{ + return nfnl_ct_build_message(ct, IPCTNL_MSG_CT_GET, flags, result); +} + +int nfnl_ct_query(struct nl_sock *sk, const struct nfnl_ct *ct, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_ct_build_query_request(ct, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** + * @name Cache Management + * @{ + */ + +/** + * Build a conntrack cache holding all conntrack currently in the kernel + * @arg sk Netlink socket. + * @arg result Pointer to store resulting cache. + * + * Allocates a new cache, initializes it properly and updates it to + * contain all conntracks currently in the kernel. + * + * @return 0 on success or a negative error code. + */ +int nfnl_ct_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&nfnl_ct_ops, sk, result); +} + +/** @} */ + +/** + * @name Conntrack Addition + * @{ + */ + +/** @} */ + +static struct nl_af_group ct_groups[] = { + { AF_UNSPEC, NFNLGRP_CONNTRACK_NEW }, + { AF_UNSPEC, NFNLGRP_CONNTRACK_UPDATE }, + { AF_UNSPEC, NFNLGRP_CONNTRACK_DESTROY }, + { END_OF_GROUP_LIST }, +}; + +#define NFNLMSG_CT_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_CTNETLINK, (type)) +static struct nl_cache_ops nfnl_ct_ops = { + .co_name = "netfilter/ct", + .co_hdrsize = NFNL_HDRLEN, + .co_msgtypes = { + { NFNLMSG_CT_TYPE(IPCTNL_MSG_CT_NEW), NL_ACT_NEW, "new" }, + { NFNLMSG_CT_TYPE(IPCTNL_MSG_CT_GET), NL_ACT_GET, "get" }, + { NFNLMSG_CT_TYPE(IPCTNL_MSG_CT_DELETE), NL_ACT_DEL, "del" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_NETFILTER, + .co_groups = ct_groups, + .co_request_update = ct_request_update, + .co_msg_parser = ct_msg_parser, + .co_obj_ops = &ct_obj_ops, +}; + +static void _nl_init ct_init(void) +{ + nl_cache_mngt_register(&nfnl_ct_ops); +} + +static void _nl_exit ct_exit(void) +{ + nl_cache_mngt_unregister(&nfnl_ct_ops); +} + +/** @} */ diff --git a/libnl/lib/netfilter/ct_obj.c b/libnl/lib/netfilter/ct_obj.c new file mode 100644 index 0000000..4430400 --- /dev/null +++ b/libnl/lib/netfilter/ct_obj.c @@ -0,0 +1,855 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#include "nl-default.h" + +#include + +#include +#include + +#include + +#include +#include + +#include "nl-priv-dynamic-core/object-api.h" +#include "nl-netfilter.h" +#include "nl-priv-dynamic-core/nl-core.h" + +/** @cond SKIP */ +#define CT_ATTR_FAMILY (1UL << 0) +#define CT_ATTR_PROTO (1UL << 1) + +#define CT_ATTR_TCP_STATE (1UL << 2) + +#define CT_ATTR_STATUS (1UL << 3) +#define CT_ATTR_TIMEOUT (1UL << 4) +#define CT_ATTR_MARK (1UL << 5) +#define CT_ATTR_USE (1UL << 6) +#define CT_ATTR_ID (1UL << 7) + +#define CT_ATTR_ORIG_SRC (1UL << 8) +#define CT_ATTR_ORIG_DST (1UL << 9) +#define CT_ATTR_ORIG_SRC_PORT (1UL << 10) +#define CT_ATTR_ORIG_DST_PORT (1UL << 11) +#define CT_ATTR_ORIG_ICMP_ID (1UL << 12) +#define CT_ATTR_ORIG_ICMP_TYPE (1UL << 13) +#define CT_ATTR_ORIG_ICMP_CODE (1UL << 14) +#define CT_ATTR_ORIG_PACKETS (1UL << 15) +#define CT_ATTR_ORIG_BYTES (1UL << 16) + +#define CT_ATTR_REPL_SRC (1UL << 17) +#define CT_ATTR_REPL_DST (1UL << 18) +#define CT_ATTR_REPL_SRC_PORT (1UL << 19) +#define CT_ATTR_REPL_DST_PORT (1UL << 20) +#define CT_ATTR_REPL_ICMP_ID (1UL << 21) +#define CT_ATTR_REPL_ICMP_TYPE (1UL << 22) +#define CT_ATTR_REPL_ICMP_CODE (1UL << 23) +#define CT_ATTR_REPL_PACKETS (1UL << 24) +#define CT_ATTR_REPL_BYTES (1UL << 25) +#define CT_ATTR_TIMESTAMP (1UL << 26) +#define CT_ATTR_ZONE (1UL << 27) +/** @endcond */ + +static void ct_free_data(struct nl_object *c) +{ + struct nfnl_ct *ct = (struct nfnl_ct *) c; + + if (ct == NULL) + return; + + nl_addr_put(ct->ct_orig.src); + nl_addr_put(ct->ct_orig.dst); + nl_addr_put(ct->ct_repl.src); + nl_addr_put(ct->ct_repl.dst); +} + +static int ct_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct nfnl_ct *dst = (struct nfnl_ct *) _dst; + struct nfnl_ct *src = (struct nfnl_ct *) _src; + struct nl_addr *addr; + + dst->ct_orig.src = NULL; + dst->ct_orig.dst = NULL; + dst->ct_repl.src = NULL; + dst->ct_repl.dst = NULL; + + if (src->ct_orig.src) { + addr = nl_addr_clone(src->ct_orig.src); + if (!addr) + return -NLE_NOMEM; + dst->ct_orig.src = addr; + } + + if (src->ct_orig.dst) { + addr = nl_addr_clone(src->ct_orig.dst); + if (!addr) + return -NLE_NOMEM; + dst->ct_orig.dst = addr; + } + + if (src->ct_repl.src) { + addr = nl_addr_clone(src->ct_repl.src); + if (!addr) + return -NLE_NOMEM; + dst->ct_repl.src = addr; + } + + if (src->ct_repl.dst) { + addr = nl_addr_clone(src->ct_repl.dst); + if (!addr) + return -NLE_NOMEM; + dst->ct_repl.dst = addr; + } + + return 0; +} + +static void dump_addr(struct nl_dump_params *p, struct nl_addr *addr, int port) +{ + char buf[64]; + + if (addr) + nl_dump(p, "%s", nl_addr2str(addr, buf, sizeof(buf))); + + if (port) + nl_dump(p, ":%u ", port); + else if (addr) + nl_dump(p, " "); +} + +static void dump_icmp(struct nl_dump_params *p, struct nfnl_ct *ct, int reply) +{ + if (nfnl_ct_test_icmp_type(ct, reply)) + nl_dump(p, "icmp type %d ", nfnl_ct_get_icmp_type(ct, reply)); + + if (nfnl_ct_test_icmp_code(ct, reply)) + nl_dump(p, "code %d ", nfnl_ct_get_icmp_code(ct, reply)); + + if (nfnl_ct_test_icmp_id(ct, reply)) + nl_dump(p, "id %d ", nfnl_ct_get_icmp_id(ct, reply)); +} + +static void ct_dump_tuples(struct nfnl_ct *ct, struct nl_dump_params *p) +{ + struct nl_addr *orig_src, *orig_dst, *reply_src, *reply_dst; + int orig_sport = 0, orig_dport = 0, reply_sport = 0, reply_dport = 0; + int sync = 0; + + orig_src = nfnl_ct_get_src(ct, 0); + orig_dst = nfnl_ct_get_dst(ct, 0); + reply_src = nfnl_ct_get_src(ct, 1); + reply_dst = nfnl_ct_get_dst(ct, 1); + + if (nfnl_ct_test_src_port(ct, 0)) + orig_sport = nfnl_ct_get_src_port(ct, 0); + + if (nfnl_ct_test_dst_port(ct, 0)) + orig_dport = nfnl_ct_get_dst_port(ct, 0); + + if (nfnl_ct_test_src_port(ct, 1)) + reply_sport = nfnl_ct_get_src_port(ct, 1); + + if (nfnl_ct_test_dst_port(ct, 1)) + reply_dport = nfnl_ct_get_dst_port(ct, 1); + + if (orig_src && orig_dst && reply_src && reply_dst && + orig_sport == reply_dport && orig_dport == reply_sport && + !nl_addr_cmp(orig_src, reply_dst) && + !nl_addr_cmp(orig_dst, reply_src)) + sync = 1; + + dump_addr(p, orig_src, orig_sport); + nl_dump(p, sync ? "<-> " : "-> "); + dump_addr(p, orig_dst, orig_dport); + dump_icmp(p, ct, 0); + + if (!sync) { + dump_addr(p, reply_src, reply_sport); + nl_dump(p, "<- "); + dump_addr(p, reply_dst, reply_dport); + dump_icmp(p, ct, 1); + } +} + +/* Compatible with /proc/net/nf_conntrack */ +static void ct_dump_line(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_ct *ct = (struct nfnl_ct *) a; + char buf[64]; + + nl_new_line(p); + + if (nfnl_ct_test_proto(ct)) + nl_dump(p, "%s ", + nl_ip_proto2str(nfnl_ct_get_proto(ct), buf, sizeof(buf))); + + if (nfnl_ct_test_tcp_state(ct)) + nl_dump(p, "%s ", + nfnl_ct_tcp_state2str(nfnl_ct_get_tcp_state(ct), + buf, sizeof(buf))); + + ct_dump_tuples(ct, p); + + if (nfnl_ct_test_mark(ct) && nfnl_ct_get_mark(ct)) + nl_dump(p, "mark %u ", nfnl_ct_get_mark(ct)); + + if (nfnl_ct_test_zone(ct)) + nl_dump(p, "zone %hu ", nfnl_ct_get_zone(ct)); + + if (nfnl_ct_test_timestamp(ct)) { + const struct nfnl_ct_timestamp *tstamp = nfnl_ct_get_timestamp(ct); + int64_t delta_time = tstamp->stop - tstamp->start; + + if (delta_time > 0) + delta_time /= NSEC_PER_SEC; + else + delta_time = 0; + nl_dump(p, "delta-time %llu ", (long long unsigned)delta_time); + } + + nl_dump(p, "\n"); +} + +static void ct_dump_details(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_ct *ct = (struct nfnl_ct *) a; + char buf[64]; + int fp = 0; + + ct_dump_line(a, p); + + nl_dump(p, " id 0x%x ", ct->ct_id); + if (ct->ce_mask & CT_ATTR_FAMILY) + nl_dump_line(p, "family %s ", + nl_af2str(ct->ct_family, buf, sizeof(buf))); + + if (nfnl_ct_test_use(ct)) + nl_dump(p, "refcnt %u ", nfnl_ct_get_use(ct)); + + if (nfnl_ct_test_timeout(ct)) { + uint64_t timeout_ms = nfnl_ct_get_timeout(ct) * 1000UL; + nl_dump(p, "timeout %s ", + nl_msec2str(timeout_ms, buf, sizeof(buf))); + } + + if (ct->ct_status) + nl_dump(p, "<"); + +#define PRINT_FLAG(str) \ + { nl_dump(p, "%s%s", fp++ ? "," : "", (str)); } + + if (ct->ct_status & IPS_EXPECTED) + PRINT_FLAG("EXPECTED"); + if (!(ct->ct_status & IPS_SEEN_REPLY)) + PRINT_FLAG("NOREPLY"); + if (ct->ct_status & IPS_ASSURED) + PRINT_FLAG("ASSURED"); + if (!(ct->ct_status & IPS_CONFIRMED)) + PRINT_FLAG("NOTSENT"); + if (ct->ct_status & IPS_SRC_NAT) + PRINT_FLAG("SNAT"); + if (ct->ct_status & IPS_DST_NAT) + PRINT_FLAG("DNAT"); + if (ct->ct_status & IPS_SEQ_ADJUST) + PRINT_FLAG("SEQADJUST"); + if (!(ct->ct_status & IPS_SRC_NAT_DONE)) + PRINT_FLAG("SNAT_INIT"); + if (!(ct->ct_status & IPS_DST_NAT_DONE)) + PRINT_FLAG("DNAT_INIT"); + if (ct->ct_status & IPS_DYING) + PRINT_FLAG("DYING"); + if (ct->ct_status & IPS_FIXED_TIMEOUT) + PRINT_FLAG("FIXED_TIMEOUT"); +#undef PRINT_FLAG + + if (ct->ct_status) + nl_dump(p, ">"); + nl_dump(p, "\n"); +} + +static void ct_dump_stats(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_ct *ct = (struct nfnl_ct *) a; + double res; + char *unit; + uint64_t packets; + const char * const names[] = {"rx", "tx"}; + int i; + + ct_dump_details(a, p); + + if (!nfnl_ct_test_bytes(ct, 0) || + !nfnl_ct_test_packets(ct, 0) || + !nfnl_ct_test_bytes(ct, 1) || + !nfnl_ct_test_packets(ct, 1)) + { + nl_dump_line(p, " Statistics are not available.\n"); + nl_dump_line(p, " Please set sysctl net.netfilter.nf_conntrack_acct=1\n"); + nl_dump_line(p, " (Require kernel 2.6.27)\n"); + return; + } + + nl_dump_line(p, " # packets volume\n"); + for (i=0; i<=1; i++) { + res = nl_cancel_down_bytes(nfnl_ct_get_bytes(ct, i), &unit); + packets = nfnl_ct_get_packets(ct, i); + nl_dump_line(p, " %s %10" PRIu64 " %7.2f %s\n", names[i], packets, res, unit); + } +} + +static uint64_t ct_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct nfnl_ct *a = (struct nfnl_ct *) _a; + struct nfnl_ct *b = (struct nfnl_ct *) _b; + uint64_t diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) +#define _DIFF_VAL(ATTR, FIELD) _DIFF(ATTR, a->FIELD != b->FIELD) +#define _DIFF_ADDR(ATTR, FIELD) \ + ((flags & LOOSE_COMPARISON) ? \ + _DIFF(ATTR, nl_addr_cmp_prefix(a->FIELD, b->FIELD)) : \ + _DIFF(ATTR, nl_addr_cmp(a->FIELD, b->FIELD))) + diff |= _DIFF_VAL(CT_ATTR_FAMILY, ct_family); + diff |= _DIFF_VAL(CT_ATTR_PROTO, ct_proto); + diff |= _DIFF_VAL(CT_ATTR_TCP_STATE, ct_protoinfo.tcp.state); + diff |= _DIFF_VAL(CT_ATTR_TIMEOUT, ct_timeout); + diff |= _DIFF_VAL(CT_ATTR_MARK, ct_mark); + diff |= _DIFF_VAL(CT_ATTR_USE, ct_use); + diff |= _DIFF_VAL(CT_ATTR_ID, ct_id); + diff |= _DIFF_ADDR(CT_ATTR_ORIG_SRC, ct_orig.src); + diff |= _DIFF_ADDR(CT_ATTR_ORIG_DST, ct_orig.dst); + diff |= _DIFF_VAL(CT_ATTR_ORIG_SRC_PORT, ct_orig.proto.port.src); + diff |= _DIFF_VAL(CT_ATTR_ORIG_DST_PORT, ct_orig.proto.port.dst); + diff |= _DIFF_VAL(CT_ATTR_ORIG_ICMP_ID, ct_orig.proto.icmp.id); + diff |= _DIFF_VAL(CT_ATTR_ORIG_ICMP_TYPE, ct_orig.proto.icmp.type); + diff |= _DIFF_VAL(CT_ATTR_ORIG_ICMP_CODE, ct_orig.proto.icmp.code); + diff |= _DIFF_VAL(CT_ATTR_ORIG_PACKETS, ct_orig.packets); + diff |= _DIFF_VAL(CT_ATTR_ORIG_BYTES, ct_orig.bytes); + diff |= _DIFF_ADDR(CT_ATTR_REPL_SRC, ct_repl.src); + diff |= _DIFF_ADDR(CT_ATTR_REPL_DST, ct_repl.dst); + diff |= _DIFF_VAL(CT_ATTR_REPL_SRC_PORT, ct_repl.proto.port.src); + diff |= _DIFF_VAL(CT_ATTR_REPL_DST_PORT, ct_repl.proto.port.dst); + diff |= _DIFF_VAL(CT_ATTR_REPL_ICMP_ID, ct_repl.proto.icmp.id); + diff |= _DIFF_VAL(CT_ATTR_REPL_ICMP_TYPE, ct_repl.proto.icmp.type); + diff |= _DIFF_VAL(CT_ATTR_REPL_ICMP_CODE, ct_repl.proto.icmp.code); + diff |= _DIFF_VAL(CT_ATTR_REPL_PACKETS, ct_repl.packets); + diff |= _DIFF_VAL(CT_ATTR_REPL_BYTES, ct_repl.bytes); + + if (flags & LOOSE_COMPARISON) + diff |= _DIFF(CT_ATTR_STATUS, (a->ct_status ^ b->ct_status) & + b->ct_status_mask); + else + diff |= _DIFF(CT_ATTR_STATUS, a->ct_status != b->ct_status); +#undef _DIFF +#undef _DIFF_VAL +#undef _DIFF_ADDR + + return diff; +} + +static const struct trans_tbl ct_attrs[] = { + __ADD(CT_ATTR_FAMILY, family), + __ADD(CT_ATTR_PROTO, proto), + __ADD(CT_ATTR_TCP_STATE, tcpstate), + __ADD(CT_ATTR_STATUS, status), + __ADD(CT_ATTR_TIMEOUT, timeout), + __ADD(CT_ATTR_MARK, mark), + __ADD(CT_ATTR_USE, use), + __ADD(CT_ATTR_ID, id), + __ADD(CT_ATTR_ORIG_SRC, origsrc), + __ADD(CT_ATTR_ORIG_DST, origdst), + __ADD(CT_ATTR_ORIG_SRC_PORT, origsrcport), + __ADD(CT_ATTR_ORIG_DST_PORT, origdstport), + __ADD(CT_ATTR_ORIG_ICMP_ID, origicmpid), + __ADD(CT_ATTR_ORIG_ICMP_TYPE, origicmptype), + __ADD(CT_ATTR_ORIG_ICMP_CODE, origicmpcode), + __ADD(CT_ATTR_ORIG_PACKETS, origpackets), + __ADD(CT_ATTR_ORIG_BYTES, origbytes), + __ADD(CT_ATTR_REPL_SRC, replysrc), + __ADD(CT_ATTR_REPL_DST, replydst), + __ADD(CT_ATTR_REPL_SRC_PORT, replysrcport), + __ADD(CT_ATTR_REPL_DST_PORT, replydstport), + __ADD(CT_ATTR_REPL_ICMP_ID, replyicmpid), + __ADD(CT_ATTR_REPL_ICMP_TYPE, replyicmptype), + __ADD(CT_ATTR_REPL_ICMP_CODE, replyicmpcode), + __ADD(CT_ATTR_REPL_PACKETS, replypackets), + __ADD(CT_ATTR_REPL_BYTES, replybytes), +}; + +static char *ct_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, ct_attrs, ARRAY_SIZE(ct_attrs)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct nfnl_ct *nfnl_ct_alloc(void) +{ + return (struct nfnl_ct *) nl_object_alloc(&ct_obj_ops); +} + +void nfnl_ct_get(struct nfnl_ct *ct) +{ + nl_object_get((struct nl_object *) ct); +} + +void nfnl_ct_put(struct nfnl_ct *ct) +{ + nl_object_put((struct nl_object *) ct); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nfnl_ct_set_family(struct nfnl_ct *ct, uint8_t family) +{ + ct->ct_family = family; + ct->ce_mask |= CT_ATTR_FAMILY; +} + +uint8_t nfnl_ct_get_family(const struct nfnl_ct *ct) +{ + if (ct->ce_mask & CT_ATTR_FAMILY) + return ct->ct_family; + else + return AF_UNSPEC; +} + +void nfnl_ct_set_proto(struct nfnl_ct *ct, uint8_t proto) +{ + ct->ct_proto = proto; + ct->ce_mask |= CT_ATTR_PROTO; +} + +int nfnl_ct_test_proto(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_PROTO); +} + +uint8_t nfnl_ct_get_proto(const struct nfnl_ct *ct) +{ + return ct->ct_proto; +} + +void nfnl_ct_set_tcp_state(struct nfnl_ct *ct, uint8_t state) +{ + ct->ct_protoinfo.tcp.state = state; + ct->ce_mask |= CT_ATTR_TCP_STATE; +} + +int nfnl_ct_test_tcp_state(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_TCP_STATE); +} + +uint8_t nfnl_ct_get_tcp_state(const struct nfnl_ct *ct) +{ + return ct->ct_protoinfo.tcp.state; +} + +static const struct trans_tbl tcp_states[] = { + __ADD(TCP_CONNTRACK_NONE,NONE), + __ADD(TCP_CONNTRACK_SYN_SENT,SYN_SENT), + __ADD(TCP_CONNTRACK_SYN_RECV,SYN_RECV), + __ADD(TCP_CONNTRACK_ESTABLISHED,ESTABLISHED), + __ADD(TCP_CONNTRACK_FIN_WAIT,FIN_WAIT), + __ADD(TCP_CONNTRACK_CLOSE_WAIT,CLOSE_WAIT), + __ADD(TCP_CONNTRACK_LAST_ACK,LAST_ACK), + __ADD(TCP_CONNTRACK_TIME_WAIT,TIME_WAIT), + __ADD(TCP_CONNTRACK_CLOSE,CLOSE), + __ADD(TCP_CONNTRACK_LISTEN,LISTEN), +}; + +char *nfnl_ct_tcp_state2str(uint8_t state, char *buf, size_t len) +{ + return __type2str(state, buf, len, tcp_states, ARRAY_SIZE(tcp_states)); +} + +int nfnl_ct_str2tcp_state(const char *name) +{ + return __str2type(name, tcp_states, ARRAY_SIZE(tcp_states)); +} + +void nfnl_ct_set_status(struct nfnl_ct *ct, uint32_t status) +{ + ct->ct_status_mask |= status; + ct->ct_status |= status; + ct->ce_mask |= CT_ATTR_STATUS; +} + +void nfnl_ct_unset_status(struct nfnl_ct *ct, uint32_t status) +{ + ct->ct_status_mask |= status; + ct->ct_status &= ~status; + ct->ce_mask |= CT_ATTR_STATUS; +} + +int nfnl_ct_test_status(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_STATUS); +} + +uint32_t nfnl_ct_get_status(const struct nfnl_ct *ct) +{ + return ct->ct_status; +} + +static const struct trans_tbl status_flags[] = { + __ADD(IPS_EXPECTED, expected), + __ADD(IPS_SEEN_REPLY, seen_reply), + __ADD(IPS_ASSURED, assured), + __ADD(IPS_CONFIRMED, confirmed), + __ADD(IPS_SRC_NAT, snat), + __ADD(IPS_DST_NAT, dnat), + __ADD(IPS_SEQ_ADJUST, seqadjust), + __ADD(IPS_SRC_NAT_DONE, snat_done), + __ADD(IPS_DST_NAT_DONE, dnat_done), + __ADD(IPS_DYING, dying), + __ADD(IPS_FIXED_TIMEOUT, fixed_timeout), +}; + +char * nfnl_ct_status2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, status_flags, + ARRAY_SIZE(status_flags)); +} + +int nfnl_ct_str2status(const char *name) +{ + return __str2flags(name, status_flags, ARRAY_SIZE(status_flags)); +} + +void nfnl_ct_set_timeout(struct nfnl_ct *ct, uint32_t timeout) +{ + ct->ct_timeout = timeout; + ct->ce_mask |= CT_ATTR_TIMEOUT; +} + +int nfnl_ct_test_timeout(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_TIMEOUT); +} + +uint32_t nfnl_ct_get_timeout(const struct nfnl_ct *ct) +{ + return ct->ct_timeout; +} + +void nfnl_ct_set_mark(struct nfnl_ct *ct, uint32_t mark) +{ + ct->ct_mark = mark; + ct->ce_mask |= CT_ATTR_MARK; +} + +int nfnl_ct_test_mark(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_MARK); +} + +uint32_t nfnl_ct_get_mark(const struct nfnl_ct *ct) +{ + return ct->ct_mark; +} + +void nfnl_ct_set_use(struct nfnl_ct *ct, uint32_t use) +{ + ct->ct_use = use; + ct->ce_mask |= CT_ATTR_USE; +} + +int nfnl_ct_test_use(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_USE); +} + +uint32_t nfnl_ct_get_use(const struct nfnl_ct *ct) +{ + return ct->ct_use; +} + +void nfnl_ct_set_id(struct nfnl_ct *ct, uint32_t id) +{ + ct->ct_id = id; + ct->ce_mask |= CT_ATTR_ID; +} + +int nfnl_ct_test_id(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_ID); +} + +uint32_t nfnl_ct_get_id(const struct nfnl_ct *ct) +{ + return ct->ct_id; +} + +void nfnl_ct_set_zone(struct nfnl_ct *ct, uint16_t zone) +{ + ct->ct_zone = zone; + ct->ce_mask |= CT_ATTR_ZONE; +} + +int nfnl_ct_test_zone(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_ZONE); +} + +uint16_t nfnl_ct_get_zone(const struct nfnl_ct *ct) +{ + return ct->ct_zone; +} + +static int ct_set_addr(struct nfnl_ct *ct, struct nl_addr *addr, + int attr, struct nl_addr ** ct_addr) +{ + if (ct->ce_mask & CT_ATTR_FAMILY) { + if (addr->a_family != ct->ct_family) + return -NLE_AF_MISMATCH; + } else + nfnl_ct_set_family(ct, addr->a_family); + + if (*ct_addr) + nl_addr_put(*ct_addr); + + nl_addr_get(addr); + *ct_addr = addr; + ct->ce_mask |= attr; + + return 0; +} + +int nfnl_ct_set_src(struct nfnl_ct *ct, int repl, struct nl_addr *addr) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_SRC : CT_ATTR_ORIG_SRC; + return ct_set_addr(ct, addr, attr, &dir->src); +} + +int nfnl_ct_set_dst(struct nfnl_ct *ct, int repl, struct nl_addr *addr) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_DST : CT_ATTR_ORIG_DST; + return ct_set_addr(ct, addr, attr, &dir->dst); +} + +struct nl_addr *nfnl_ct_get_src(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_SRC : CT_ATTR_ORIG_SRC; + if (!(ct->ce_mask & attr)) + return NULL; + return dir->src; +} + +struct nl_addr *nfnl_ct_get_dst(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_DST : CT_ATTR_ORIG_DST; + if (!(ct->ce_mask & attr)) + return NULL; + return dir->dst; +} + +void nfnl_ct_set_src_port(struct nfnl_ct *ct, int repl, uint16_t port) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_SRC_PORT : CT_ATTR_ORIG_SRC_PORT; + + dir->proto.port.src = port; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_src_port(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_SRC_PORT : CT_ATTR_ORIG_SRC_PORT; + return !!(ct->ce_mask & attr); +} + +uint16_t nfnl_ct_get_src_port(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->proto.port.src; +} + +void nfnl_ct_set_dst_port(struct nfnl_ct *ct, int repl, uint16_t port) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_DST_PORT : CT_ATTR_ORIG_DST_PORT; + + dir->proto.port.dst = port; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_dst_port(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_DST_PORT : CT_ATTR_ORIG_DST_PORT; + return !!(ct->ce_mask & attr); +} + +uint16_t nfnl_ct_get_dst_port(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->proto.port.dst; +} + +void nfnl_ct_set_icmp_id(struct nfnl_ct *ct, int repl, uint16_t id) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_ICMP_ID : CT_ATTR_ORIG_ICMP_ID; + + dir->proto.icmp.id = id; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_icmp_id(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_ICMP_ID : CT_ATTR_ORIG_ICMP_ID; + return !!(ct->ce_mask & attr); +} + +uint16_t nfnl_ct_get_icmp_id(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->proto.icmp.id; +} + +void nfnl_ct_set_icmp_type(struct nfnl_ct *ct, int repl, uint8_t type) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_ICMP_TYPE : CT_ATTR_ORIG_ICMP_TYPE; + + dir->proto.icmp.type = type; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_icmp_type(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_ICMP_TYPE : CT_ATTR_ORIG_ICMP_TYPE; + return !!(ct->ce_mask & attr); +} + +uint8_t nfnl_ct_get_icmp_type(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->proto.icmp.type; +} + +void nfnl_ct_set_icmp_code(struct nfnl_ct *ct, int repl, uint8_t code) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_ICMP_CODE : CT_ATTR_ORIG_ICMP_CODE; + + dir->proto.icmp.code = code; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_icmp_code(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_ICMP_CODE : CT_ATTR_ORIG_ICMP_CODE; + return !!(ct->ce_mask & attr); +} + +uint8_t nfnl_ct_get_icmp_code(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->proto.icmp.code; +} + +void nfnl_ct_set_packets(struct nfnl_ct *ct, int repl, uint64_t packets) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_PACKETS : CT_ATTR_ORIG_PACKETS; + + dir->packets = packets; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_packets(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_PACKETS : CT_ATTR_ORIG_PACKETS; + return !!(ct->ce_mask & attr); +} + +uint64_t nfnl_ct_get_packets(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->packets; +} + +void nfnl_ct_set_bytes(struct nfnl_ct *ct, int repl, uint64_t bytes) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_BYTES : CT_ATTR_ORIG_BYTES; + + dir->bytes = bytes; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_bytes(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_BYTES : CT_ATTR_ORIG_BYTES; + return !!(ct->ce_mask & attr); +} + +uint64_t nfnl_ct_get_bytes(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->bytes; +} + +void nfnl_ct_set_timestamp(struct nfnl_ct *ct, uint64_t start, uint64_t stop) +{ + ct->ct_tstamp.start = start; + ct->ct_tstamp.stop = stop; + ct->ce_mask |= CT_ATTR_TIMESTAMP; +} + +int nfnl_ct_test_timestamp(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_TIMESTAMP); +} + +const struct nfnl_ct_timestamp *nfnl_ct_get_timestamp(const struct nfnl_ct *ct) +{ + return &ct->ct_tstamp; +} + +/** @} */ + +struct nl_object_ops ct_obj_ops = { + .oo_name = "netfilter/ct", + .oo_size = sizeof(struct nfnl_ct), + .oo_free_data = ct_free_data, + .oo_clone = ct_clone, + .oo_dump = { + [NL_DUMP_LINE] = ct_dump_line, + [NL_DUMP_DETAILS] = ct_dump_details, + [NL_DUMP_STATS] = ct_dump_stats, + }, + .oo_compare = ct_compare, + .oo_attrs2str = ct_attrs2str, +}; + +/** @} */ diff --git a/libnl/lib/netfilter/exp.c b/libnl/lib/netfilter/exp.c new file mode 100644 index 0000000..b0832d6 --- /dev/null +++ b/libnl/lib/netfilter/exp.c @@ -0,0 +1,628 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2008 Patrick McHardy + * Copyright (c) 2012 Rich Fought + */ + +/** + * @ingroup nfnl + * @defgroup exp Expectation + * @brief + * @{ + */ + +#include "nl-default.h" + +#include + +#include + +#include +#include +#include + +#include "nl-netfilter.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" + +static struct nl_cache_ops nfnl_exp_ops; + +static struct nla_policy exp_policy[CTA_EXPECT_MAX+1] = { + [CTA_EXPECT_MASTER] = { .type = NLA_NESTED }, + [CTA_EXPECT_TUPLE] = { .type = NLA_NESTED }, + [CTA_EXPECT_MASK] = { .type = NLA_NESTED }, + [CTA_EXPECT_TIMEOUT] = { .type = NLA_U32 }, + [CTA_EXPECT_ID] = { .type = NLA_U32 }, + [CTA_EXPECT_HELP_NAME] = { .type = NLA_STRING }, + [CTA_EXPECT_ZONE] = { .type = NLA_U16 }, + [CTA_EXPECT_FLAGS] = { .type = NLA_U32 }, // Added in kernel 2.6.37 + [CTA_EXPECT_CLASS] = { .type = NLA_U32 }, // Added in kernel 3.5 + [CTA_EXPECT_NAT] = { .type = NLA_NESTED }, // Added in kernel 3.5 + [CTA_EXPECT_FN] = { .type = NLA_STRING }, // Added in kernel 3.5 +}; + +static struct nla_policy exp_tuple_policy[CTA_TUPLE_MAX+1] = { + [CTA_TUPLE_IP] = { .type = NLA_NESTED }, + [CTA_TUPLE_PROTO] = { .type = NLA_NESTED }, +}; + +static struct nla_policy exp_ip_policy[CTA_IP_MAX+1] = { + [CTA_IP_V4_SRC] = { .type = NLA_U32 }, + [CTA_IP_V4_DST] = { .type = NLA_U32 }, + [CTA_IP_V6_SRC] = { .minlen = 16 }, + [CTA_IP_V6_DST] = { .minlen = 16 }, +}; + +static struct nla_policy exp_proto_policy[CTA_PROTO_MAX+1] = { + [CTA_PROTO_NUM] = { .type = NLA_U8 }, + [CTA_PROTO_SRC_PORT] = { .type = NLA_U16 }, + [CTA_PROTO_DST_PORT] = { .type = NLA_U16 }, + [CTA_PROTO_ICMP_ID] = { .type = NLA_U16 }, + [CTA_PROTO_ICMP_TYPE] = { .type = NLA_U8 }, + [CTA_PROTO_ICMP_CODE] = { .type = NLA_U8 }, + [CTA_PROTO_ICMPV6_ID] = { .type = NLA_U16 }, + [CTA_PROTO_ICMPV6_TYPE] = { .type = NLA_U8 }, + [CTA_PROTO_ICMPV6_CODE] = { .type = NLA_U8 }, +}; + +static struct nla_policy exp_nat_policy[CTA_EXPECT_NAT_MAX+1] = { + [CTA_EXPECT_NAT_DIR] = { .type = NLA_U32 }, + [CTA_EXPECT_NAT_TUPLE] = { .type = NLA_NESTED }, +}; + +static int exp_parse_ip(struct nfnl_exp *exp, int tuple, struct nlattr *attr) +{ + struct nlattr *tb[CTA_IP_MAX+1]; + struct nl_addr *addr; + int err; + + err = nla_parse_nested(tb, CTA_IP_MAX, attr, exp_ip_policy); + if (err < 0) + goto errout; + + if (tb[CTA_IP_V4_SRC]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V4_SRC], AF_INET); + if (addr == NULL) + goto errout_enomem; + err = nfnl_exp_set_src(exp, tuple, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + if (tb[CTA_IP_V4_DST]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V4_DST], AF_INET); + if (addr == NULL) + goto errout_enomem; + err = nfnl_exp_set_dst(exp, tuple, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + if (tb[CTA_IP_V6_SRC]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V6_SRC], AF_INET6); + if (addr == NULL) + goto errout_enomem; + err = nfnl_exp_set_src(exp, tuple, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + if (tb[CTA_IP_V6_DST]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V6_DST], AF_INET6); + if (addr == NULL) + goto errout_enomem; + err = nfnl_exp_set_dst(exp, tuple, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + + return 0; + +errout_enomem: + err = -NLE_NOMEM; +errout: + return err; +} + +static int exp_parse_proto(struct nfnl_exp *exp, int tuple, struct nlattr *attr) +{ + struct nlattr *tb[CTA_PROTO_MAX+1]; + int err; + uint16_t srcport = 0, dstport = 0, icmpid = 0; + uint8_t icmptype = 0, icmpcode = 0; + + err = nla_parse_nested(tb, CTA_PROTO_MAX, attr, exp_proto_policy); + if (err < 0) + return err; + + if (tb[CTA_PROTO_NUM]) + nfnl_exp_set_l4protonum(exp, tuple, nla_get_u8(tb[CTA_PROTO_NUM])); + + if (tb[CTA_PROTO_SRC_PORT]) + srcport = ntohs(nla_get_u16(tb[CTA_PROTO_SRC_PORT])); + if (tb[CTA_PROTO_DST_PORT]) + dstport = ntohs(nla_get_u16(tb[CTA_PROTO_DST_PORT])); + if (tb[CTA_PROTO_SRC_PORT] || tb[CTA_PROTO_DST_PORT]) + nfnl_exp_set_ports(exp, tuple, srcport, dstport); + + if (tb[CTA_PROTO_ICMP_ID]) + icmpid = ntohs(nla_get_u16(tb[CTA_PROTO_ICMP_ID])); + if (tb[CTA_PROTO_ICMP_TYPE]) + icmptype = nla_get_u8(tb[CTA_PROTO_ICMP_TYPE]); + if (tb[CTA_PROTO_ICMP_CODE]) + icmpcode = nla_get_u8(tb[CTA_PROTO_ICMP_CODE]); + if (tb[CTA_PROTO_ICMP_ID] || tb[CTA_PROTO_ICMP_TYPE] || tb[CTA_PROTO_ICMP_CODE]) + nfnl_exp_set_icmp(exp, tuple, icmpid, icmptype, icmpcode); + return 0; +} + +static int exp_parse_tuple(struct nfnl_exp *exp, int tuple, struct nlattr *attr) +{ + struct nlattr *tb[CTA_TUPLE_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_TUPLE_MAX, attr, exp_tuple_policy); + if (err < 0) + return err; + + if (tb[CTA_TUPLE_IP]) { + err = exp_parse_ip(exp, tuple, tb[CTA_TUPLE_IP]); + if (err < 0) + return err; + } + + if (tb[CTA_TUPLE_PROTO]) { + err = exp_parse_proto(exp, tuple, tb[CTA_TUPLE_PROTO]); + if (err < 0) + return err; + } + + return 0; +} + +static int exp_parse_nat(struct nfnl_exp *exp, struct nlattr *attr) +{ + struct nlattr *tb[CTA_EXPECT_NAT_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_EXPECT_NAT_MAX, attr, exp_nat_policy); + if (err < 0) + return err; + + if (tb[CTA_EXPECT_NAT_DIR]) + nfnl_exp_set_nat_dir(exp, nla_get_u32(tb[CTA_EXPECT_NAT_DIR])); + + if (tb[CTA_EXPECT_NAT_TUPLE]) { + err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_NAT, tb[CTA_EXPECT_NAT_TUPLE]); + if (err < 0) + return err; + } + + return 0; +} + +int nfnlmsg_exp_group(struct nlmsghdr *nlh) +{ + switch (nfnlmsg_subtype(nlh)) { + case IPCTNL_MSG_EXP_NEW: + if (nlh->nlmsg_flags & (NLM_F_CREATE|NLM_F_EXCL)) + return NFNLGRP_CONNTRACK_EXP_NEW; + else + return NFNLGRP_CONNTRACK_EXP_UPDATE; + case IPCTNL_MSG_EXP_DELETE: + return NFNLGRP_CONNTRACK_EXP_DESTROY; + default: + return NFNLGRP_NONE; + } +} + +int nfnlmsg_exp_parse(struct nlmsghdr *nlh, struct nfnl_exp **result) +{ + struct nfnl_exp *exp; + struct nlattr *tb[CTA_MAX+1]; + int err; + + exp = nfnl_exp_alloc(); + if (!exp) + return -NLE_NOMEM; + + exp->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, CTA_EXPECT_MAX, + exp_policy); + if (err < 0) + goto errout; + + nfnl_exp_set_family(exp, nfnlmsg_family(nlh)); + + if (tb[CTA_EXPECT_TUPLE]) { + err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_EXPECT, tb[CTA_EXPECT_TUPLE]); + if (err < 0) + goto errout; + } + if (tb[CTA_EXPECT_MASTER]) { + err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_MASTER, tb[CTA_EXPECT_MASTER]); + if (err < 0) + goto errout; + } + if (tb[CTA_EXPECT_MASK]) { + err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_MASK, tb[CTA_EXPECT_MASK]); + if (err < 0) + goto errout; + } + + if (tb[CTA_EXPECT_NAT]) { + err = exp_parse_nat(exp, tb[CTA_EXPECT_MASK]); + if (err < 0) + goto errout; + } + + if (tb[CTA_EXPECT_CLASS]) + nfnl_exp_set_class(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_CLASS]))); + + if (tb[CTA_EXPECT_FN]) + nfnl_exp_set_fn(exp, nla_data(tb[CTA_EXPECT_FN])); + + if (tb[CTA_EXPECT_TIMEOUT]) + nfnl_exp_set_timeout(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_TIMEOUT]))); + + if (tb[CTA_EXPECT_ID]) + nfnl_exp_set_id(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_ID]))); + + if (tb[CTA_EXPECT_HELP_NAME]) + nfnl_exp_set_helper_name(exp, nla_data(tb[CTA_EXPECT_HELP_NAME])); + + if (tb[CTA_EXPECT_ZONE]) + nfnl_exp_set_zone(exp, ntohs(nla_get_u16(tb[CTA_EXPECT_ZONE]))); + + if (tb[CTA_EXPECT_FLAGS]) + nfnl_exp_set_flags(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_FLAGS]))); + + *result = exp; + return 0; + +errout: + nfnl_exp_put(exp); + return err; +} + +static int exp_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct nfnl_exp *exp; + int err; + + if ((err = nfnlmsg_exp_parse(nlh, &exp)) < 0) + return err; + + err = pp->pp_cb((struct nl_object *) exp, pp); + nfnl_exp_put(exp); + return err; +} + +/** + * Send nfnl exp dump request + * @arg sk Netlink socket. + * + * @return 0 on success or a negative error code. Due to a bug, this function + * returns the number of bytes sent. Treat any non-negative number as success. + */ +int nfnl_exp_dump_request(struct nl_sock *sk) +{ + return nfnl_send_simple(sk, NFNL_SUBSYS_CTNETLINK_EXP, IPCTNL_MSG_EXP_GET, + NLM_F_DUMP, AF_UNSPEC, 0); +} + +static int exp_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + return nfnl_exp_dump_request(sk); +} + +static int exp_get_tuple_attr(int tuple) +{ + int attr = 0; + + switch (tuple) { + case CTA_EXPECT_MASTER: + attr = NFNL_EXP_TUPLE_MASTER; + break; + case CTA_EXPECT_MASK: + attr = NFNL_EXP_TUPLE_MASK; + break; + case CTA_EXPECT_NAT: + attr = NFNL_EXP_TUPLE_NAT; + break; + case CTA_EXPECT_TUPLE: + default : + attr = NFNL_EXP_TUPLE_EXPECT; + break; + } + + return attr; +} + +static int nfnl_exp_build_tuple(struct nl_msg *msg, const struct nfnl_exp *exp, + int cta) +{ + struct nlattr *tuple, *ip, *proto; + struct nl_addr *addr; + int family; + int type; + + family = nfnl_exp_get_family(exp); + + type = exp_get_tuple_attr(cta); + + if (cta == CTA_EXPECT_NAT) + tuple = nla_nest_start(msg, CTA_EXPECT_NAT_TUPLE); + else + tuple = nla_nest_start(msg, cta); + + if (!tuple) + goto nla_put_failure; + + ip = nla_nest_start(msg, CTA_TUPLE_IP); + if (!ip) + goto nla_put_failure; + + addr = nfnl_exp_get_src(exp, type); + if (addr) + NLA_PUT_ADDR(msg, + family == AF_INET ? CTA_IP_V4_SRC : CTA_IP_V6_SRC, + addr); + + addr = nfnl_exp_get_dst(exp, type); + if (addr) + NLA_PUT_ADDR(msg, + family == AF_INET ? CTA_IP_V4_DST : CTA_IP_V6_DST, + addr); + + nla_nest_end(msg, ip); + + proto = nla_nest_start(msg, CTA_TUPLE_PROTO); + if (!proto) + goto nla_put_failure; + + if (nfnl_exp_test_l4protonum(exp, type)) + NLA_PUT_U8(msg, CTA_PROTO_NUM, nfnl_exp_get_l4protonum(exp, type)); + + if (nfnl_exp_test_ports(exp, type)) { + NLA_PUT_U16(msg, CTA_PROTO_SRC_PORT, + htons(nfnl_exp_get_src_port(exp, type))); + + NLA_PUT_U16(msg, CTA_PROTO_DST_PORT, + htons(nfnl_exp_get_dst_port(exp, type))); + } + + if (nfnl_exp_test_icmp(exp, type)) { + NLA_PUT_U16(msg, CTA_PROTO_ICMP_ID, + htons(nfnl_exp_get_icmp_id(exp, type))); + + NLA_PUT_U8(msg, CTA_PROTO_ICMP_TYPE, + nfnl_exp_get_icmp_type(exp, type)); + + NLA_PUT_U8(msg, CTA_PROTO_ICMP_CODE, + nfnl_exp_get_icmp_code(exp, type)); + } + + nla_nest_end(msg, proto); + + nla_nest_end(msg, tuple); + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int nfnl_exp_build_nat(struct nl_msg *msg, const struct nfnl_exp *exp) +{ + struct nlattr *nat; + + nat = nla_nest_start(msg, CTA_EXPECT_NAT); + + if (nfnl_exp_test_nat_dir(exp)) { + NLA_PUT_U32(msg, CTA_EXPECT_NAT_DIR, + nfnl_exp_get_nat_dir(exp)); + } + + if (nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_NAT) < 0) + goto nla_put_failure; + + nla_nest_end(msg, nat); + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int nfnl_exp_build_message(const struct nfnl_exp *exp, int cmd, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + int err; + + msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_CTNETLINK_EXP, cmd, flags, + nfnl_exp_get_family(exp), 0); + if (msg == NULL) + return -NLE_NOMEM; + + if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_TUPLE)) < 0) + goto err_out; + + if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_MASTER)) < 0) + goto err_out; + + if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_MASK)) < 0) + goto err_out; + + if (nfnl_exp_test_src(exp, NFNL_EXP_TUPLE_NAT)) { + if ((err = nfnl_exp_build_nat(msg, exp)) < 0) + goto err_out; + } + + if (nfnl_exp_test_class(exp)) + NLA_PUT_U32(msg, CTA_EXPECT_CLASS, htonl(nfnl_exp_get_class(exp))); + + if (nfnl_exp_test_fn(exp)) + NLA_PUT_STRING(msg, CTA_EXPECT_FN, nfnl_exp_get_fn(exp)); + + if (nfnl_exp_test_id(exp)) + NLA_PUT_U32(msg, CTA_EXPECT_ID, htonl(nfnl_exp_get_id(exp))); + + if (nfnl_exp_test_timeout(exp)) + NLA_PUT_U32(msg, CTA_EXPECT_TIMEOUT, htonl(nfnl_exp_get_timeout(exp))); + + if (nfnl_exp_test_helper_name(exp)) + NLA_PUT_STRING(msg, CTA_EXPECT_HELP_NAME, nfnl_exp_get_helper_name(exp)); + + if (nfnl_exp_test_zone(exp)) + NLA_PUT_U16(msg, CTA_EXPECT_ZONE, htons(nfnl_exp_get_zone(exp))); + + if (nfnl_exp_test_flags(exp)) + NLA_PUT_U32(msg, CTA_EXPECT_FLAGS, htonl(nfnl_exp_get_flags(exp))); + + *result = msg; + return 0; + +nla_put_failure: + err = -NLE_NOMEM; + +err_out: + nlmsg_free(msg); + return err; +} + +int nfnl_exp_build_add_request(const struct nfnl_exp *exp, int flags, + struct nl_msg **result) +{ + return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_NEW, flags, result); +} + +int nfnl_exp_add(struct nl_sock *sk, const struct nfnl_exp *exp, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_exp_build_add_request(exp, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +int nfnl_exp_build_delete_request(const struct nfnl_exp *exp, int flags, + struct nl_msg **result) +{ + return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_DELETE, flags, result); +} + +int nfnl_exp_del(struct nl_sock *sk, const struct nfnl_exp *exp, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_exp_build_delete_request(exp, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +int nfnl_exp_build_query_request(const struct nfnl_exp *exp, int flags, + struct nl_msg **result) +{ + return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_GET, flags, result); +} + +int nfnl_exp_query(struct nl_sock *sk, const struct nfnl_exp *exp, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_exp_build_query_request(exp, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** + * @name Cache Management + * @{ + */ + +/** + * Build a expectation cache holding all expectations currently in the kernel + * @arg sk Netlink socket. + * @arg result Pointer to store resulting cache. + * + * Allocates a new cache, initializes it properly and updates it to + * contain all expectations currently in the kernel. + * + * @return 0 on success or a negative error code. + */ +int nfnl_exp_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&nfnl_exp_ops, sk, result); +} + +/** @} */ + +/** + * @name Expectation Addition + * @{ + */ + +/** @} */ + +static struct nl_af_group exp_groups[] = { + { AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_NEW }, + { AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_UPDATE }, + { AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_DESTROY }, + { END_OF_GROUP_LIST }, +}; + +#define NFNLMSG_EXP_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_CTNETLINK_EXP, (type)) +static struct nl_cache_ops nfnl_exp_ops = { + .co_name = "netfilter/exp", + .co_hdrsize = NFNL_HDRLEN, + .co_msgtypes = { + { NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_NEW), NL_ACT_NEW, "new" }, + { NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_GET), NL_ACT_GET, "get" }, + { NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_DELETE), NL_ACT_DEL, "del" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_NETFILTER, + .co_groups = exp_groups, + .co_request_update = exp_request_update, + .co_msg_parser = exp_msg_parser, + .co_obj_ops = &exp_obj_ops, +}; + +static void _nl_init exp_init(void) +{ + nl_cache_mngt_register(&nfnl_exp_ops); +} + +static void _nl_exit exp_exit(void) +{ + nl_cache_mngt_unregister(&nfnl_exp_ops); +} + +/** @} */ diff --git a/libnl/lib/netfilter/exp_obj.c b/libnl/lib/netfilter/exp_obj.c new file mode 100644 index 0000000..8b78898 --- /dev/null +++ b/libnl/lib/netfilter/exp_obj.c @@ -0,0 +1,903 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2012 Rich Fought + */ + +#include "nl-default.h" + +#include + +#include +#include + +#include + +#include +#include + +#include "nl-priv-dynamic-core/object-api.h" +#include "nl-netfilter.h" +#include "nl-priv-dynamic-core/nl-core.h" + +// The 32-bit attribute mask in the common object header isn't +// big enough to handle all attributes of an expectation. So +// we'll for sure specify optional attributes + parent attributes +// that are required for valid object comparison. Comparison of +// these parent attributes will include nested attributes. + +/** @cond SKIP */ +#define EXP_ATTR_FAMILY (1UL << 0) // 8-bit +#define EXP_ATTR_TIMEOUT (1UL << 1) // 32-bit +#define EXP_ATTR_ID (1UL << 2) // 32-bit +#define EXP_ATTR_HELPER_NAME (1UL << 3) // string +#define EXP_ATTR_ZONE (1UL << 4) // 16-bit +#define EXP_ATTR_FLAGS (1UL << 5) // 32-bit +#define EXP_ATTR_CLASS (1UL << 6) // 32-bit +#define EXP_ATTR_FN (1UL << 7) // String +// Tuples +#define EXP_ATTR_EXPECT_IP_SRC (1UL << 8) +#define EXP_ATTR_EXPECT_IP_DST (1UL << 9) +#define EXP_ATTR_EXPECT_L4PROTO_NUM (1UL << 10) +#define EXP_ATTR_EXPECT_L4PROTO_PORTS (1UL << 11) +#define EXP_ATTR_EXPECT_L4PROTO_ICMP (1UL << 12) +#define EXP_ATTR_MASTER_IP_SRC (1UL << 13) +#define EXP_ATTR_MASTER_IP_DST (1UL << 14) +#define EXP_ATTR_MASTER_L4PROTO_NUM (1UL << 15) +#define EXP_ATTR_MASTER_L4PROTO_PORTS (1UL << 16) +#define EXP_ATTR_MASTER_L4PROTO_ICMP (1UL << 17) +#define EXP_ATTR_MASK_IP_SRC (1UL << 18) +#define EXP_ATTR_MASK_IP_DST (1UL << 19) +#define EXP_ATTR_MASK_L4PROTO_NUM (1UL << 20) +#define EXP_ATTR_MASK_L4PROTO_PORTS (1UL << 21) +#define EXP_ATTR_MASK_L4PROTO_ICMP (1UL << 22) +#define EXP_ATTR_NAT_IP_SRC (1UL << 23) +#define EXP_ATTR_NAT_IP_DST (1UL << 24) +#define EXP_ATTR_NAT_L4PROTO_NUM (1UL << 25) +#define EXP_ATTR_NAT_L4PROTO_PORTS (1UL << 26) +#define EXP_ATTR_NAT_L4PROTO_ICMP (1UL << 27) +#define EXP_ATTR_NAT_DIR (1UL << 28) +/** @endcond */ + +static void exp_free_data(struct nl_object *c) +{ + struct nfnl_exp *exp = (struct nfnl_exp *) c; + + if (exp == NULL) + return; + + nl_addr_put(exp->exp_expect.src); + nl_addr_put(exp->exp_expect.dst); + nl_addr_put(exp->exp_master.src); + nl_addr_put(exp->exp_master.dst); + nl_addr_put(exp->exp_mask.src); + nl_addr_put(exp->exp_mask.dst); + nl_addr_put(exp->exp_nat.src); + nl_addr_put(exp->exp_nat.dst); + + free(exp->exp_fn); + free(exp->exp_helper_name); +} + +static int exp_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct nfnl_exp *dst = (struct nfnl_exp *) _dst; + struct nfnl_exp *src = (struct nfnl_exp *) _src; + struct nl_addr *addr; + + dst->exp_helper_name = NULL; + dst->exp_fn = NULL; + dst->exp_expect.src = NULL; + dst->exp_expect.dst = NULL; + dst->exp_master.src = NULL; + dst->exp_master.dst = NULL; + dst->exp_mask.src = NULL; + dst->exp_mask.dst = NULL; + dst->exp_nat.src = NULL; + dst->exp_nat.dst = NULL; + + if (src->exp_expect.src) { + addr = nl_addr_clone(src->exp_expect.src); + if (!addr) + return -NLE_NOMEM; + dst->exp_expect.src = addr; + } + + if (src->exp_expect.dst) { + addr = nl_addr_clone(src->exp_expect.dst); + if (!addr) + return -NLE_NOMEM; + dst->exp_expect.dst = addr; + } + + if (src->exp_master.src) { + addr = nl_addr_clone(src->exp_master.src); + if (!addr) + return -NLE_NOMEM; + dst->exp_master.src = addr; + } + + if (src->exp_master.dst) { + addr = nl_addr_clone(src->exp_master.dst); + if (!addr) + return -NLE_NOMEM; + dst->exp_master.dst = addr; + } + + if (src->exp_mask.src) { + addr = nl_addr_clone(src->exp_mask.src); + if (!addr) + return -NLE_NOMEM; + dst->exp_mask.src = addr; + } + + if (src->exp_mask.dst) { + addr = nl_addr_clone(src->exp_mask.dst); + if (!addr) + return -NLE_NOMEM; + dst->exp_mask.dst = addr; + } + + if (src->exp_nat.src) { + addr = nl_addr_clone(src->exp_nat.src); + if (!addr) + return -NLE_NOMEM; + dst->exp_nat.src = addr; + } + + if (src->exp_nat.dst) { + addr = nl_addr_clone(src->exp_nat.dst); + if (!addr) + return -NLE_NOMEM; + dst->exp_nat.dst = addr; + } + + if (src->exp_fn) + dst->exp_fn = strdup(src->exp_fn); + + if (src->exp_helper_name) + dst->exp_helper_name = strdup(src->exp_helper_name); + + return 0; +} + +static void dump_addr(struct nl_dump_params *p, struct nl_addr *addr, int port) +{ + char buf[64]; + + if (addr) + nl_dump(p, "%s", nl_addr2str(addr, buf, sizeof(buf))); + + if (port) + nl_dump(p, ":%u ", port); + else if (addr) + nl_dump(p, " "); +} + +static void dump_icmp(struct nl_dump_params *p, struct nfnl_exp *exp, int tuple) +{ + if (nfnl_exp_test_icmp(exp, tuple)) { + + nl_dump(p, "icmp type %d ", nfnl_exp_get_icmp_type(exp, tuple)); + + nl_dump(p, "code %d ", nfnl_exp_get_icmp_code(exp, tuple)); + + nl_dump(p, "id %d ", nfnl_exp_get_icmp_id(exp, tuple)); + } +} + +static void exp_dump_tuples(struct nfnl_exp *exp, struct nl_dump_params *p) +{ + struct nl_addr *tuple_src, *tuple_dst; + int tuple_sport, tuple_dport; + int i = 0; + char buf[64]; + + for (i = NFNL_EXP_TUPLE_EXPECT; i < NFNL_EXP_TUPLE_MAX; i++) { + tuple_src = NULL; + tuple_dst = NULL; + tuple_sport = 0; + tuple_dport = 0; + + // Test needed for NAT case + if (nfnl_exp_test_src(exp, i)) + tuple_src = nfnl_exp_get_src(exp, i); + if (nfnl_exp_test_dst(exp, i)) + tuple_dst = nfnl_exp_get_dst(exp, i); + + // Don't have tests for individual ports/types/codes/ids, + if (nfnl_exp_test_l4protonum(exp, i)) { + nl_dump(p, "%s ", + nl_ip_proto2str(nfnl_exp_get_l4protonum(exp, i), buf, sizeof(buf))); + } + + if (nfnl_exp_test_ports(exp, i)) { + tuple_sport = nfnl_exp_get_src_port(exp, i); + tuple_dport = nfnl_exp_get_dst_port(exp, i); + } + + dump_addr(p, tuple_src, tuple_sport); + dump_addr(p, tuple_dst, tuple_dport); + dump_icmp(p, exp, 0); + } + + if (nfnl_exp_test_nat_dir(exp)) + nl_dump(p, "nat dir %u ", exp->exp_nat_dir); + +} + +/* FIXME Compatible with /proc/net/nf_conntrack */ +static void exp_dump_line(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_exp *exp = (struct nfnl_exp *) a; + + nl_new_line(p); + + exp_dump_tuples(exp, p); + + nl_dump(p, "\n"); +} + +static void exp_dump_details(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_exp *exp = (struct nfnl_exp *) a; + char buf[64]; + int fp = 0; + + exp_dump_line(a, p); + + nl_dump(p, " id 0x%x ", exp->exp_id); + nl_dump_line(p, "family %s ", + nl_af2str(exp->exp_family, buf, sizeof(buf))); + + if (nfnl_exp_test_timeout(exp)) { + uint64_t timeout_ms = nfnl_exp_get_timeout(exp) * 1000UL; + nl_dump(p, "timeout %s ", + nl_msec2str(timeout_ms, buf, sizeof(buf))); + } + + if (nfnl_exp_test_helper_name(exp)) + nl_dump(p, "helper %s ", exp->exp_helper_name); + + if (nfnl_exp_test_fn(exp)) + nl_dump(p, "fn %s ", exp->exp_fn); + + if (nfnl_exp_test_class(exp)) + nl_dump(p, "class %u ", nfnl_exp_get_class(exp)); + + if (nfnl_exp_test_zone(exp)) + nl_dump(p, "zone %u ", nfnl_exp_get_zone(exp)); + + if (nfnl_exp_test_flags(exp)) + nl_dump(p, "<"); +#define PRINT_FLAG(str) \ + { nl_dump(p, "%s%s", fp++ ? "," : "", (str)); } + + if (exp->exp_flags & NF_CT_EXPECT_PERMANENT) + PRINT_FLAG("PERMANENT"); + if (exp->exp_flags & NF_CT_EXPECT_INACTIVE) + PRINT_FLAG("INACTIVE"); + if (exp->exp_flags & NF_CT_EXPECT_USERSPACE) + PRINT_FLAG("USERSPACE"); +#undef PRINT_FLAG + + if (nfnl_exp_test_flags(exp)) + nl_dump(p, ">"); + + nl_dump(p, "\n"); +} + +static int exp_cmp_l4proto_ports (union nfnl_exp_protodata *a, union nfnl_exp_protodata *b) { + // Must return 0 for match, 1 for mismatch + int d = 0; + d = ( (a->port.src != b->port.src) || + (a->port.dst != b->port.dst) ); + + return d; +} + +static int exp_cmp_l4proto_icmp (union nfnl_exp_protodata *a, union nfnl_exp_protodata *b) { + // Must return 0 for match, 1 for mismatch + int d = 0; + d = ( (a->icmp.code != b->icmp.code) || + (a->icmp.type != b->icmp.type) || + (a->icmp.id != b->icmp.id) ); + + return d; +} + +static uint64_t exp_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct nfnl_exp *a = (struct nfnl_exp *) _a; + struct nfnl_exp *b = (struct nfnl_exp *) _b; + uint64_t diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) +#define _DIFF_VAL(ATTR, FIELD) _DIFF(ATTR, a->FIELD != b->FIELD) +#define _DIFF_STRING(ATTR, FIELD) _DIFF(ATTR, (strcmp(a->FIELD, b->FIELD) != 0)) +#define _DIFF_ADDR(ATTR, FIELD) \ + ((flags & LOOSE_COMPARISON) ? \ + _DIFF(ATTR, nl_addr_cmp_prefix(a->FIELD, b->FIELD)) : \ + _DIFF(ATTR, nl_addr_cmp(a->FIELD, b->FIELD))) +#define _DIFF_L4PROTO_PORTS(ATTR, FIELD) \ + _DIFF(ATTR, exp_cmp_l4proto_ports(&(a->FIELD), &(b->FIELD))) +#define _DIFF_L4PROTO_ICMP(ATTR, FIELD) \ + _DIFF(ATTR, exp_cmp_l4proto_icmp(&(a->FIELD), &(b->FIELD))) + diff |= _DIFF_VAL(EXP_ATTR_FAMILY, exp_family); + diff |= _DIFF_VAL(EXP_ATTR_TIMEOUT, exp_timeout); + diff |= _DIFF_VAL(EXP_ATTR_ID, exp_id); + diff |= _DIFF_VAL(EXP_ATTR_ZONE, exp_zone); + diff |= _DIFF_VAL(EXP_ATTR_CLASS, exp_class); + diff |= _DIFF_VAL(EXP_ATTR_FLAGS, exp_flags); + diff |= _DIFF_VAL(EXP_ATTR_NAT_DIR, exp_nat_dir); + + diff |= _DIFF_STRING(EXP_ATTR_FN, exp_fn); + diff |= _DIFF_STRING(EXP_ATTR_HELPER_NAME, exp_helper_name); + + diff |= _DIFF_ADDR(EXP_ATTR_EXPECT_IP_SRC, exp_expect.src); + diff |= _DIFF_ADDR(EXP_ATTR_EXPECT_IP_DST, exp_expect.dst); + diff |= _DIFF_VAL(EXP_ATTR_EXPECT_L4PROTO_NUM, + exp_expect.proto.l4protonum); + diff |= _DIFF_L4PROTO_PORTS(EXP_ATTR_EXPECT_L4PROTO_PORTS, + exp_expect.proto.l4protodata); + diff |= _DIFF_L4PROTO_ICMP(EXP_ATTR_EXPECT_L4PROTO_ICMP, + exp_expect.proto.l4protodata); + + diff |= _DIFF_ADDR(EXP_ATTR_MASTER_IP_SRC, exp_master.src); + diff |= _DIFF_ADDR(EXP_ATTR_MASTER_IP_DST, exp_master.dst); + diff |= _DIFF_VAL(EXP_ATTR_MASTER_L4PROTO_NUM, + exp_master.proto.l4protonum); + diff |= _DIFF_L4PROTO_PORTS(EXP_ATTR_MASTER_L4PROTO_PORTS, + exp_master.proto.l4protodata); + diff |= _DIFF_L4PROTO_ICMP(EXP_ATTR_MASTER_L4PROTO_ICMP, + exp_master.proto.l4protodata); + + diff |= _DIFF_ADDR(EXP_ATTR_MASK_IP_SRC, exp_mask.src); + diff |= _DIFF_ADDR(EXP_ATTR_MASK_IP_DST, exp_mask.dst); + diff |= _DIFF_VAL(EXP_ATTR_MASK_L4PROTO_NUM, exp_mask.proto.l4protonum); + diff |= _DIFF_L4PROTO_PORTS(EXP_ATTR_MASK_L4PROTO_PORTS, + exp_mask.proto.l4protodata); + diff |= _DIFF_L4PROTO_ICMP(EXP_ATTR_MASK_L4PROTO_ICMP, + exp_mask.proto.l4protodata); + + diff |= _DIFF_ADDR(EXP_ATTR_NAT_IP_SRC, exp_nat.src); + diff |= _DIFF_ADDR(EXP_ATTR_NAT_IP_DST, exp_nat.dst); + diff |= _DIFF_VAL(EXP_ATTR_NAT_L4PROTO_NUM, exp_nat.proto.l4protonum); + diff |= _DIFF_L4PROTO_PORTS(EXP_ATTR_NAT_L4PROTO_PORTS, + exp_nat.proto.l4protodata); + diff |= _DIFF_L4PROTO_ICMP(EXP_ATTR_NAT_L4PROTO_ICMP, + exp_nat.proto.l4protodata); +#undef _DIFF +#undef _DIFF_VAL +#undef _DIFF_STRING +#undef _DIFF_ADDR +#undef _DIFF_L4PROTO_PORTS +#undef _DIFF_L4PROTO_ICMP + + return diff; +} + +// CLI arguments? +static const struct trans_tbl exp_attrs[] = { + __ADD(EXP_ATTR_FAMILY, family), + __ADD(EXP_ATTR_TIMEOUT, timeout), + __ADD(EXP_ATTR_ID, id), + __ADD(EXP_ATTR_HELPER_NAME, helpername), + __ADD(EXP_ATTR_ZONE, zone), + __ADD(EXP_ATTR_CLASS, class), + __ADD(EXP_ATTR_FLAGS, flags), + __ADD(EXP_ATTR_FN, function), + __ADD(EXP_ATTR_EXPECT_IP_SRC, expectipsrc), + __ADD(EXP_ATTR_EXPECT_IP_DST, expectipdst), + __ADD(EXP_ATTR_EXPECT_L4PROTO_NUM, expectprotonum), + __ADD(EXP_ATTR_EXPECT_L4PROTO_PORTS, expectports), + __ADD(EXP_ATTR_EXPECT_L4PROTO_ICMP, expecticmp), + __ADD(EXP_ATTR_MASTER_IP_SRC, masteripsrc), + __ADD(EXP_ATTR_MASTER_IP_DST, masteripdst), + __ADD(EXP_ATTR_MASTER_L4PROTO_NUM, masterprotonum), + __ADD(EXP_ATTR_MASTER_L4PROTO_PORTS, masterports), + __ADD(EXP_ATTR_MASTER_L4PROTO_ICMP, mastericmp), + __ADD(EXP_ATTR_MASK_IP_SRC, maskipsrc), + __ADD(EXP_ATTR_MASK_IP_DST, maskipdst), + __ADD(EXP_ATTR_MASK_L4PROTO_NUM, maskprotonum), + __ADD(EXP_ATTR_MASK_L4PROTO_PORTS, maskports), + __ADD(EXP_ATTR_MASK_L4PROTO_ICMP, maskicmp), + __ADD(EXP_ATTR_NAT_IP_SRC, natipsrc), + __ADD(EXP_ATTR_NAT_IP_DST, natipdst), + __ADD(EXP_ATTR_NAT_L4PROTO_NUM, natprotonum), + __ADD(EXP_ATTR_NAT_L4PROTO_PORTS, natports), + __ADD(EXP_ATTR_NAT_L4PROTO_ICMP, naticmp), + __ADD(EXP_ATTR_NAT_DIR, natdir), +}; + +static char *exp_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, exp_attrs, ARRAY_SIZE(exp_attrs)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct nfnl_exp *nfnl_exp_alloc(void) +{ + return (struct nfnl_exp *) nl_object_alloc(&exp_obj_ops); +} + +void nfnl_exp_get(struct nfnl_exp *exp) +{ + nl_object_get((struct nl_object *) exp); +} + +void nfnl_exp_put(struct nfnl_exp *exp) +{ + nl_object_put((struct nl_object *) exp); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nfnl_exp_set_family(struct nfnl_exp *exp, uint8_t family) +{ + exp->exp_family = family; + exp->ce_mask |= EXP_ATTR_FAMILY; +} + +uint8_t nfnl_exp_get_family(const struct nfnl_exp *exp) +{ + if (exp->ce_mask & EXP_ATTR_FAMILY) + return exp->exp_family; + else + return AF_UNSPEC; +} + +void nfnl_exp_set_flags(struct nfnl_exp *exp, uint32_t flags) +{ + exp->exp_flags |= flags; + exp->ce_mask |= EXP_ATTR_FLAGS; +} + +int nfnl_exp_test_flags(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_FLAGS); +} + +void nfnl_exp_unset_flags(struct nfnl_exp *exp, uint32_t flags) +{ + exp->exp_flags &= ~flags; + exp->ce_mask |= EXP_ATTR_FLAGS; +} + +uint32_t nfnl_exp_get_flags(const struct nfnl_exp *exp) +{ + return exp->exp_flags; +} + +static const struct trans_tbl flag_table[] = { + __ADD(IPS_EXPECTED, expected), + __ADD(IPS_SEEN_REPLY, seen_reply), + __ADD(IPS_ASSURED, assured), +}; + +char * nfnl_exp_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, flag_table, + ARRAY_SIZE(flag_table)); +} + +int nfnl_exp_str2flags(const char *name) +{ + return __str2flags(name, flag_table, ARRAY_SIZE(flag_table)); +} + +void nfnl_exp_set_timeout(struct nfnl_exp *exp, uint32_t timeout) +{ + exp->exp_timeout = timeout; + exp->ce_mask |= EXP_ATTR_TIMEOUT; +} + +int nfnl_exp_test_timeout(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_TIMEOUT); +} + +uint32_t nfnl_exp_get_timeout(const struct nfnl_exp *exp) +{ + return exp->exp_timeout; +} + +void nfnl_exp_set_id(struct nfnl_exp *exp, uint32_t id) +{ + exp->exp_id = id; + exp->ce_mask |= EXP_ATTR_ID; +} + +int nfnl_exp_test_id(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_ID); +} + +uint32_t nfnl_exp_get_id(const struct nfnl_exp *exp) +{ + return exp->exp_id; +} + +int nfnl_exp_set_helper_name(struct nfnl_exp *exp, void *name) +{ + free(exp->exp_helper_name); + exp->exp_helper_name = strdup(name); + if (!exp->exp_helper_name) + return -NLE_NOMEM; + + exp->ce_mask |= EXP_ATTR_HELPER_NAME; + return 0; +} + +int nfnl_exp_test_helper_name(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_HELPER_NAME); +} + +const char * nfnl_exp_get_helper_name(const struct nfnl_exp *exp) +{ + return exp->exp_helper_name; +} + +void nfnl_exp_set_zone(struct nfnl_exp *exp, uint16_t zone) +{ + exp->exp_zone = zone; + exp->ce_mask |= EXP_ATTR_ZONE; +} + +int nfnl_exp_test_zone(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_ZONE); +} + +uint16_t nfnl_exp_get_zone(const struct nfnl_exp *exp) +{ + return exp->exp_zone; +} + +void nfnl_exp_set_class(struct nfnl_exp *exp, uint32_t class) +{ + exp->exp_class = class; + exp->ce_mask |= EXP_ATTR_CLASS; +} + +int nfnl_exp_test_class(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_CLASS); +} + +uint32_t nfnl_exp_get_class(const struct nfnl_exp *exp) +{ + return exp->exp_class; +} + +int nfnl_exp_set_fn(struct nfnl_exp *exp, void *fn) +{ + free(exp->exp_fn); + exp->exp_fn = strdup(fn); + if (!exp->exp_fn) + return -NLE_NOMEM; + + exp->ce_mask |= EXP_ATTR_FN; + return 0; +} + +int nfnl_exp_test_fn(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_FN); +} + +const char * nfnl_exp_get_fn(const struct nfnl_exp *exp) +{ + return exp->exp_fn; +} + +void nfnl_exp_set_nat_dir(struct nfnl_exp *exp, uint8_t nat_dir) +{ + exp->exp_nat_dir = nat_dir; + exp->ce_mask |= EXP_ATTR_NAT_DIR; +} + +int nfnl_exp_test_nat_dir(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_NAT_DIR); +} + +uint8_t nfnl_exp_get_nat_dir(const struct nfnl_exp *exp) +{ + return exp->exp_nat_dir; +} + +#define EXP_GET_TUPLE(e, t) \ + (t == NFNL_EXP_TUPLE_MASTER) ? \ + &(e->exp_master) : \ + (t == NFNL_EXP_TUPLE_MASK) ? \ + &(e->exp_mask) : \ + (t == NFNL_EXP_TUPLE_NAT) ? \ + &(e->exp_nat) : &(exp->exp_expect) + +static int exp_get_src_attr(int tuple) +{ + int attr = 0; + + switch (tuple) { + case NFNL_EXP_TUPLE_MASTER: + attr = EXP_ATTR_MASTER_IP_SRC; + break; + case NFNL_EXP_TUPLE_MASK: + attr = EXP_ATTR_MASK_IP_SRC; + break; + case NFNL_EXP_TUPLE_NAT: + attr = EXP_ATTR_NAT_IP_SRC; + break; + case NFNL_EXP_TUPLE_EXPECT: + default : + attr = EXP_ATTR_EXPECT_IP_SRC; + } + + return attr; +} + +static int exp_get_dst_attr(int tuple) +{ + int attr = 0; + + switch (tuple) { + case NFNL_EXP_TUPLE_MASTER: + attr = EXP_ATTR_MASTER_IP_DST; + break; + case NFNL_EXP_TUPLE_MASK: + attr = EXP_ATTR_MASK_IP_DST; + break; + case NFNL_EXP_TUPLE_NAT: + attr = EXP_ATTR_NAT_IP_DST; + break; + case NFNL_EXP_TUPLE_EXPECT: + default : + attr = EXP_ATTR_EXPECT_IP_DST; + } + + return attr; +} + + +static int exp_set_addr(struct nfnl_exp *exp, struct nl_addr *addr, + int attr, struct nl_addr ** exp_addr) +{ + if (exp->ce_mask & EXP_ATTR_FAMILY) { + if (addr->a_family != exp->exp_family) + return -NLE_AF_MISMATCH; + } else + nfnl_exp_set_family(exp, addr->a_family); + + if (*exp_addr) + nl_addr_put(*exp_addr); + + nl_addr_get(addr); + *exp_addr = addr; + exp->ce_mask |= attr; + + return 0; +} + +int nfnl_exp_set_src(struct nfnl_exp *exp, int tuple, struct nl_addr *addr) +{ + struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + return exp_set_addr(exp, addr, exp_get_src_attr(tuple), &dir->src); +} + +int nfnl_exp_set_dst(struct nfnl_exp *exp, int tuple, struct nl_addr *addr) +{ + struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + return exp_set_addr(exp, addr, exp_get_dst_attr(tuple), &dir->dst); +} + +int nfnl_exp_test_src(const struct nfnl_exp *exp, int tuple) +{ + return !!(exp->ce_mask & exp_get_src_attr(tuple)); +} + +int nfnl_exp_test_dst(const struct nfnl_exp *exp, int tuple) +{ + return !!(exp->ce_mask & exp_get_dst_attr(tuple)); +} + +struct nl_addr *nfnl_exp_get_src(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + if (!(exp->ce_mask & exp_get_src_attr(tuple))) + return NULL; + return dir->src; +} + +struct nl_addr *nfnl_exp_get_dst(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + if (!(exp->ce_mask & exp_get_dst_attr(tuple))) + return NULL; + return dir->dst; +} + +static int exp_get_l4protonum_attr(int tuple) +{ + int attr = 0; + + switch (tuple) { + case NFNL_EXP_TUPLE_MASTER: + attr = EXP_ATTR_MASTER_L4PROTO_NUM; + break; + case NFNL_EXP_TUPLE_MASK: + attr = EXP_ATTR_MASK_L4PROTO_NUM; + break; + case NFNL_EXP_TUPLE_NAT: + attr = EXP_ATTR_NAT_L4PROTO_NUM; + break; + case NFNL_EXP_TUPLE_EXPECT: + default : + attr = EXP_ATTR_EXPECT_L4PROTO_NUM; + } + + return attr; +} + +void nfnl_exp_set_l4protonum(struct nfnl_exp *exp, int tuple, uint8_t l4protonum) +{ + struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + dir->proto.l4protonum = l4protonum; + exp->ce_mask |= exp_get_l4protonum_attr(tuple); +} + +int nfnl_exp_test_l4protonum(const struct nfnl_exp *exp, int tuple) +{ + return !!(exp->ce_mask & exp_get_l4protonum_attr(tuple)); +} + +uint8_t nfnl_exp_get_l4protonum(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + return dir->proto.l4protonum; +} + +static int exp_get_l4ports_attr(int tuple) +{ + int attr = 0; + + switch (tuple) { + case NFNL_EXP_TUPLE_MASTER: + attr = EXP_ATTR_MASTER_L4PROTO_PORTS; + break; + case NFNL_EXP_TUPLE_MASK: + attr = EXP_ATTR_MASK_L4PROTO_PORTS; + break; + case NFNL_EXP_TUPLE_NAT: + attr = EXP_ATTR_NAT_L4PROTO_PORTS; + break; + case NFNL_EXP_TUPLE_EXPECT: + default : + attr = EXP_ATTR_EXPECT_L4PROTO_PORTS; + } + + return attr; +} + +void nfnl_exp_set_ports(struct nfnl_exp *exp, int tuple, uint16_t srcport, uint16_t dstport) +{ + struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + dir->proto.l4protodata.port.src = srcport; + dir->proto.l4protodata.port.dst = dstport; + + exp->ce_mask |= exp_get_l4ports_attr(tuple); +} + +int nfnl_exp_test_ports(const struct nfnl_exp *exp, int tuple) +{ + return !!(exp->ce_mask & exp_get_l4ports_attr(tuple)); +} + +uint16_t nfnl_exp_get_src_port(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + return dir->proto.l4protodata.port.src; +} + +uint16_t nfnl_exp_get_dst_port(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + return dir->proto.l4protodata.port.dst; +} + +static int exp_get_l4icmp_attr(int tuple) +{ + int attr = 0; + + switch (tuple) { + case NFNL_EXP_TUPLE_MASTER: + attr = EXP_ATTR_MASTER_L4PROTO_ICMP; + break; + case NFNL_EXP_TUPLE_MASK: + attr = EXP_ATTR_MASK_L4PROTO_ICMP; + break; + case NFNL_EXP_TUPLE_NAT: + attr = EXP_ATTR_NAT_L4PROTO_ICMP; + break; + case NFNL_EXP_TUPLE_EXPECT: + default : + attr = EXP_ATTR_EXPECT_L4PROTO_ICMP; + } + + return attr; +} + +void nfnl_exp_set_icmp(struct nfnl_exp *exp, int tuple, uint16_t id, uint8_t type, uint8_t code) +{ + struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + dir->proto.l4protodata.icmp.id = id; + dir->proto.l4protodata.icmp.type = type; + dir->proto.l4protodata.icmp.code = code; + + exp->ce_mask |= exp_get_l4icmp_attr(tuple); +} + +int nfnl_exp_test_icmp(const struct nfnl_exp *exp, int tuple) +{ + int attr = exp_get_l4icmp_attr(tuple); + return !!(exp->ce_mask & attr); +} + +uint16_t nfnl_exp_get_icmp_id(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + return dir->proto.l4protodata.icmp.id; +} + +uint8_t nfnl_exp_get_icmp_type(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + return dir->proto.l4protodata.icmp.type; +} + +uint8_t nfnl_exp_get_icmp_code(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + return dir->proto.l4protodata.icmp.code; +} + +/** @} */ + +struct nl_object_ops exp_obj_ops = { + .oo_name = "netfilter/exp", + .oo_size = sizeof(struct nfnl_exp), + .oo_free_data = exp_free_data, + .oo_clone = exp_clone, + .oo_dump = { + [NL_DUMP_LINE] = exp_dump_line, + [NL_DUMP_DETAILS] = exp_dump_details, + }, + .oo_compare = exp_compare, + .oo_attrs2str = exp_attrs2str, +}; + +/** @} */ diff --git a/libnl/lib/netfilter/log.c b/libnl/lib/netfilter/log.c new file mode 100644 index 0000000..7b0abaa --- /dev/null +++ b/libnl/lib/netfilter/log.c @@ -0,0 +1,255 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +/** + * @ingroup nfnl + * @defgroup log Log + * @brief + * @{ + */ + +#include "nl-default.h" + +#include + +#include + +#include +#include +#include + +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" + +/** + * @name Log Commands + * @{ + */ + +static int build_log_cmd_request(uint8_t family, uint16_t queuenum, + uint8_t command, struct nl_msg **result) +{ + struct nl_msg *msg; + struct nfulnl_msg_config_cmd cmd; + + msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_ULOG, NFULNL_MSG_CONFIG, 0, + family, queuenum); + if (msg == NULL) + return -NLE_NOMEM; + + cmd.command = command; + if (nla_put(msg, NFULA_CFG_CMD, sizeof(cmd), &cmd) < 0) + goto nla_put_failure; + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +static int send_log_request(struct nl_sock *sk, struct nl_msg *msg) +{ + int err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +int nfnl_log_build_pf_bind(uint8_t pf, struct nl_msg **result) +{ + return build_log_cmd_request(pf, 0, NFULNL_CFG_CMD_PF_BIND, result); +} + +int nfnl_log_pf_bind(struct nl_sock *nlh, uint8_t pf) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_log_build_pf_bind(pf, &msg)) < 0) + return err; + + return send_log_request(nlh, msg); +} + +int nfnl_log_build_pf_unbind(uint8_t pf, struct nl_msg **result) +{ + return build_log_cmd_request(pf, 0, NFULNL_CFG_CMD_PF_UNBIND, result); +} + +int nfnl_log_pf_unbind(struct nl_sock *nlh, uint8_t pf) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_log_build_pf_unbind(pf, &msg)) < 0) + return err; + + return send_log_request(nlh, msg); +} + +static int nfnl_log_build_request(const struct nfnl_log *log, + struct nl_msg **result) +{ + struct nl_msg *msg; + + if (!nfnl_log_test_group(log)) + return -NLE_MISSING_ATTR; + + msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_ULOG, NFULNL_MSG_CONFIG, 0, + 0, nfnl_log_get_group(log)); + if (msg == NULL) + return -NLE_NOMEM; + + /* This sucks. The nfnetlink_log interface always expects both + * parameters to be present. Needs to be done properly. + */ + if (nfnl_log_test_copy_mode(log)) { + struct nfulnl_msg_config_mode mode; + + switch (nfnl_log_get_copy_mode(log)) { + case NFNL_LOG_COPY_NONE: + mode.copy_mode = NFULNL_COPY_NONE; + break; + case NFNL_LOG_COPY_META: + mode.copy_mode = NFULNL_COPY_META; + break; + case NFNL_LOG_COPY_PACKET: + mode.copy_mode = NFULNL_COPY_PACKET; + break; + } + mode.copy_range = htonl(nfnl_log_get_copy_range(log)); + mode._pad = 0; + + if (nla_put(msg, NFULA_CFG_MODE, sizeof(mode), &mode) < 0) + goto nla_put_failure; + } + + if (nfnl_log_test_flush_timeout(log) && + nla_put_u32(msg, NFULA_CFG_TIMEOUT, + htonl(nfnl_log_get_flush_timeout(log))) < 0) + goto nla_put_failure; + + if (nfnl_log_test_alloc_size(log) && + nla_put_u32(msg, NFULA_CFG_NLBUFSIZ, + htonl(nfnl_log_get_alloc_size(log))) < 0) + goto nla_put_failure; + + if (nfnl_log_test_queue_threshold(log) && + nla_put_u32(msg, NFULA_CFG_QTHRESH, + htonl(nfnl_log_get_queue_threshold(log))) < 0) + goto nla_put_failure; + + if (nfnl_log_get_flags(log) && + nla_put_u16(msg, NFULA_CFG_FLAGS, + htons(nfnl_log_get_flags(log))) < 0) + goto nla_put_failure; + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +int nfnl_log_build_create_request(const struct nfnl_log *log, + struct nl_msg **result) +{ + struct nfulnl_msg_config_cmd cmd; + int err; + + if ((err = nfnl_log_build_request(log, result)) < 0) + return err; + + cmd.command = NFULNL_CFG_CMD_BIND; + + if (nla_put(*result, NFULA_CFG_CMD, sizeof(cmd), &cmd) < 0) + goto nla_put_failure; + + return 0; + +nla_put_failure: + nlmsg_free(*result); + return -NLE_MSGSIZE; +} + +int nfnl_log_create(struct nl_sock *nlh, const struct nfnl_log *log) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_log_build_create_request(log, &msg)) < 0) + return err; + + return send_log_request(nlh, msg); +} + +int nfnl_log_build_change_request(const struct nfnl_log *log, + struct nl_msg **result) +{ + return nfnl_log_build_request(log, result); +} + +int nfnl_log_change(struct nl_sock *nlh, const struct nfnl_log *log) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_log_build_change_request(log, &msg)) < 0) + return err; + + return send_log_request(nlh, msg); +} + +int nfnl_log_build_delete_request(const struct nfnl_log *log, + struct nl_msg **result) +{ + if (!nfnl_log_test_group(log)) + return -NLE_MISSING_ATTR; + + return build_log_cmd_request(0, nfnl_log_get_group(log), + NFULNL_CFG_CMD_UNBIND, result); +} + +int nfnl_log_delete(struct nl_sock *nlh, const struct nfnl_log *log) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_log_build_delete_request(log, &msg)) < 0) + return err; + + return send_log_request(nlh, msg); +} + +/** @} */ + +static struct nl_cache_ops nfnl_log_ops = { + .co_name = "netfilter/log", + .co_obj_ops = &log_obj_ops, + .co_msgtypes = { + END_OF_MSGTYPES_LIST, + }, +}; + +static void _nl_init log_init(void) +{ + nl_cache_mngt_register(&nfnl_log_ops); +} + +static void _nl_exit log_exit(void) +{ + nl_cache_mngt_unregister(&nfnl_log_ops); +} + +/** @} */ diff --git a/libnl/lib/netfilter/log_msg.c b/libnl/lib/netfilter/log_msg.c new file mode 100644 index 0000000..2ddc06a --- /dev/null +++ b/libnl/lib/netfilter/log_msg.c @@ -0,0 +1,261 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2008 Patrick McHardy + */ + +/** + * @ingroup nfnl + * @defgroup log Log + * @brief + * @{ + */ + +#include "nl-default.h" + +#include + +#include + +#include +#include +#include + +#include "nl-netfilter.h" +#include "nl-priv-dynamic-core/cache-api.h" + +static struct nla_policy log_msg_policy[NFULA_MAX+1] = { + [NFULA_PACKET_HDR] = { + .minlen = sizeof(struct nfulnl_msg_packet_hdr) + }, + [NFULA_MARK] = { .type = NLA_U32 }, + [NFULA_TIMESTAMP] = { + .minlen = sizeof(struct nfulnl_msg_packet_timestamp) + }, + [NFULA_IFINDEX_INDEV] = { .type = NLA_U32 }, + [NFULA_IFINDEX_OUTDEV] = { .type = NLA_U32 }, + [NFULA_IFINDEX_PHYSINDEV] = { .type = NLA_U32 }, + [NFULA_IFINDEX_PHYSOUTDEV] = { .type = NLA_U32 }, + [NFULA_HWADDR] = { + .minlen = sizeof(struct nfulnl_msg_packet_hw) + }, + //[NFULA_PAYLOAD] + [NFULA_PREFIX] = { .type = NLA_STRING, }, + [NFULA_UID] = { .type = NLA_U32 }, + [NFULA_GID] = { .type = NLA_U32 }, + [NFULA_SEQ] = { .type = NLA_U32 }, + [NFULA_SEQ_GLOBAL] = { .type = NLA_U32 }, + [NFULA_HWTYPE] = { .type = NLA_U16 }, + [NFULA_HWLEN] = { .type = NLA_U16 }, + [NFULA_VLAN] = { .type = NLA_NESTED }, + [NFULA_CT] = { .type = NLA_NESTED }, + [NFULA_CT_INFO] = { .type = NLA_U32 }, +}; + +static struct nla_policy log_msg_vlan_policy[NFULA_VLAN_MAX+1] = { + [NFULA_VLAN_PROTO] = { .type = NLA_U16 }, + [NFULA_VLAN_TCI] = { .type = NLA_U16 }, +}; + +static int +nfnlmsg_log_msg_parse_vlan(struct nlattr *attr_full, struct nfnl_log_msg *msg) +{ + struct nlattr *tb[NFULA_VLAN_MAX+1]; + struct nlattr *attr; + int err; + + err = nla_parse_nested(tb, NFULA_VLAN_MAX, attr_full, + log_msg_vlan_policy); + if (err < 0) + return err; + + attr = tb[NFULA_VLAN_PROTO]; + if (attr) + nfnl_log_msg_set_vlan_proto(msg, nla_get_u16(attr)); + + attr = tb[NFULA_VLAN_TCI]; + if (attr) + nfnl_log_msg_set_vlan_tag(msg, ntohs(nla_get_u16(attr))); + + return 0; +} + +int nfnlmsg_log_msg_parse(struct nlmsghdr *nlh, struct nfnl_log_msg **result) +{ + struct nfnl_log_msg *msg; + struct nlattr *tb[NFULA_MAX+1]; + struct nlattr *attr; + int err; + + msg = nfnl_log_msg_alloc(); + if (!msg) + return -NLE_NOMEM; + + msg->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, NFULA_MAX, + log_msg_policy); + if (err < 0) + goto errout; + + nfnl_log_msg_set_family(msg, nfnlmsg_family(nlh)); + + attr = tb[NFULA_PACKET_HDR]; + if (attr) { + struct nfulnl_msg_packet_hdr *hdr = nla_data(attr); + + if (hdr->hw_protocol) + nfnl_log_msg_set_hwproto(msg, hdr->hw_protocol); + nfnl_log_msg_set_hook(msg, hdr->hook); + } + + attr = tb[NFULA_MARK]; + if (attr) + nfnl_log_msg_set_mark(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_TIMESTAMP]; + if (attr) { + struct nfulnl_msg_packet_timestamp *timestamp = nla_data(attr); + struct timeval tv; + + tv.tv_sec = ntohll(timestamp->sec); + tv.tv_usec = ntohll(timestamp->usec); + nfnl_log_msg_set_timestamp(msg, &tv); + } + + attr = tb[NFULA_IFINDEX_INDEV]; + if (attr) + nfnl_log_msg_set_indev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_IFINDEX_OUTDEV]; + if (attr) + nfnl_log_msg_set_outdev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_IFINDEX_PHYSINDEV]; + if (attr) + nfnl_log_msg_set_physindev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_IFINDEX_PHYSOUTDEV]; + if (attr) + nfnl_log_msg_set_physoutdev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_HWADDR]; + if (attr) { + struct nfulnl_msg_packet_hw *hw = nla_data(attr); + + nfnl_log_msg_set_hwaddr(msg, hw->hw_addr, ntohs(hw->hw_addrlen)); + } + + attr = tb[NFULA_PAYLOAD]; + if (attr) { + err = nfnl_log_msg_set_payload(msg, nla_data(attr), nla_len(attr)); + if (err < 0) + goto errout; + } + + attr = tb[NFULA_PREFIX]; + if (attr) { + err = nfnl_log_msg_set_prefix(msg, nla_data(attr)); + if (err < 0) + goto errout; + } + + attr = tb[NFULA_UID]; + if (attr) + nfnl_log_msg_set_uid(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_GID]; + if (attr) + nfnl_log_msg_set_gid(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_SEQ]; + if (attr) + nfnl_log_msg_set_seq(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_SEQ_GLOBAL]; + if (attr) + nfnl_log_msg_set_seq_global(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_HWTYPE]; + if (attr) + nfnl_log_msg_set_hwtype(msg, ntohs(nla_get_u16(attr))); + + attr = tb[NFULA_HWLEN]; + if (attr) + nfnl_log_msg_set_hwlen(msg, ntohs(nla_get_u16(attr))); + + attr = tb[NFULA_HWHEADER]; + if (attr) + nfnl_log_msg_set_hwheader(msg, nla_data(attr), nla_len(attr)); + + attr = tb[NFULA_VLAN]; + if (attr) { + err = nfnlmsg_log_msg_parse_vlan(attr, msg); + if (err < 0) + goto errout; + } + + attr = tb[NFULA_CT]; + if (attr) { + struct nfnl_ct *ct = NULL; + err = nfnlmsg_ct_parse_nested(attr, &ct); + if (err < 0) + goto errout; + nfnl_log_msg_set_ct(msg, ct); + nfnl_ct_put(ct); + } + + attr = tb[NFULA_CT_INFO]; + if (attr) + nfnl_log_msg_set_ct_info(msg, ntohl(nla_get_u32(attr))); + + *result = msg; + return 0; + +errout: + nfnl_log_msg_put(msg); + return err; +} + +static int log_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct nfnl_log_msg *msg; + int err; + + if ((err = nfnlmsg_log_msg_parse(nlh, &msg)) < 0) + return err; + + err = pp->pp_cb((struct nl_object *) msg, pp); + nfnl_log_msg_put(msg); + return err; +} + +/** @} */ + +#define NFNLMSG_LOG_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_ULOG, (type)) +static struct nl_cache_ops nfnl_log_msg_ops = { + .co_name = "netfilter/log_msg", + .co_hdrsize = NFNL_HDRLEN, + .co_msgtypes = { + { NFNLMSG_LOG_TYPE(NFULNL_MSG_PACKET), NL_ACT_NEW, "new" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_NETFILTER, + .co_msg_parser = log_msg_parser, + .co_obj_ops = &log_msg_obj_ops, +}; + +static void _nl_init log_msg_init(void) +{ + nl_cache_mngt_register(&nfnl_log_msg_ops); +} + +static void _nl_exit log_msg_exit(void) +{ + nl_cache_mngt_unregister(&nfnl_log_msg_ops); +} + +/** @} */ diff --git a/libnl/lib/netfilter/log_msg_obj.c b/libnl/lib/netfilter/log_msg_obj.c new file mode 100644 index 0000000..6e8b554 --- /dev/null +++ b/libnl/lib/netfilter/log_msg_obj.c @@ -0,0 +1,684 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include + +#include "nl-priv-dynamic-core/object-api.h" +#include "nl-netfilter.h" + +/** @cond SKIP */ +#define LOG_MSG_ATTR_FAMILY (1UL << 0) +#define LOG_MSG_ATTR_HWPROTO (1UL << 1) +#define LOG_MSG_ATTR_HOOK (1UL << 2) +#define LOG_MSG_ATTR_MARK (1UL << 3) +#define LOG_MSG_ATTR_TIMESTAMP (1UL << 4) +#define LOG_MSG_ATTR_INDEV (1UL << 5) +#define LOG_MSG_ATTR_OUTDEV (1UL << 6) +#define LOG_MSG_ATTR_PHYSINDEV (1UL << 7) +#define LOG_MSG_ATTR_PHYSOUTDEV (1UL << 8) +#define LOG_MSG_ATTR_HWADDR (1UL << 9) +#define LOG_MSG_ATTR_PAYLOAD (1UL << 10) +#define LOG_MSG_ATTR_PREFIX (1UL << 11) +#define LOG_MSG_ATTR_UID (1UL << 12) +#define LOG_MSG_ATTR_GID (1UL << 13) +#define LOG_MSG_ATTR_SEQ (1UL << 14) +#define LOG_MSG_ATTR_SEQ_GLOBAL (1UL << 15) +#define LOG_MSG_ATTR_HWTYPE (1UL << 16) +#define LOG_MSG_ATTR_HWLEN (1UL << 17) +#define LOG_MSG_ATTR_HWHEADER (1UL << 18) +#define LOG_MSG_ATTR_VLAN_PROTO (1UL << 19) +#define LOG_MSG_ATTR_VLAN_TAG (1UL << 20) +#define LOG_MSG_ATTR_CT_INFO (1UL << 21) +#define LOG_MSG_ATTR_CT (1UL << 22) +/** @endcond */ + +static void log_msg_free_data(struct nl_object *c) +{ + struct nfnl_log_msg *msg = (struct nfnl_log_msg *) c; + + if (msg == NULL) + return; + + free(msg->log_msg_payload); + free(msg->log_msg_prefix); + free(msg->log_msg_hwheader); + if (msg->log_msg_ct) + nfnl_ct_put(msg->log_msg_ct); +} + +static int log_msg_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct nfnl_log_msg *dst = (struct nfnl_log_msg *) _dst; + struct nfnl_log_msg *src = (struct nfnl_log_msg *) _src; + int err; + + dst->log_msg_payload = NULL; + dst->log_msg_payload_len = 0; + dst->log_msg_prefix = NULL; + dst->log_msg_hwheader = NULL; + dst->log_msg_hwheader_len = 0; + dst->log_msg_ct = NULL; + + if (src->log_msg_payload) { + err = nfnl_log_msg_set_payload(dst, src->log_msg_payload, + src->log_msg_payload_len); + if (err < 0) + return err; + } + + if (src->log_msg_prefix) { + err = nfnl_log_msg_set_prefix(dst, src->log_msg_prefix); + if (err < 0) + return err; + } + + if (src->log_msg_hwheader) { + err = nfnl_log_msg_set_hwheader(dst, src->log_msg_hwheader, + src->log_msg_hwheader_len); + if (err < 0) + return err; + } + + if (src->log_msg_ct) { + dst->log_msg_ct = (struct nfnl_ct *) nl_object_clone((struct nl_object *) src->log_msg_ct); + if (!dst->log_msg_ct) { + return -NLE_NOMEM; + } + } + + return 0; +} + +static void log_msg_dump(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_log_msg *msg = (struct nfnl_log_msg *) a; + struct nl_cache *link_cache; + char buf[64]; + + link_cache = nl_cache_mngt_require_safe("route/link"); + + nl_new_line(p); + + if (msg->ce_mask & LOG_MSG_ATTR_PREFIX) + nl_dump(p, "%s", msg->log_msg_prefix); + + if (msg->ce_mask & LOG_MSG_ATTR_INDEV) { + if (link_cache) + nl_dump(p, "IN=%s ", + rtnl_link_i2name(link_cache, + msg->log_msg_indev, + buf, sizeof(buf))); + else + nl_dump(p, "IN=%d ", msg->log_msg_indev); + } + + if (msg->ce_mask & LOG_MSG_ATTR_PHYSINDEV) { + if (link_cache) + nl_dump(p, "PHYSIN=%s ", + rtnl_link_i2name(link_cache, + msg->log_msg_physindev, + buf, sizeof(buf))); + else + nl_dump(p, "PHYSIN=%d ", msg->log_msg_physindev); + } + + if (msg->ce_mask & LOG_MSG_ATTR_OUTDEV) { + if (link_cache) + nl_dump(p, "OUT=%s ", + rtnl_link_i2name(link_cache, + msg->log_msg_outdev, + buf, sizeof(buf))); + else + nl_dump(p, "OUT=%d ", msg->log_msg_outdev); + } + + if (msg->ce_mask & LOG_MSG_ATTR_PHYSOUTDEV) { + if (link_cache) + nl_dump(p, "PHYSOUT=%s ", + rtnl_link_i2name(link_cache, + msg->log_msg_physoutdev, + buf, sizeof(buf))); + else + nl_dump(p, "PHYSOUT=%d ", msg->log_msg_physoutdev); + } + + if (msg->ce_mask & LOG_MSG_ATTR_HWADDR) { + int i; + + nl_dump(p, "MAC"); + for (i = 0; i < msg->log_msg_hwaddr_len; i++) + nl_dump(p, "%c%02x", i?':':'=', msg->log_msg_hwaddr[i]); + nl_dump(p, " "); + } + + /* FIXME: parse the payload to get iptables LOG compatible format */ + + if (msg->ce_mask & LOG_MSG_ATTR_FAMILY) + nl_dump(p, "FAMILY=%s ", + nl_af2str(msg->log_msg_family, buf, sizeof(buf))); + + if (msg->ce_mask & LOG_MSG_ATTR_HWPROTO) + nl_dump(p, "HWPROTO=%s ", + nl_ether_proto2str(ntohs(msg->log_msg_hwproto), + buf, sizeof(buf))); + + if (msg->ce_mask & LOG_MSG_ATTR_HOOK) + nl_dump(p, "HOOK=%s ", + nfnl_inet_hook2str(msg->log_msg_hook, + buf, sizeof(buf))); + + if (msg->ce_mask & LOG_MSG_ATTR_MARK) + nl_dump(p, "MARK=%u ", msg->log_msg_mark); + + if (msg->ce_mask & LOG_MSG_ATTR_PAYLOAD) + nl_dump(p, "PAYLOADLEN=%d ", msg->log_msg_payload_len); + + if (msg->ce_mask & LOG_MSG_ATTR_UID) + nl_dump(p, "UID=%u ", msg->log_msg_uid); + + if (msg->ce_mask & LOG_MSG_ATTR_GID) + nl_dump(p, "GID=%u ", msg->log_msg_gid); + + if (msg->ce_mask & LOG_MSG_ATTR_SEQ) + nl_dump(p, "SEQ=%d ", msg->log_msg_seq); + + if (msg->ce_mask & LOG_MSG_ATTR_SEQ_GLOBAL) + nl_dump(p, "SEQGLOBAL=%d ", msg->log_msg_seq_global); + + if (msg->ce_mask & LOG_MSG_ATTR_HWTYPE) + nl_dump(p, "HWTYPE=%u ", msg->log_msg_hwtype); + + if (msg->ce_mask & LOG_MSG_ATTR_HWLEN) + nl_dump(p, "HWLEN=%u ", msg->log_msg_hwlen); + + if (msg->ce_mask & LOG_MSG_ATTR_HWHEADER) { + int i; + + nl_dump(p, "HWHEADER"); + for (i = 0; i < msg->log_msg_hwheader_len; i++) + nl_dump(p, "%c%02x", i?':':'=', ((uint8_t*) msg->log_msg_hwheader) [i]); + nl_dump(p, " "); + } + + if (msg->ce_mask & LOG_MSG_ATTR_VLAN_TAG) + nl_dump(p, "VLAN=%d CFI=%d PRIO=%d", + (int) nfnl_log_msg_get_vlan_id(msg), + (int) nfnl_log_msg_get_vlan_cfi(msg), + (int) nfnl_log_msg_get_vlan_prio(msg)); + + if (msg->ce_mask & LOG_MSG_ATTR_CT_INFO) + nl_dump(p, "CTINFO=%u ", msg->log_msg_ct_info); + + nl_dump(p, "\n"); + + if (msg->ce_mask & LOG_MSG_ATTR_CT) + ct_obj_ops.oo_dump[NL_DUMP_LINE]((struct nl_object *)msg->log_msg_ct, p); + + if (link_cache) + nl_cache_put(link_cache); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct nfnl_log_msg *nfnl_log_msg_alloc(void) +{ + return (struct nfnl_log_msg *) nl_object_alloc(&log_msg_obj_ops); +} + +void nfnl_log_msg_get(struct nfnl_log_msg *msg) +{ + nl_object_get((struct nl_object *) msg); +} + +void nfnl_log_msg_put(struct nfnl_log_msg *msg) +{ + nl_object_put((struct nl_object *) msg); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nfnl_log_msg_set_family(struct nfnl_log_msg *msg, uint8_t family) +{ + msg->log_msg_family = family; + msg->ce_mask |= LOG_MSG_ATTR_FAMILY; +} + +uint8_t nfnl_log_msg_get_family(const struct nfnl_log_msg *msg) +{ + if (msg->ce_mask & LOG_MSG_ATTR_FAMILY) + return msg->log_msg_family; + else + return AF_UNSPEC; +} + +void nfnl_log_msg_set_hwproto(struct nfnl_log_msg *msg, uint16_t hwproto) +{ + msg->log_msg_hwproto = hwproto; + msg->ce_mask |= LOG_MSG_ATTR_HWPROTO; +} + +int nfnl_log_msg_test_hwproto(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_HWPROTO); +} + +uint16_t nfnl_log_msg_get_hwproto(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_hwproto; +} + +void nfnl_log_msg_set_hook(struct nfnl_log_msg *msg, uint8_t hook) +{ + msg->log_msg_hook = hook; + msg->ce_mask |= LOG_MSG_ATTR_HOOK; +} + +int nfnl_log_msg_test_hook(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_HOOK); +} + +uint8_t nfnl_log_msg_get_hook(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_hook; +} + +void nfnl_log_msg_set_mark(struct nfnl_log_msg *msg, uint32_t mark) +{ + msg->log_msg_mark = mark; + msg->ce_mask |= LOG_MSG_ATTR_MARK; +} + +int nfnl_log_msg_test_mark(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_MARK); +} + +uint32_t nfnl_log_msg_get_mark(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_mark; +} + +void nfnl_log_msg_set_timestamp(struct nfnl_log_msg *msg, struct timeval *tv) +{ + msg->log_msg_timestamp.tv_sec = tv->tv_sec; + msg->log_msg_timestamp.tv_usec = tv->tv_usec; + msg->ce_mask |= LOG_MSG_ATTR_TIMESTAMP; +} + +const struct timeval *nfnl_log_msg_get_timestamp(const struct nfnl_log_msg *msg) +{ + if (!(msg->ce_mask & LOG_MSG_ATTR_TIMESTAMP)) + return NULL; + return &msg->log_msg_timestamp; +} + +void nfnl_log_msg_set_indev(struct nfnl_log_msg *msg, uint32_t indev) +{ + msg->log_msg_indev = indev; + msg->ce_mask |= LOG_MSG_ATTR_INDEV; +} + +uint32_t nfnl_log_msg_get_indev(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_indev; +} + +void nfnl_log_msg_set_outdev(struct nfnl_log_msg *msg, uint32_t outdev) +{ + msg->log_msg_outdev = outdev; + msg->ce_mask |= LOG_MSG_ATTR_OUTDEV; +} + +uint32_t nfnl_log_msg_get_outdev(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_outdev; +} + +void nfnl_log_msg_set_physindev(struct nfnl_log_msg *msg, uint32_t physindev) +{ + msg->log_msg_physindev = physindev; + msg->ce_mask |= LOG_MSG_ATTR_PHYSINDEV; +} + +uint32_t nfnl_log_msg_get_physindev(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_physindev; +} + +void nfnl_log_msg_set_physoutdev(struct nfnl_log_msg *msg, uint32_t physoutdev) +{ + msg->log_msg_physoutdev = physoutdev; + msg->ce_mask |= LOG_MSG_ATTR_PHYSOUTDEV; +} + +uint32_t nfnl_log_msg_get_physoutdev(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_physoutdev; +} + +void nfnl_log_msg_set_hwaddr(struct nfnl_log_msg *msg, uint8_t *hwaddr, int len) +{ + if (len < 0) + len = 0; + else if (((unsigned)len) > sizeof(msg->log_msg_hwaddr)) + len = sizeof(msg->log_msg_hwaddr); + msg->log_msg_hwaddr_len = len; + memcpy(msg->log_msg_hwaddr, hwaddr, len); + msg->ce_mask |= LOG_MSG_ATTR_HWADDR; +} + +const uint8_t *nfnl_log_msg_get_hwaddr(const struct nfnl_log_msg *msg, int *len) +{ + if (!(msg->ce_mask & LOG_MSG_ATTR_HWADDR)) { + *len = 0; + return NULL; + } + + *len = msg->log_msg_hwaddr_len; + return msg->log_msg_hwaddr; +} + +int nfnl_log_msg_set_payload(struct nfnl_log_msg *msg, uint8_t *payload, int len) +{ + uint8_t *p = NULL; + + if (len < 0) + return -NLE_INVAL; + + p = _nl_memdup(payload, len); + if (!p && len > 0) + return -NLE_NOMEM; + + free(msg->log_msg_payload); + msg->log_msg_payload = p; + msg->log_msg_payload_len = len; + if (len > 0) + msg->ce_mask |= LOG_MSG_ATTR_PAYLOAD; + else + msg->ce_mask &= ~LOG_MSG_ATTR_PAYLOAD; + return 0; +} + +const void *nfnl_log_msg_get_payload(const struct nfnl_log_msg *msg, int *len) +{ + if (!(msg->ce_mask & LOG_MSG_ATTR_PAYLOAD)) { + *len = 0; + return NULL; + } + + *len = msg->log_msg_payload_len; + return msg->log_msg_payload; +} + +int nfnl_log_msg_set_prefix(struct nfnl_log_msg *msg, void *prefix) +{ + char *p = NULL; + + if (prefix) { + p = strdup(prefix); + if (!p) + return -NLE_NOMEM; + } + + free(msg->log_msg_prefix); + msg->log_msg_prefix = p; + + if (p) + msg->ce_mask |= LOG_MSG_ATTR_PREFIX; + else + msg->ce_mask &= ~LOG_MSG_ATTR_PREFIX; + return 0; +} + +const char *nfnl_log_msg_get_prefix(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_prefix; +} + +void nfnl_log_msg_set_uid(struct nfnl_log_msg *msg, uint32_t uid) +{ + msg->log_msg_uid = uid; + msg->ce_mask |= LOG_MSG_ATTR_UID; +} + +int nfnl_log_msg_test_uid(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_UID); +} + +uint32_t nfnl_log_msg_get_uid(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_uid; +} + +void nfnl_log_msg_set_gid(struct nfnl_log_msg *msg, uint32_t gid) +{ + msg->log_msg_gid = gid; + msg->ce_mask |= LOG_MSG_ATTR_GID; +} + +int nfnl_log_msg_test_gid(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_GID); +} + +uint32_t nfnl_log_msg_get_gid(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_gid; +} + + +void nfnl_log_msg_set_seq(struct nfnl_log_msg *msg, uint32_t seq) +{ + msg->log_msg_seq = seq; + msg->ce_mask |= LOG_MSG_ATTR_SEQ; +} + +int nfnl_log_msg_test_seq(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_SEQ); +} + +uint32_t nfnl_log_msg_get_seq(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_seq; +} + +void nfnl_log_msg_set_seq_global(struct nfnl_log_msg *msg, uint32_t seq_global) +{ + msg->log_msg_seq_global = seq_global; + msg->ce_mask |= LOG_MSG_ATTR_SEQ_GLOBAL; +} + +int nfnl_log_msg_test_seq_global(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_SEQ_GLOBAL); +} + +uint32_t nfnl_log_msg_get_seq_global(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_seq_global; +} + +void nfnl_log_msg_set_hwtype(struct nfnl_log_msg *msg, uint16_t hwtype) +{ + msg->log_msg_hwtype = hwtype; + msg->ce_mask |= LOG_MSG_ATTR_HWTYPE; +} + +int nfnl_log_msg_test_hwtype(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_HWTYPE); +} + +uint16_t nfnl_log_msg_get_hwtype(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_hwtype; +} + +void nfnl_log_msg_set_hwlen(struct nfnl_log_msg *msg, uint16_t hwlen) +{ + msg->log_msg_hwlen = hwlen; + msg->ce_mask |= LOG_MSG_ATTR_HWLEN; +} + +int nfnl_log_msg_test_hwlen(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_HWLEN); +} + +uint16_t nfnl_log_msg_get_hwlen(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_hwlen; +} + +int nfnl_log_msg_set_hwheader(struct nfnl_log_msg *msg, void *data, int len) +{ + void *p = NULL; + + if (len < 0) + return -NLE_INVAL; + + p = _nl_memdup(data, len); + if (!p && len > 0) + return -NLE_NOMEM; + + free(msg->log_msg_hwheader); + msg->log_msg_hwheader = p; + msg->log_msg_hwheader_len = len; + if (len > 0) + msg->ce_mask |= LOG_MSG_ATTR_HWHEADER; + else + msg->ce_mask &= ~LOG_MSG_ATTR_HWHEADER; + return 0; +} + +int nfnl_log_msg_test_hwheader(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_HWHEADER); +} + +const void *nfnl_log_msg_get_hwheader(const struct nfnl_log_msg *msg, int *len) +{ + if (!(msg->ce_mask & LOG_MSG_ATTR_HWHEADER)) { + *len = 0; + return NULL; + } + + *len = msg->log_msg_hwheader_len; + return msg->log_msg_hwheader; +} + +void nfnl_log_msg_set_vlan_proto(struct nfnl_log_msg *msg, uint16_t vlan_proto) +{ + msg->log_msg_vlan_proto = vlan_proto; + msg->ce_mask |= LOG_MSG_ATTR_VLAN_PROTO; +} + +int nfnl_log_msg_test_vlan_proto(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_VLAN_PROTO); +} + +uint16_t nfnl_log_msg_get_vlan_proto(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_vlan_proto; +} + +void nfnl_log_msg_set_vlan_tag(struct nfnl_log_msg *msg, uint16_t vlan_tag) +{ + msg->log_msg_vlan_tag = vlan_tag; + msg->ce_mask |= LOG_MSG_ATTR_VLAN_TAG; +} + +int nfnl_log_msg_test_vlan_tag(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_VLAN_TAG); +} + +uint16_t nfnl_log_msg_get_vlan_tag(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_vlan_tag; +} + +uint16_t nfnl_log_msg_get_vlan_id(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_vlan_tag & 0x0fff; +} + +uint16_t nfnl_log_msg_get_vlan_cfi(const struct nfnl_log_msg *msg) +{ + return !!(msg->log_msg_vlan_tag & 0x1000); +} + +uint16_t nfnl_log_msg_get_vlan_prio(const struct nfnl_log_msg *msg) +{ + return (msg->log_msg_vlan_tag & 0xe000 ) >> 13; +} + +void nfnl_log_msg_set_ct_info(struct nfnl_log_msg *msg, uint32_t ct_info) +{ + msg->log_msg_ct_info = ct_info; + msg->ce_mask |= LOG_MSG_ATTR_CT_INFO; +} + +int nfnl_log_msg_test_ct_info(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_CT_INFO); +} + +uint32_t nfnl_log_msg_get_ct_info(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_ct_info; +} + +void nfnl_log_msg_set_ct(struct nfnl_log_msg *msg, struct nfnl_ct *ct) +{ + msg->log_msg_ct = (struct nfnl_ct *) nl_object_clone((struct nl_object *)ct); + msg->ce_mask |= LOG_MSG_ATTR_CT; +} + +int nfnl_log_msg_test_ct(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_CT); +} + +struct nfnl_ct *nfnl_log_msg_get_ct(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_ct; +} + +/** @} */ + +struct nl_object_ops log_msg_obj_ops = { + .oo_name = "netfilter/log_msg", + .oo_size = sizeof(struct nfnl_log_msg), + .oo_free_data = log_msg_free_data, + .oo_clone = log_msg_clone, + .oo_dump = { + [NL_DUMP_LINE] = log_msg_dump, + [NL_DUMP_DETAILS] = log_msg_dump, + [NL_DUMP_STATS] = log_msg_dump, + }, +}; + +/** @} */ diff --git a/libnl/lib/netfilter/log_obj.c b/libnl/lib/netfilter/log_obj.c new file mode 100644 index 0000000..acfe58b --- /dev/null +++ b/libnl/lib/netfilter/log_obj.c @@ -0,0 +1,303 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2008 Patrick McHardy + */ + +#include "nl-default.h" + +#include +#include + +#include "nl-priv-dynamic-core/object-api.h" +#include "nl-priv-dynamic-core/cache-api.h" +#include "nl-priv-dynamic-core/nl-core.h" + +/** @cond SKIP */ +struct nfnl_log { + NLHDR_COMMON + + uint16_t log_group; + uint8_t log_copy_mode; + uint32_t log_copy_range; + uint32_t log_flush_timeout; + uint32_t log_alloc_size; + uint32_t log_queue_threshold; + uint32_t log_flags; + uint32_t log_flag_mask; +}; + +#define LOG_ATTR_GROUP (1UL << 0) +#define LOG_ATTR_COPY_MODE (1UL << 1) +#define LOG_ATTR_COPY_RANGE (1UL << 3) +#define LOG_ATTR_FLUSH_TIMEOUT (1UL << 4) +#define LOG_ATTR_ALLOC_SIZE (1UL << 5) +#define LOG_ATTR_QUEUE_THRESHOLD (1UL << 6) + +/** @endcond */ + +static void nfnl_log_dump(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_log *log = (struct nfnl_log *) a; + char buf[64]; + + nl_new_line(p); + + if (log->ce_mask & LOG_ATTR_GROUP) + nl_dump(p, "group=%u ", log->log_group); + + if (log->ce_mask & LOG_ATTR_COPY_MODE) + nl_dump(p, "copy_mode=%s ", + nfnl_log_copy_mode2str(log->log_copy_mode, + buf, sizeof(buf))); + + if (log->ce_mask & LOG_ATTR_COPY_RANGE) + nl_dump(p, "copy_range=%u ", log->log_copy_range); + + if (log->ce_mask & LOG_ATTR_FLUSH_TIMEOUT) + nl_dump(p, "flush_timeout=%u ", log->log_flush_timeout); + + if (log->ce_mask & LOG_ATTR_ALLOC_SIZE) + nl_dump(p, "alloc_size=%u ", log->log_alloc_size); + + if (log->ce_mask & LOG_ATTR_QUEUE_THRESHOLD) + nl_dump(p, "queue_threshold=%u ", log->log_queue_threshold); + + nl_dump(p, "\n"); +} + +static const struct trans_tbl copy_modes[] = { + __ADD(NFNL_LOG_COPY_NONE, none), + __ADD(NFNL_LOG_COPY_META, meta), + __ADD(NFNL_LOG_COPY_PACKET, packet), +}; + +char *nfnl_log_copy_mode2str(enum nfnl_log_copy_mode copy_mode, char *buf, + size_t len) +{ + return __type2str(copy_mode, buf, len, copy_modes, + ARRAY_SIZE(copy_modes)); +} + +int nfnl_log_str2copy_mode(const char *name) +{ + return __str2type(name, copy_modes, ARRAY_SIZE(copy_modes)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct nfnl_log *nfnl_log_alloc(void) +{ + return (struct nfnl_log *) nl_object_alloc(&log_obj_ops); +} + +void nfnl_log_get(struct nfnl_log *log) +{ + nl_object_get((struct nl_object *) log); +} + +void nfnl_log_put(struct nfnl_log *log) +{ + nl_object_put((struct nl_object *) log); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nfnl_log_set_group(struct nfnl_log *log, uint16_t group) +{ + log->log_group = group; + log->ce_mask |= LOG_ATTR_GROUP; +} + +int nfnl_log_test_group(const struct nfnl_log *log) +{ + return !!(log->ce_mask & LOG_ATTR_GROUP); +} + +uint16_t nfnl_log_get_group(const struct nfnl_log *log) +{ + return log->log_group; +} + +void nfnl_log_set_copy_mode(struct nfnl_log *log, enum nfnl_log_copy_mode mode) +{ + log->log_copy_mode = mode; + log->ce_mask |= LOG_ATTR_COPY_MODE; +} + +int nfnl_log_test_copy_mode(const struct nfnl_log *log) +{ + return !!(log->ce_mask & LOG_ATTR_COPY_MODE); +} + +enum nfnl_log_copy_mode nfnl_log_get_copy_mode(const struct nfnl_log *log) +{ + return log->log_copy_mode; +} + +void nfnl_log_set_copy_range(struct nfnl_log *log, uint32_t copy_range) +{ + log->log_copy_range = copy_range; + log->ce_mask |= LOG_ATTR_COPY_RANGE; +} + +int nfnl_log_test_copy_range(const struct nfnl_log *log) +{ + return !!(log->ce_mask & LOG_ATTR_COPY_RANGE); +} + +uint32_t nfnl_log_get_copy_range(const struct nfnl_log *log) +{ + return log->log_copy_range; +} + +void nfnl_log_set_flush_timeout(struct nfnl_log *log, uint32_t timeout) +{ + log->log_flush_timeout = timeout; + log->ce_mask |= LOG_ATTR_FLUSH_TIMEOUT; +} + +int nfnl_log_test_flush_timeout(const struct nfnl_log *log) +{ + return !!(log->ce_mask & LOG_ATTR_FLUSH_TIMEOUT); +} + +uint32_t nfnl_log_get_flush_timeout(const struct nfnl_log *log) +{ + return log->log_flush_timeout; +} + +void nfnl_log_set_alloc_size(struct nfnl_log *log, uint32_t alloc_size) +{ + log->log_alloc_size = alloc_size; + log->ce_mask |= LOG_ATTR_ALLOC_SIZE; +} + +int nfnl_log_test_alloc_size(const struct nfnl_log *log) +{ + return !!(log->ce_mask & LOG_ATTR_ALLOC_SIZE); +} + +uint32_t nfnl_log_get_alloc_size(const struct nfnl_log *log) +{ + return log->log_alloc_size; +} + +void nfnl_log_set_queue_threshold(struct nfnl_log *log, uint32_t threshold) +{ + log->log_queue_threshold = threshold; + log->ce_mask |= LOG_ATTR_QUEUE_THRESHOLD; +} + +int nfnl_log_test_queue_threshold(const struct nfnl_log *log) +{ + return !!(log->ce_mask & LOG_ATTR_QUEUE_THRESHOLD); +} + +uint32_t nfnl_log_get_queue_threshold(const struct nfnl_log *log) +{ + return log->log_queue_threshold; +} + +/* We don't actually use the flags for anything yet since the + * nfnetlog_log interface truly sucks - it only contains the + * flag value, but not mask, so we would have to make assumptions + * about the supported flags. + */ +void nfnl_log_set_flags(struct nfnl_log *log, unsigned int flags) +{ + log->log_flags |= flags; + log->log_flag_mask |= flags; +} + +void nfnl_log_unset_flags(struct nfnl_log *log, unsigned int flags) +{ + log->log_flags &= ~flags; + log->log_flag_mask |= flags; +} + +unsigned int nfnl_log_get_flags(const struct nfnl_log *log) +{ + return log->log_flags; +} + +static const struct trans_tbl log_flags[] = { + __ADD(NFNL_LOG_FLAG_SEQ, seq), + __ADD(NFNL_LOG_FLAG_SEQ_GLOBAL, seq_global), + __ADD(NFNL_LOG_FLAG_CONNTRACK, conntrack), +}; + +char *nfnl_log_flags2str(unsigned int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, log_flags, ARRAY_SIZE(log_flags)); +} + +unsigned int nfnl_log_str2flags(const char *name) +{ + return __str2flags(name, log_flags, ARRAY_SIZE(log_flags)); +} + +static uint64_t nfnl_log_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct nfnl_log *a = (struct nfnl_log *) _a; + struct nfnl_log *b = (struct nfnl_log *) _b; + uint64_t diff = 0; + +#define NFNL_LOG_DIFF(ATTR, EXPR) \ + ATTR_DIFF(attrs, ATTR, a, b, EXPR) +#define NFNL_LOG_DIFF_VAL(ATTR, FIELD) \ + NFNL_LOG_DIFF(ATTR, a->FIELD != b->FIELD) + diff |= NFNL_LOG_DIFF_VAL(LOG_ATTR_GROUP, log_group); + diff |= NFNL_LOG_DIFF_VAL(LOG_ATTR_COPY_MODE, log_copy_mode); + diff |= NFNL_LOG_DIFF_VAL(LOG_ATTR_COPY_RANGE, log_copy_range); + diff |= NFNL_LOG_DIFF_VAL(LOG_ATTR_FLUSH_TIMEOUT, log_flush_timeout); + diff |= NFNL_LOG_DIFF_VAL(LOG_ATTR_ALLOC_SIZE, log_alloc_size); + diff |= NFNL_LOG_DIFF_VAL(LOG_ATTR_QUEUE_THRESHOLD, log_queue_threshold); +#undef NFNL_LOG_DIFF +#undef NFNL_LOG_DIFF_VAL + + return diff; +} + +static const struct trans_tbl nfnl_log_attrs[] = { + __ADD(LOG_ATTR_GROUP, group), + __ADD(LOG_ATTR_COPY_MODE, copy_mode), + __ADD(LOG_ATTR_COPY_RANGE, copy_range), + __ADD(LOG_ATTR_FLUSH_TIMEOUT, flush_timeout), + __ADD(LOG_ATTR_ALLOC_SIZE, alloc_size), + __ADD(LOG_ATTR_QUEUE_THRESHOLD, queue_threshold), +}; + +static char *nfnl_log_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, nfnl_log_attrs, + ARRAY_SIZE(nfnl_log_attrs)); +} + +/** @} */ + +struct nl_object_ops log_obj_ops = { + .oo_name = "netfilter/log", + .oo_size = sizeof(struct nfnl_log), + .oo_dump = { + [NL_DUMP_LINE] = nfnl_log_dump, + [NL_DUMP_DETAILS] = nfnl_log_dump, + [NL_DUMP_STATS] = nfnl_log_dump, + }, + .oo_compare = nfnl_log_compare, + .oo_attrs2str = nfnl_log_attrs2str, + .oo_id_attrs = LOG_ATTR_GROUP, +}; + +/** @} */ diff --git a/libnl/lib/netfilter/netfilter.c b/libnl/lib/netfilter/netfilter.c new file mode 100644 index 0000000..b8a5d2a --- /dev/null +++ b/libnl/lib/netfilter/netfilter.c @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008 Patrick McHardy + */ + +#include "nl-default.h" + +#include + +#include + +#include "nl-priv-dynamic-core/nl-core.h" + +static const struct trans_tbl nfnl_verdicts[] = { + __ADD(NF_DROP, NF_DROP), + __ADD(NF_ACCEPT, NF_ACCEPT), + __ADD(NF_STOLEN, NF_STOLEN), + __ADD(NF_QUEUE, NF_QUEUE), + __ADD(NF_REPEAT, NF_REPEAT), + __ADD(NF_STOP, NF_STOP), +}; + +char *nfnl_verdict2str(unsigned int verdict, char *buf, size_t len) +{ + return __type2str(verdict, buf, len, nfnl_verdicts, + ARRAY_SIZE(nfnl_verdicts)); +} + +unsigned int nfnl_str2verdict(const char *name) +{ + return __str2type(name, nfnl_verdicts, ARRAY_SIZE(nfnl_verdicts)); +} + +static const struct trans_tbl nfnl_inet_hooks[] = { + __ADD(NF_INET_PRE_ROUTING, NF_INET_PREROUTING), + __ADD(NF_INET_LOCAL_IN, NF_INET_LOCAL_IN), + __ADD(NF_INET_FORWARD, NF_INET_FORWARD), + __ADD(NF_INET_LOCAL_OUT, NF_INET_LOCAL_OUT), + __ADD(NF_INET_POST_ROUTING, NF_INET_POST_ROUTING), +}; + +char *nfnl_inet_hook2str(unsigned int hook, char *buf, size_t len) +{ + return __type2str(hook, buf, len, nfnl_inet_hooks, + ARRAY_SIZE(nfnl_inet_hooks)); +} + +unsigned int nfnl_str2inet_hook(const char *name) +{ + return __str2type(name, nfnl_inet_hooks, ARRAY_SIZE(nfnl_inet_hooks)); +} diff --git a/libnl/lib/netfilter/nfnl.c b/libnl/lib/netfilter/nfnl.c new file mode 100644 index 0000000..c70827b --- /dev/null +++ b/libnl/lib/netfilter/nfnl.c @@ -0,0 +1,247 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +/** + * @defgroup nfnl Netfilter Library (libnl-nf) + * + * @par Message Format + * @code + * <------- NLMSG_ALIGN(hlen) ------> <---- NLMSG_ALIGN(len) ---> + * +----------------------------+- - -+- - - - - - - - - - -+- - -+ + * | Header | Pad | Payload | Pad | + * | struct nlmsghdr | | | | + * +----------------------------+- - -+- - - - - - - - - - -+- - -+ + * @endcode + * @code + * <-------- NFNL_HDRLEN ---------> + * +--------------------------+- - -+------------+ + * | Netfilter Netlink Header | Pad | Attributes | + * | struct nfgenmsg | | | + * +--------------------------+- - -+------------+ + * nfnlmsg_attrdata(nfg, hdrlen)-----^ + * @endcode + * + * @par 1) Creating a new netfilter netlink message + * @code + * struct nl_msg *msg; + * + * // Create a new empty netlink message + * msg = nlmsg_alloc(); + * + * // Append the netlink and netfilter netlink message header + * hdr = nfnlmsg_put(msg, PID, SEQ, SUBSYS, TYPE, NLM_F_ECHO, + * FAMILY, RES_ID); + * + * // Append the attributes. + * nla_put_u32(msg, 1, 0x10); + * + * // Message is ready to be sent. + * nl_send_auto_complete(sk, msg); + * + * // All done? Free the message. + * nlmsg_free(msg); + * @endcode + * + * @par 2) Sending of trivial messages + * @code + * // For trivial messages not requiring any subsys specific header or + * // attributes, nfnl_send_simple() may be used to send messages directly. + * nfnl_send_simple(sk, SUBSYS, TYPE, 0, FAMILY, RES_ID); + * @endcode + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include + +#include "nl-aux-core/nl-core.h" + +/** + * @name Socket Creating + * @{ + */ + +/** + * Create and connect netfilter netlink socket. + * @arg sk Netlink socket. + * + * Creates a NETLINK_NETFILTER netlink socket, binds the socket and + * issues a connection attempt. + * + * @see nl_connect() + * + * @return 0 on success or a negative error code. + */ +int nfnl_connect(struct nl_sock *sk) +{ + return nl_connect(sk, NETLINK_NETFILTER); +} + +/** @} */ + +/** + * @name Sending + * @{ + */ + +/** + * Send trivial netfilter netlink message + * @arg sk Netlink socket. + * @arg subsys_id nfnetlink subsystem + * @arg type nfnetlink message type + * @arg flags message flags + * @arg family nfnetlink address family + * @arg res_id nfnetlink resource id + * + * @return 0 on success or a negative error code. Due to a bug, this function + * returns the number of bytes sent. Treat any non-negative number as success. + */ +int nfnl_send_simple(struct nl_sock *sk, uint8_t subsys_id, uint8_t type, + int flags, uint8_t family, uint16_t res_id) +{ + struct nfgenmsg hdr = { + .nfgen_family = family, + .version = NFNETLINK_V0, + .res_id = htons(res_id), + }; + + return nl_send_simple(sk, NFNLMSG_TYPE(subsys_id, type), flags, + &hdr, sizeof(hdr)); +} + +/** @} */ + +/** + * @name Message Parsing + * @{ + */ + +/** + * Get netfilter subsystem id from message + * @arg nlh netlink messsage header + */ +uint8_t nfnlmsg_subsys(struct nlmsghdr *nlh) +{ + return NFNL_SUBSYS_ID(nlh->nlmsg_type); +} + +/** + * Get netfilter message type from message + * @arg nlh netlink messsage header + */ +uint8_t nfnlmsg_subtype(struct nlmsghdr *nlh) +{ + return NFNL_MSG_TYPE(nlh->nlmsg_type); +} + +/** + * Get netfilter family from message + * @arg nlh netlink messsage header + */ +uint8_t nfnlmsg_family(struct nlmsghdr *nlh) +{ + struct nfgenmsg *nfg = nlmsg_data(nlh); + + return nfg->nfgen_family; +} + +/** + * Get netfilter resource id from message + * @arg nlh netlink messsage header + */ +uint16_t nfnlmsg_res_id(struct nlmsghdr *nlh) +{ + struct nfgenmsg *nfg = nlmsg_data(nlh); + + return ntohs(nfg->res_id); +} + +/** @} */ + +/** + * @name Message Building + * @{ + */ + +static int nfnlmsg_append(struct nl_msg *msg, uint8_t family, uint16_t res_id) +{ + struct nfgenmsg *nfg; + + nfg = nlmsg_reserve(msg, sizeof(*nfg), NLMSG_ALIGNTO); + if (nfg == NULL) + return -NLE_NOMEM; + + nfg->nfgen_family = family; + nfg->version = NFNETLINK_V0; + nfg->res_id = htons(res_id); + NL_DBG(2, "msg %p: Added nfnetlink header family=%d res_id=%d\n", + msg, family, res_id); + return 0; +} + +/** + * Allocate a new netfilter netlink message + * @arg subsys_id nfnetlink subsystem + * @arg type nfnetlink message type + * @arg flags message flags + * @arg family nfnetlink address family + * @arg res_id nfnetlink resource id + * + * @return Newly allocated netlink message or NULL. + */ +struct nl_msg *nfnlmsg_alloc_simple(uint8_t subsys_id, uint8_t type, int flags, + uint8_t family, uint16_t res_id) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc_simple(NFNLMSG_TYPE(subsys_id, type), flags); + if (msg == NULL) + return NULL; + + if (nfnlmsg_append(msg, family, res_id) < 0) + goto nla_put_failure; + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +} + +/** + * Add netlink and netfilter netlink headers to netlink message + * @arg msg netlink message + * @arg pid netlink process id + * @arg seq sequence number of message + * @arg subsys_id nfnetlink subsystem + * @arg type nfnetlink message type + * @arg flags message flags + * @arg family nfnetlink address family + * @arg res_id nfnetlink resource id + */ +int nfnlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq, + uint8_t subsys_id, uint8_t type, int flags, uint8_t family, + uint16_t res_id) +{ + struct nlmsghdr *nlh; + + nlh = nlmsg_put(msg, pid, seq, NFNLMSG_TYPE(subsys_id, type), 0, flags); + if (nlh == NULL) + return -NLE_MSGSIZE; + + return nfnlmsg_append(msg, family, res_id); +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/netfilter/nl-netfilter.h b/libnl/lib/netfilter/nl-netfilter.h new file mode 100644 index 0000000..c8a4899 --- /dev/null +++ b/libnl/lib/netfilter/nl-netfilter.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + * Copyright (c) 2013 Sassano Systems LLC + */ + +#ifndef __LIB_NETFILTER_NL_NETFILTER_H__ +#define __LIB_NETFILTER_NL_NETFILTER_H__ + +#include + +#include "nl-priv-dynamic-core/object-api.h" + +union nfnl_ct_proto { + struct { + uint16_t src; + uint16_t dst; + } port; + struct { + uint16_t id; + uint8_t type; + uint8_t code; + } icmp; +}; + +struct nfnl_ct_dir { + struct nl_addr *src; + struct nl_addr *dst; + union nfnl_ct_proto proto; + uint64_t packets; + uint64_t bytes; +}; + +union nfnl_ct_protoinfo { + struct { + uint8_t state; + } tcp; +}; + +struct nfnl_ct { + NLHDR_COMMON + + uint8_t ct_family; + uint8_t ct_proto; + union nfnl_ct_protoinfo ct_protoinfo; + + uint32_t ct_status; + uint32_t ct_status_mask; + uint32_t ct_timeout; + uint32_t ct_mark; + uint32_t ct_use; + uint32_t ct_id; + uint16_t ct_zone; + + struct nfnl_ct_dir ct_orig; + struct nfnl_ct_dir ct_repl; + + struct nfnl_ct_timestamp ct_tstamp; +}; + +union nfnl_exp_protodata { + struct { + uint16_t src; + uint16_t dst; + } port; + struct { + uint16_t id; + uint8_t type; + uint8_t code; + } icmp; +}; + +// Allow for different master/expect l4 protocols +struct nfnl_exp_proto { + uint8_t l4protonum; + union nfnl_exp_protodata l4protodata; +}; + +struct nfnl_exp_dir { + struct nl_addr *src; + struct nl_addr *dst; + struct nfnl_exp_proto proto; +}; + +struct nfnl_exp { + NLHDR_COMMON + + uint8_t exp_family; + uint32_t exp_timeout; + uint32_t exp_id; + uint16_t exp_zone; + uint32_t exp_class; + uint32_t exp_flags; + char *exp_helper_name; + char *exp_fn; + uint8_t exp_nat_dir; + + struct nfnl_exp_dir exp_expect; + struct nfnl_exp_dir exp_master; + struct nfnl_exp_dir exp_mask; + struct nfnl_exp_dir exp_nat; +}; + +struct nfnl_log_msg { + NLHDR_COMMON + + uint8_t log_msg_family; + uint8_t log_msg_hook; + uint16_t log_msg_hwproto; + uint32_t log_msg_mark; + struct timeval log_msg_timestamp; + uint32_t log_msg_indev; + uint32_t log_msg_outdev; + uint32_t log_msg_physindev; + uint32_t log_msg_physoutdev; + uint8_t log_msg_hwaddr[8]; + int log_msg_hwaddr_len; + void *log_msg_payload; + int log_msg_payload_len; + char *log_msg_prefix; + uint32_t log_msg_uid; + uint32_t log_msg_gid; + uint32_t log_msg_seq; + uint32_t log_msg_seq_global; + uint16_t log_msg_hwtype; + uint16_t log_msg_hwlen; + void *log_msg_hwheader; + int log_msg_hwheader_len; + uint16_t log_msg_vlan_tag; + uint16_t log_msg_vlan_proto; + uint32_t log_msg_ct_info; + struct nfnl_ct *log_msg_ct; +}; + +struct nfnl_queue_msg { + NLHDR_COMMON + + uint16_t queue_msg_group; + uint8_t queue_msg_family; + uint8_t queue_msg_hook; + uint16_t queue_msg_hwproto; + uint32_t queue_msg_packetid; + uint32_t queue_msg_mark; + struct timeval queue_msg_timestamp; + uint32_t queue_msg_indev; + uint32_t queue_msg_outdev; + uint32_t queue_msg_physindev; + uint32_t queue_msg_physoutdev; + uint8_t queue_msg_hwaddr[8]; + int queue_msg_hwaddr_len; + void *queue_msg_payload; + int queue_msg_payload_len; + uint32_t queue_msg_verdict; +}; + +#endif /* __LIB_NETFILTER_NL_NETFILTER_H__*/ diff --git a/libnl/lib/netfilter/queue.c b/libnl/lib/netfilter/queue.c new file mode 100644 index 0000000..b780b0f --- /dev/null +++ b/libnl/lib/netfilter/queue.c @@ -0,0 +1,250 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2007, 2008 Patrick McHardy + */ + +/** + * @ingroup nfnl + * @defgroup queue Queue + * @brief + * @{ + */ + +#include "nl-default.h" + +#include + +#include + +#include +#include +#include + +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" + +struct nl_sock *nfnl_queue_socket_alloc(void) +{ + struct nl_sock *nlsk; + + nlsk = nl_socket_alloc(); + if (nlsk) + nl_socket_disable_auto_ack(nlsk); + return nlsk; +} + +static int send_queue_request(struct nl_sock *sk, struct nl_msg *msg) +{ + int err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** + * @name Queue Commands + * @{ + */ + +static int build_queue_cmd_request(uint8_t family, uint16_t queuenum, + uint8_t command, struct nl_msg **result) +{ + struct nl_msg *msg; + struct nfqnl_msg_config_cmd cmd; + + msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_QUEUE, NFQNL_MSG_CONFIG, 0, + family, queuenum); + if (msg == NULL) + return -NLE_NOMEM; + + cmd.pf = htons(family); + cmd._pad = 0; + cmd.command = command; + if (nla_put(msg, NFQA_CFG_CMD, sizeof(cmd), &cmd) < 0) + goto nla_put_failure; + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +int nfnl_queue_build_pf_bind(uint8_t pf, struct nl_msg **result) +{ + return build_queue_cmd_request(pf, 0, NFQNL_CFG_CMD_PF_BIND, result); +} + +int nfnl_queue_pf_bind(struct nl_sock *nlh, uint8_t pf) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_queue_build_pf_bind(pf, &msg)) < 0) + return err; + + return send_queue_request(nlh, msg); +} + +int nfnl_queue_build_pf_unbind(uint8_t pf, struct nl_msg **result) +{ + return build_queue_cmd_request(pf, 0, NFQNL_CFG_CMD_PF_UNBIND, result); +} + +int nfnl_queue_pf_unbind(struct nl_sock *nlh, uint8_t pf) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_queue_build_pf_unbind(pf, &msg)) < 0) + return err; + + return send_queue_request(nlh, msg); +} + +static int nfnl_queue_build_request(const struct nfnl_queue *queue, + struct nl_msg **result) +{ + struct nl_msg *msg; + + if (!nfnl_queue_test_group(queue)) + return -NLE_MISSING_ATTR; + + msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_QUEUE, NFQNL_MSG_CONFIG, 0, + 0, nfnl_queue_get_group(queue)); + if (msg == NULL) + return -NLE_NOMEM; + + if (nfnl_queue_test_maxlen(queue) && + nla_put_u32(msg, NFQA_CFG_QUEUE_MAXLEN, + htonl(nfnl_queue_get_maxlen(queue))) < 0) + goto nla_put_failure; + + /* This sucks, the nfnetlink_queue interface always expects both + * parameters to be present. Needs to be done properly. + */ + if (nfnl_queue_test_copy_mode(queue)) { + struct nfqnl_msg_config_params params; + + switch (nfnl_queue_get_copy_mode(queue)) { + case NFNL_QUEUE_COPY_NONE: + params.copy_mode = NFQNL_COPY_NONE; + break; + case NFNL_QUEUE_COPY_META: + params.copy_mode = NFQNL_COPY_META; + break; + case NFNL_QUEUE_COPY_PACKET: + params.copy_mode = NFQNL_COPY_PACKET; + break; + } + params.copy_range = htonl(nfnl_queue_get_copy_range(queue)); + + if (nla_put(msg, NFQA_CFG_PARAMS, sizeof(params), ¶ms) < 0) + goto nla_put_failure; + } + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +int nfnl_queue_build_create_request(const struct nfnl_queue *queue, + struct nl_msg **result) +{ + struct nfqnl_msg_config_cmd cmd; + int err; + + if ((err = nfnl_queue_build_request(queue, result)) < 0) + return err; + + cmd.pf = 0; + cmd._pad = 0; + cmd.command = NFQNL_CFG_CMD_BIND; + + NLA_PUT(*result, NFQA_CFG_CMD, sizeof(cmd), &cmd); + + return 0; + +nla_put_failure: + nlmsg_free(*result); + return -NLE_MSGSIZE; +} + +int nfnl_queue_create(struct nl_sock *nlh, const struct nfnl_queue *queue) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_queue_build_create_request(queue, &msg)) < 0) + return err; + + return send_queue_request(nlh, msg); +} + +int nfnl_queue_build_change_request(const struct nfnl_queue *queue, + struct nl_msg **result) +{ + return nfnl_queue_build_request(queue, result); +} + +int nfnl_queue_change(struct nl_sock *nlh, const struct nfnl_queue *queue) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_queue_build_change_request(queue, &msg)) < 0) + return err; + + return send_queue_request(nlh, msg); +} + +int nfnl_queue_build_delete_request(const struct nfnl_queue *queue, + struct nl_msg **result) +{ + if (!nfnl_queue_test_group(queue)) + return -NLE_MISSING_ATTR; + + return build_queue_cmd_request(0, nfnl_queue_get_group(queue), + NFQNL_CFG_CMD_UNBIND, result); +} + +int nfnl_queue_delete(struct nl_sock *nlh, const struct nfnl_queue *queue) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_queue_build_delete_request(queue, &msg)) < 0) + return err; + + return send_queue_request(nlh, msg); +} + +/** @} */ + +static struct nl_cache_ops nfnl_queue_ops = { + .co_name = "netfilter/queue", + .co_obj_ops = &queue_obj_ops, + .co_msgtypes = { + END_OF_MSGTYPES_LIST, + }, +}; + +static void _nl_init nfnl_queue_init(void) +{ + nl_cache_mngt_register(&nfnl_queue_ops); +} + +static void _nl_exit nfnl_queue_exit(void) +{ + nl_cache_mngt_unregister(&nfnl_queue_ops); +} + +/** @} */ diff --git a/libnl/lib/netfilter/queue_msg.c b/libnl/lib/netfilter/queue_msg.c new file mode 100644 index 0000000..413d180 --- /dev/null +++ b/libnl/lib/netfilter/queue_msg.c @@ -0,0 +1,308 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2007, 2008 Patrick McHardy + * Copyright (c) 2010 Karl Hiramoto + */ + +/** + * @ingroup nfnl + * @defgroup queue Queue + * @brief + * @{ + */ + +#include "nl-default.h" + +#include + +#include + +#include +#include +#include + +#include "nl-netfilter.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" + +static struct nl_cache_ops nfnl_queue_msg_ops; + +static struct nla_policy queue_policy[NFQA_MAX+1] = { + [NFQA_PACKET_HDR] = { + .minlen = sizeof(struct nfqnl_msg_packet_hdr), + }, + [NFQA_VERDICT_HDR] = { + .minlen = sizeof(struct nfqnl_msg_verdict_hdr), + }, + [NFQA_MARK] = { .type = NLA_U32 }, + [NFQA_TIMESTAMP] = { + .minlen = sizeof(struct nfqnl_msg_packet_timestamp), + }, + [NFQA_IFINDEX_INDEV] = { .type = NLA_U32 }, + [NFQA_IFINDEX_OUTDEV] = { .type = NLA_U32 }, + [NFQA_IFINDEX_PHYSINDEV] = { .type = NLA_U32 }, + [NFQA_IFINDEX_PHYSOUTDEV] = { .type = NLA_U32 }, + [NFQA_HWADDR] = { + .minlen = sizeof(struct nfqnl_msg_packet_hw), + }, +}; + +int nfnlmsg_queue_msg_parse(struct nlmsghdr *nlh, + struct nfnl_queue_msg **result) +{ + struct nfnl_queue_msg *msg; + struct nlattr *tb[NFQA_MAX+1]; + struct nlattr *attr; + int err; + + msg = nfnl_queue_msg_alloc(); + if (!msg) + return -NLE_NOMEM; + + msg->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, NFQA_MAX, + queue_policy); + if (err < 0) + goto errout; + + nfnl_queue_msg_set_group(msg, nfnlmsg_res_id(nlh)); + nfnl_queue_msg_set_family(msg, nfnlmsg_family(nlh)); + + attr = tb[NFQA_PACKET_HDR]; + if (attr) { + struct nfqnl_msg_packet_hdr *hdr = nla_data(attr); + + nfnl_queue_msg_set_packetid(msg, ntohl(hdr->packet_id)); + if (hdr->hw_protocol) + nfnl_queue_msg_set_hwproto(msg, hdr->hw_protocol); + nfnl_queue_msg_set_hook(msg, hdr->hook); + } + + attr = tb[NFQA_MARK]; + if (attr) + nfnl_queue_msg_set_mark(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFQA_TIMESTAMP]; + if (attr) { + struct nfqnl_msg_packet_timestamp *timestamp = nla_data(attr); + struct timeval tv; + + tv.tv_sec = ntohll(timestamp->sec); + tv.tv_usec = ntohll(timestamp->usec); + nfnl_queue_msg_set_timestamp(msg, &tv); + } + + attr = tb[NFQA_IFINDEX_INDEV]; + if (attr) + nfnl_queue_msg_set_indev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFQA_IFINDEX_OUTDEV]; + if (attr) + nfnl_queue_msg_set_outdev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFQA_IFINDEX_PHYSINDEV]; + if (attr) + nfnl_queue_msg_set_physindev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFQA_IFINDEX_PHYSOUTDEV]; + if (attr) + nfnl_queue_msg_set_physoutdev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFQA_HWADDR]; + if (attr) { + struct nfqnl_msg_packet_hw *hw = nla_data(attr); + + nfnl_queue_msg_set_hwaddr(msg, hw->hw_addr, + ntohs(hw->hw_addrlen)); + } + + attr = tb[NFQA_PAYLOAD]; + if (attr) { + err = nfnl_queue_msg_set_payload(msg, nla_data(attr), + nla_len(attr)); + if (err < 0) + goto errout; + } + + *result = msg; + return 0; + +errout: + nfnl_queue_msg_put(msg); + return err; +} + +static int queue_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct nfnl_queue_msg *msg; + int err; + + if ((err = nfnlmsg_queue_msg_parse(nlh, &msg)) < 0) + return err; + + err = pp->pp_cb((struct nl_object *) msg, pp); + nfnl_queue_msg_put(msg); + return err; +} + +/** @} */ + +static struct nl_msg * +__nfnl_queue_msg_build_verdict(const struct nfnl_queue_msg *msg, + uint8_t type) +{ + struct nl_msg *nlmsg; + struct nfqnl_msg_verdict_hdr verdict; + + nlmsg = nfnlmsg_alloc_simple(NFNL_SUBSYS_QUEUE, type, 0, + nfnl_queue_msg_get_family(msg), + nfnl_queue_msg_get_group(msg)); + if (nlmsg == NULL) + return NULL; + + verdict.id = htonl(nfnl_queue_msg_get_packetid(msg)); + verdict.verdict = htonl(nfnl_queue_msg_get_verdict(msg)); + if (nla_put(nlmsg, NFQA_VERDICT_HDR, sizeof(verdict), &verdict) < 0) + goto nla_put_failure; + + if (nfnl_queue_msg_test_mark(msg) && + nla_put_u32(nlmsg, NFQA_MARK, + ntohl(nfnl_queue_msg_get_mark(msg))) < 0) + goto nla_put_failure; + + return nlmsg; + +nla_put_failure: + nlmsg_free(nlmsg); + return NULL; +} + +struct nl_msg * +nfnl_queue_msg_build_verdict(const struct nfnl_queue_msg *msg) +{ + return __nfnl_queue_msg_build_verdict(msg, NFQNL_MSG_VERDICT); +} + +struct nl_msg * +nfnl_queue_msg_build_verdict_batch(const struct nfnl_queue_msg *msg) +{ + return __nfnl_queue_msg_build_verdict(msg, NFQNL_MSG_VERDICT_BATCH); +} + +/** +* Send a message verdict/mark +* @arg nlh netlink messsage header +* @arg msg queue msg +* @return 0 on OK or error code +*/ +int nfnl_queue_msg_send_verdict(struct nl_sock *nlh, + const struct nfnl_queue_msg *msg) +{ + struct nl_msg *nlmsg; + int err; + + nlmsg = nfnl_queue_msg_build_verdict(msg); + if (nlmsg == NULL) + return -NLE_NOMEM; + + err = nl_send_auto_complete(nlh, nlmsg); + nlmsg_free(nlmsg); + if (err < 0) + return err; + return wait_for_ack(nlh); +} + +/** +* Send a message batched verdict/mark +* @arg nlh netlink messsage header +* @arg msg queue msg +* @return 0 on OK or error code +*/ +int nfnl_queue_msg_send_verdict_batch(struct nl_sock *nlh, + const struct nfnl_queue_msg *msg) +{ + struct nl_msg *nlmsg; + int err; + + nlmsg = nfnl_queue_msg_build_verdict_batch(msg); + if (nlmsg == NULL) + return -NLE_NOMEM; + + err = nl_send_auto_complete(nlh, nlmsg); + nlmsg_free(nlmsg); + if (err < 0) + return err; + return wait_for_ack(nlh); +} + +/** +* Send a message verdict including the payload +* @arg nlh netlink messsage header +* @arg msg queue msg +* @arg payload_data packet payload data +* @arg payload_len payload length +* @return 0 on OK or error code +*/ +int nfnl_queue_msg_send_verdict_payload(struct nl_sock *nlh, + const struct nfnl_queue_msg *msg, + const void *payload_data, unsigned payload_len) +{ + struct nl_msg *nlmsg; + int err; + struct iovec iov[3]; + struct nlattr nla; + + nlmsg = nfnl_queue_msg_build_verdict(msg); + if (nlmsg == NULL) + return -NLE_NOMEM; + + memset(iov, 0, sizeof(iov)); + + iov[0].iov_base = (void *) nlmsg_hdr(nlmsg); + iov[0].iov_len = nlmsg_hdr(nlmsg)->nlmsg_len; + + nla.nla_type = NFQA_PAYLOAD; + nla.nla_len = payload_len + sizeof(nla); + nlmsg_hdr(nlmsg)->nlmsg_len += nla.nla_len; + + iov[1].iov_base = (void *) &nla; + iov[1].iov_len = sizeof(nla); + + iov[2].iov_base = (void *) payload_data; + iov[2].iov_len = NLA_ALIGN(payload_len); + + nl_complete_msg(nlh, nlmsg); + err = nl_send_iovec(nlh, nlmsg, iov, 3); + + nlmsg_free(nlmsg); + if (err < 0) + return err; + return wait_for_ack(nlh); +} + +#define NFNLMSG_QUEUE_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_QUEUE, (type)) +static struct nl_cache_ops nfnl_queue_msg_ops = { + .co_name = "netfilter/queue_msg", + .co_hdrsize = NFNL_HDRLEN, + .co_msgtypes = { + { NFNLMSG_QUEUE_TYPE(NFQNL_MSG_PACKET), NL_ACT_NEW, "new" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_NETFILTER, + .co_msg_parser = queue_msg_parser, + .co_obj_ops = &queue_msg_obj_ops, +}; + +static void _nl_init nfnl_msg_queue_init(void) +{ + nl_cache_mngt_register(&nfnl_queue_msg_ops); +} + +static void _nl_exit nfnl_queue_msg_exit(void) +{ + nl_cache_mngt_unregister(&nfnl_queue_msg_ops); +} + +/** @} */ diff --git a/libnl/lib/netfilter/queue_msg_obj.c b/libnl/lib/netfilter/queue_msg_obj.c new file mode 100644 index 0000000..210351b --- /dev/null +++ b/libnl/lib/netfilter/queue_msg_obj.c @@ -0,0 +1,505 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2007, 2008 Patrick McHardy + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include + +#include "nl-netfilter.h" +#include "nl-priv-dynamic-core/nl-core.h" + +/** @cond SKIP */ +#define QUEUE_MSG_ATTR_GROUP (1UL << 0) +#define QUEUE_MSG_ATTR_FAMILY (1UL << 1) +#define QUEUE_MSG_ATTR_PACKETID (1UL << 2) +#define QUEUE_MSG_ATTR_HWPROTO (1UL << 3) +#define QUEUE_MSG_ATTR_HOOK (1UL << 4) +#define QUEUE_MSG_ATTR_MARK (1UL << 5) +#define QUEUE_MSG_ATTR_TIMESTAMP (1UL << 6) +#define QUEUE_MSG_ATTR_INDEV (1UL << 7) +#define QUEUE_MSG_ATTR_OUTDEV (1UL << 8) +#define QUEUE_MSG_ATTR_PHYSINDEV (1UL << 9) +#define QUEUE_MSG_ATTR_PHYSOUTDEV (1UL << 10) +#define QUEUE_MSG_ATTR_HWADDR (1UL << 11) +#define QUEUE_MSG_ATTR_PAYLOAD (1UL << 12) +#define QUEUE_MSG_ATTR_VERDICT (1UL << 13) +/** @endcond */ + +static void nfnl_queue_msg_free_data(struct nl_object *c) +{ + struct nfnl_queue_msg *msg = (struct nfnl_queue_msg *) c; + + if (msg == NULL) + return; + + free(msg->queue_msg_payload); +} + +static int nfnl_queue_msg_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct nfnl_queue_msg *dst = (struct nfnl_queue_msg *) _dst; + struct nfnl_queue_msg *src = (struct nfnl_queue_msg *) _src; + int err; + + dst->queue_msg_payload = NULL; + dst->queue_msg_payload_len = 0; + + if (src->queue_msg_payload) { + err = nfnl_queue_msg_set_payload(dst, src->queue_msg_payload, + src->queue_msg_payload_len); + if (err < 0) + return err; + } + + return 0; +} + +static void nfnl_queue_msg_dump(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_queue_msg *msg = (struct nfnl_queue_msg *) a; + struct nl_cache *link_cache; + char buf[64]; + + link_cache = nl_cache_mngt_require_safe("route/link"); + + nl_new_line(p); + + if (msg->ce_mask & QUEUE_MSG_ATTR_GROUP) + nl_dump(p, "GROUP=%u ", msg->queue_msg_group); + + if (msg->ce_mask & QUEUE_MSG_ATTR_INDEV) { + if (link_cache) + nl_dump(p, "IN=%s ", + rtnl_link_i2name(link_cache, + msg->queue_msg_indev, + buf, sizeof(buf))); + else + nl_dump(p, "IN=%d ", msg->queue_msg_indev); + } + + if (msg->ce_mask & QUEUE_MSG_ATTR_PHYSINDEV) { + if (link_cache) + nl_dump(p, "PHYSIN=%s ", + rtnl_link_i2name(link_cache, + msg->queue_msg_physindev, + buf, sizeof(buf))); + else + nl_dump(p, "IN=%d ", msg->queue_msg_physindev); + } + + if (msg->ce_mask & QUEUE_MSG_ATTR_OUTDEV) { + if (link_cache) + nl_dump(p, "OUT=%s ", + rtnl_link_i2name(link_cache, + msg->queue_msg_outdev, + buf, sizeof(buf))); + else + nl_dump(p, "OUT=%d ", msg->queue_msg_outdev); + } + + if (msg->ce_mask & QUEUE_MSG_ATTR_PHYSOUTDEV) { + if (link_cache) + nl_dump(p, "PHYSOUT=%s ", + rtnl_link_i2name(link_cache, + msg->queue_msg_physoutdev, + buf, sizeof(buf))); + else + nl_dump(p, "PHYSOUT=%d ", msg->queue_msg_physoutdev); + } + + if (msg->ce_mask & QUEUE_MSG_ATTR_HWADDR) { + int i; + + nl_dump(p, "MAC"); + for (i = 0; i < msg->queue_msg_hwaddr_len; i++) + nl_dump(p, "%c%02x", i?':':'=', + msg->queue_msg_hwaddr[i]); + nl_dump(p, " "); + } + + if (msg->ce_mask & QUEUE_MSG_ATTR_FAMILY) + nl_dump(p, "FAMILY=%s ", + nl_af2str(msg->queue_msg_family, buf, sizeof(buf))); + + if (msg->ce_mask & QUEUE_MSG_ATTR_HWPROTO) + nl_dump(p, "HWPROTO=%s ", + nl_ether_proto2str(ntohs(msg->queue_msg_hwproto), + buf, sizeof(buf))); + + if (msg->ce_mask & QUEUE_MSG_ATTR_HOOK) + nl_dump(p, "HOOK=%s ", + nfnl_inet_hook2str(msg->queue_msg_hook, + buf, sizeof(buf))); + + if (msg->ce_mask & QUEUE_MSG_ATTR_MARK) + nl_dump(p, "MARK=%d ", msg->queue_msg_mark); + + if (msg->ce_mask & QUEUE_MSG_ATTR_PAYLOAD) + nl_dump(p, "PAYLOADLEN=%d ", msg->queue_msg_payload_len); + + if (msg->ce_mask & QUEUE_MSG_ATTR_PACKETID) + nl_dump(p, "PACKETID=%u ", msg->queue_msg_packetid); + + if (msg->ce_mask & QUEUE_MSG_ATTR_VERDICT) + nl_dump(p, "VERDICT=%s ", + nfnl_verdict2str(msg->queue_msg_verdict, + buf, sizeof(buf))); + + nl_dump(p, "\n"); + + if (link_cache) + nl_cache_put(link_cache); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct nfnl_queue_msg *nfnl_queue_msg_alloc(void) +{ + return (struct nfnl_queue_msg *) nl_object_alloc(&queue_msg_obj_ops); +} + +void nfnl_queue_msg_get(struct nfnl_queue_msg *msg) +{ + nl_object_get((struct nl_object *) msg); +} + +void nfnl_queue_msg_put(struct nfnl_queue_msg *msg) +{ + nl_object_put((struct nl_object *) msg); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nfnl_queue_msg_set_group(struct nfnl_queue_msg *msg, uint16_t group) +{ + msg->queue_msg_group = group; + msg->ce_mask |= QUEUE_MSG_ATTR_GROUP; +} + +int nfnl_queue_msg_test_group(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_GROUP); +} + +uint16_t nfnl_queue_msg_get_group(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_group; +} + +/** +* Set the protocol family +* @arg msg NF queue message +* @arg family AF_XXX address family example: AF_INET, AF_UNIX, etc +*/ +void nfnl_queue_msg_set_family(struct nfnl_queue_msg *msg, uint8_t family) +{ + msg->queue_msg_family = family; + msg->ce_mask |= QUEUE_MSG_ATTR_FAMILY; +} + +int nfnl_queue_msg_test_family(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_FAMILY); +} + +uint8_t nfnl_queue_msg_get_family(const struct nfnl_queue_msg *msg) +{ + if (msg->ce_mask & QUEUE_MSG_ATTR_FAMILY) + return msg->queue_msg_family; + else + return AF_UNSPEC; +} + +void nfnl_queue_msg_set_packetid(struct nfnl_queue_msg *msg, uint32_t packetid) +{ + msg->queue_msg_packetid = packetid; + msg->ce_mask |= QUEUE_MSG_ATTR_PACKETID; +} + +int nfnl_queue_msg_test_packetid(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_PACKETID); +} + +uint32_t nfnl_queue_msg_get_packetid(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_packetid; +} + +void nfnl_queue_msg_set_hwproto(struct nfnl_queue_msg *msg, uint16_t hwproto) +{ + msg->queue_msg_hwproto = hwproto; + msg->ce_mask |= QUEUE_MSG_ATTR_HWPROTO; +} + +int nfnl_queue_msg_test_hwproto(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_HWPROTO); +} + +uint16_t nfnl_queue_msg_get_hwproto(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_hwproto; +} + +void nfnl_queue_msg_set_hook(struct nfnl_queue_msg *msg, uint8_t hook) +{ + msg->queue_msg_hook = hook; + msg->ce_mask |= QUEUE_MSG_ATTR_HOOK; +} + +int nfnl_queue_msg_test_hook(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_HOOK); +} + +uint8_t nfnl_queue_msg_get_hook(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_hook; +} + +void nfnl_queue_msg_set_mark(struct nfnl_queue_msg *msg, uint32_t mark) +{ + msg->queue_msg_mark = mark; + msg->ce_mask |= QUEUE_MSG_ATTR_MARK; +} + +int nfnl_queue_msg_test_mark(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_MARK); +} + +uint32_t nfnl_queue_msg_get_mark(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_mark; +} + +void nfnl_queue_msg_set_timestamp(struct nfnl_queue_msg *msg, + struct timeval *tv) +{ + msg->queue_msg_timestamp.tv_sec = tv->tv_sec; + msg->queue_msg_timestamp.tv_usec = tv->tv_usec; + msg->ce_mask |= QUEUE_MSG_ATTR_TIMESTAMP; +} + +int nfnl_queue_msg_test_timestamp(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_TIMESTAMP); +} + +const struct timeval *nfnl_queue_msg_get_timestamp(const struct nfnl_queue_msg *msg) +{ + if (!(msg->ce_mask & QUEUE_MSG_ATTR_TIMESTAMP)) + return NULL; + return &msg->queue_msg_timestamp; +} + +void nfnl_queue_msg_set_indev(struct nfnl_queue_msg *msg, uint32_t indev) +{ + msg->queue_msg_indev = indev; + msg->ce_mask |= QUEUE_MSG_ATTR_INDEV; +} + +int nfnl_queue_msg_test_indev(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_INDEV); +} + +uint32_t nfnl_queue_msg_get_indev(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_indev; +} + +void nfnl_queue_msg_set_outdev(struct nfnl_queue_msg *msg, uint32_t outdev) +{ + msg->queue_msg_outdev = outdev; + msg->ce_mask |= QUEUE_MSG_ATTR_OUTDEV; +} + +int nfnl_queue_msg_test_outdev(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_OUTDEV); +} + +uint32_t nfnl_queue_msg_get_outdev(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_outdev; +} + +void nfnl_queue_msg_set_physindev(struct nfnl_queue_msg *msg, + uint32_t physindev) +{ + msg->queue_msg_physindev = physindev; + msg->ce_mask |= QUEUE_MSG_ATTR_PHYSINDEV; +} + +int nfnl_queue_msg_test_physindev(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_PHYSINDEV); +} + +uint32_t nfnl_queue_msg_get_physindev(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_physindev; +} + +void nfnl_queue_msg_set_physoutdev(struct nfnl_queue_msg *msg, + uint32_t physoutdev) +{ + msg->queue_msg_physoutdev = physoutdev; + msg->ce_mask |= QUEUE_MSG_ATTR_PHYSOUTDEV; +} + +int nfnl_queue_msg_test_physoutdev(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_PHYSOUTDEV); +} + +uint32_t nfnl_queue_msg_get_physoutdev(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_physoutdev; +} + +void nfnl_queue_msg_set_hwaddr(struct nfnl_queue_msg *msg, uint8_t *hwaddr, + int len) +{ + if (len < 0) + len = 0; + else if (((unsigned)len) > sizeof(msg->queue_msg_hwaddr)) + len = sizeof(msg->queue_msg_hwaddr); + msg->queue_msg_hwaddr_len = len; + memcpy(msg->queue_msg_hwaddr, hwaddr, len); + msg->ce_mask |= QUEUE_MSG_ATTR_HWADDR; +} + +int nfnl_queue_msg_test_hwaddr(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_HWADDR); +} + +const uint8_t *nfnl_queue_msg_get_hwaddr(const struct nfnl_queue_msg *msg, + int *len) +{ + if (!(msg->ce_mask & QUEUE_MSG_ATTR_HWADDR)) { + *len = 0; + return NULL; + } + + *len = msg->queue_msg_hwaddr_len; + return msg->queue_msg_hwaddr; +} + +int nfnl_queue_msg_set_payload(struct nfnl_queue_msg *msg, uint8_t *payload, + int len) +{ + void *p = NULL; + + if (len < 0) + return -NLE_INVAL; + + p = _nl_memdup(payload, len); + if (!p && len > 0) + return -NLE_NOMEM; + + free(msg->queue_msg_payload); + msg->queue_msg_payload = p; + msg->queue_msg_payload_len = len; + if (len > 0) + msg->ce_mask |= QUEUE_MSG_ATTR_PAYLOAD; + else + msg->ce_mask &= ~QUEUE_MSG_ATTR_PAYLOAD; + return 0; +} + +int nfnl_queue_msg_test_payload(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_PAYLOAD); +} + +const void *nfnl_queue_msg_get_payload(const struct nfnl_queue_msg *msg, int *len) +{ + if (!(msg->ce_mask & QUEUE_MSG_ATTR_PAYLOAD)) { + *len = 0; + return NULL; + } + + *len = msg->queue_msg_payload_len; + return msg->queue_msg_payload; +} + +/** +* Return the number of items matching a filter in the cache +* @arg msg queue msg +* @arg verdict NF_DROP, NF_ACCEPT, NF_REPEAT, etc +*/ +void nfnl_queue_msg_set_verdict(struct nfnl_queue_msg *msg, + unsigned int verdict) +{ + msg->queue_msg_verdict = verdict; + msg->ce_mask |= QUEUE_MSG_ATTR_VERDICT; +} + +int nfnl_queue_msg_test_verdict(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_VERDICT); +} + +unsigned int nfnl_queue_msg_get_verdict(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_verdict; +} + +static const struct trans_tbl nfnl_queue_msg_attrs[] = { + __ADD(QUEUE_MSG_ATTR_GROUP, group), + __ADD(QUEUE_MSG_ATTR_FAMILY, family), + __ADD(QUEUE_MSG_ATTR_PACKETID, packetid), + __ADD(QUEUE_MSG_ATTR_HWPROTO, hwproto), + __ADD(QUEUE_MSG_ATTR_HOOK, hook), + __ADD(QUEUE_MSG_ATTR_MARK, mark), + __ADD(QUEUE_MSG_ATTR_TIMESTAMP, timestamp), + __ADD(QUEUE_MSG_ATTR_INDEV, indev), + __ADD(QUEUE_MSG_ATTR_OUTDEV, outdev), + __ADD(QUEUE_MSG_ATTR_PHYSINDEV, physindev), + __ADD(QUEUE_MSG_ATTR_PHYSOUTDEV, physoutdev), + __ADD(QUEUE_MSG_ATTR_HWADDR, hwaddr), + __ADD(QUEUE_MSG_ATTR_PAYLOAD, payload), + __ADD(QUEUE_MSG_ATTR_VERDICT, verdict), +}; + +static char *nfnl_queue_msg_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, nfnl_queue_msg_attrs, + ARRAY_SIZE(nfnl_queue_msg_attrs)); +} + +/** @} */ + +struct nl_object_ops queue_msg_obj_ops = { + .oo_name = "netfilter/queuemsg", + .oo_size = sizeof(struct nfnl_queue_msg), + .oo_free_data = nfnl_queue_msg_free_data, + .oo_clone = nfnl_queue_msg_clone, + .oo_dump = { + [NL_DUMP_LINE] = nfnl_queue_msg_dump, + [NL_DUMP_DETAILS] = nfnl_queue_msg_dump, + [NL_DUMP_STATS] = nfnl_queue_msg_dump, + }, + .oo_attrs2str = nfnl_queue_msg_attrs2str, +}; + +/** @} */ diff --git a/libnl/lib/netfilter/queue_obj.c b/libnl/lib/netfilter/queue_obj.c new file mode 100644 index 0000000..a6b08a2 --- /dev/null +++ b/libnl/lib/netfilter/queue_obj.c @@ -0,0 +1,218 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2007, 2008 Patrick McHardy + */ + +/** + * @ingroup nfnl + * @defgroup queue Queue + * @brief + * @{ + */ + +#include "nl-default.h" + +#include +#include + +#include "nl-priv-dynamic-core/object-api.h" +#include "nl-priv-dynamic-core/nl-core.h" + +/** @cond SKIP */ +struct nfnl_queue { + NLHDR_COMMON + + uint16_t queue_group; + uint32_t queue_maxlen; + uint32_t queue_copy_range; + uint8_t queue_copy_mode; +}; + +#define QUEUE_ATTR_GROUP (1UL << 0) +#define QUEUE_ATTR_MAXLEN (1UL << 1) +#define QUEUE_ATTR_COPY_MODE (1UL << 2) +#define QUEUE_ATTR_COPY_RANGE (1UL << 3) +/** @endcond */ + + +static void nfnl_queue_dump(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_queue *queue = (struct nfnl_queue *) a; + char buf[64]; + + nl_new_line(p); + + if (queue->ce_mask & QUEUE_ATTR_GROUP) + nl_dump(p, "group=%u ", queue->queue_group); + + if (queue->ce_mask & QUEUE_ATTR_MAXLEN) + nl_dump(p, "maxlen=%u ", queue->queue_maxlen); + + if (queue->ce_mask & QUEUE_ATTR_COPY_MODE) + nl_dump(p, "copy_mode=%s ", + nfnl_queue_copy_mode2str(queue->queue_copy_mode, + buf, sizeof(buf))); + + if (queue->ce_mask & QUEUE_ATTR_COPY_RANGE) + nl_dump(p, "copy_range=%u ", queue->queue_copy_range); + + nl_dump(p, "\n"); +} + +static const struct trans_tbl copy_modes[] = { + __ADD(NFNL_QUEUE_COPY_NONE, none), + __ADD(NFNL_QUEUE_COPY_META, meta), + __ADD(NFNL_QUEUE_COPY_PACKET, packet), +}; + +char *nfnl_queue_copy_mode2str(enum nfnl_queue_copy_mode copy_mode, char *buf, + size_t len) +{ + return __type2str(copy_mode, buf, len, copy_modes, + ARRAY_SIZE(copy_modes)); +} + +int nfnl_queue_str2copy_mode(const char *name) +{ + return __str2type(name, copy_modes, ARRAY_SIZE(copy_modes)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct nfnl_queue *nfnl_queue_alloc(void) +{ + return (struct nfnl_queue *) nl_object_alloc(&queue_obj_ops); +} + +void nfnl_queue_get(struct nfnl_queue *queue) +{ + nl_object_get((struct nl_object *) queue); +} + +void nfnl_queue_put(struct nfnl_queue *queue) +{ + nl_object_put((struct nl_object *) queue); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nfnl_queue_set_group(struct nfnl_queue *queue, uint16_t group) +{ + queue->queue_group = group; + queue->ce_mask |= QUEUE_ATTR_GROUP; +} + +int nfnl_queue_test_group(const struct nfnl_queue *queue) +{ + return !!(queue->ce_mask & QUEUE_ATTR_GROUP); +} + +uint16_t nfnl_queue_get_group(const struct nfnl_queue *queue) +{ + return queue->queue_group; +} + +void nfnl_queue_set_maxlen(struct nfnl_queue *queue, uint32_t maxlen) +{ + queue->queue_maxlen = maxlen; + queue->ce_mask |= QUEUE_ATTR_MAXLEN; +} + +int nfnl_queue_test_maxlen(const struct nfnl_queue *queue) +{ + return !!(queue->ce_mask & QUEUE_ATTR_MAXLEN); +} + +uint32_t nfnl_queue_get_maxlen(const struct nfnl_queue *queue) +{ + return queue->queue_maxlen; +} + +void nfnl_queue_set_copy_mode(struct nfnl_queue *queue, enum nfnl_queue_copy_mode mode) +{ + queue->queue_copy_mode = mode; + queue->ce_mask |= QUEUE_ATTR_COPY_MODE; +} + +int nfnl_queue_test_copy_mode(const struct nfnl_queue *queue) +{ + return !!(queue->ce_mask & QUEUE_ATTR_COPY_MODE); +} + +enum nfnl_queue_copy_mode nfnl_queue_get_copy_mode(const struct nfnl_queue *queue) +{ + return queue->queue_copy_mode; +} + +void nfnl_queue_set_copy_range(struct nfnl_queue *queue, uint32_t copy_range) +{ + queue->queue_copy_range = copy_range; + queue->ce_mask |= QUEUE_ATTR_COPY_RANGE; +} + +int nfnl_queue_test_copy_range(const struct nfnl_queue *queue) +{ + return !!(queue->ce_mask & QUEUE_ATTR_COPY_RANGE); +} + +uint32_t nfnl_queue_get_copy_range(const struct nfnl_queue *queue) +{ + return queue->queue_copy_range; +} + +static uint64_t nfnl_queue_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct nfnl_queue *a = (struct nfnl_queue *) _a; + struct nfnl_queue *b = (struct nfnl_queue *) _b; + uint64_t diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) +#define _DIFF_VAL(ATTR, FIELD) _DIFF(ATTR, a->FIELD != b->FIELD) + diff |= _DIFF_VAL(QUEUE_ATTR_GROUP, queue_group); + diff |= _DIFF_VAL(QUEUE_ATTR_MAXLEN, queue_maxlen); + diff |= _DIFF_VAL(QUEUE_ATTR_COPY_MODE, queue_copy_mode); + diff |= _DIFF_VAL(QUEUE_ATTR_COPY_RANGE, queue_copy_range); +#undef _DIFF +#undef _DIFF_VAL + + return diff; +} + +static const struct trans_tbl nfnl_queue_attrs[] = { + __ADD(QUEUE_ATTR_GROUP, group), + __ADD(QUEUE_ATTR_MAXLEN, maxlen), + __ADD(QUEUE_ATTR_COPY_MODE, copy_mode), + __ADD(QUEUE_ATTR_COPY_RANGE, copy_range), +}; + +static char *nfnl_queue_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, nfnl_queue_attrs, + ARRAY_SIZE(nfnl_queue_attrs)); +} + +/** @} */ + +struct nl_object_ops queue_obj_ops = { + .oo_name = "netfilter/queue", + .oo_size = sizeof(struct nfnl_queue), + .oo_dump = { + [NL_DUMP_LINE] = nfnl_queue_dump, + [NL_DUMP_DETAILS] = nfnl_queue_dump, + [NL_DUMP_STATS] = nfnl_queue_dump, + }, + .oo_compare = nfnl_queue_compare, + .oo_attrs2str = nfnl_queue_attrs2str, + .oo_id_attrs = QUEUE_ATTR_GROUP, +}; + +/** @} */ diff --git a/libnl/lib/nl-core.h b/libnl/lib/nl-core.h new file mode 100644 index 0000000..42d7629 --- /dev/null +++ b/libnl/lib/nl-core.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Thomas Graf + */ + +#ifndef __LIB_NL_CORE_H__ +#define __LIB_NL_CORE_H__ + +struct nl_cb +{ + nl_recvmsg_msg_cb_t cb_set[NL_CB_TYPE_MAX+1]; + void * cb_args[NL_CB_TYPE_MAX+1]; + + nl_recvmsg_err_cb_t cb_err; + void * cb_err_arg; + + /** May be used to replace nl_recvmsgs with your own implementation + * in all internal calls to nl_recvmsgs. */ + int (*cb_recvmsgs_ow)(struct nl_sock *, + struct nl_cb *); + + /** Overwrite internal calls to nl_recv, must return the number of + * octets read and allocate a buffer for the received data. */ + int (*cb_recv_ow)(struct nl_sock *, + struct sockaddr_nl *, + unsigned char **, + struct ucred **); + + /** Overwrites internal calls to nl_send, must send the netlink + * message. */ + int (*cb_send_ow)(struct nl_sock *, + struct nl_msg *); + + int cb_refcnt; + /** indicates the callback that is currently active */ + enum nl_cb_type cb_active; +}; + +static inline int nl_cb_call(struct nl_cb *cb, enum nl_cb_type type, struct nl_msg *msg) +{ + int ret; + + cb->cb_active = type; + ret = cb->cb_set[type](msg, cb->cb_args[type]); + cb->cb_active = __NL_CB_TYPE_MAX; + return ret; +} + +int _nl_socket_is_local_port_unspecified (struct nl_sock *sk); +uint32_t _nl_socket_set_local_port_no_release(struct nl_sock *sk, int generate_other); + +void _nl_socket_used_ports_release_all(const uint32_t *used_ports); +void _nl_socket_used_ports_set(uint32_t *used_ports, uint32_t port); + +extern int nl_cache_parse(struct nl_cache_ops *, struct sockaddr_nl *, + struct nlmsghdr *, struct nl_parser_param *); + +extern void dump_from_ops(struct nl_object *, struct nl_dump_params *); + +#endif /* __LIB_NL_CORE_H__ */ diff --git a/libnl/lib/nl.c b/libnl/lib/nl.c new file mode 100644 index 0000000..1225eba --- /dev/null +++ b/libnl/lib/nl.c @@ -0,0 +1,1257 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @defgroup core Core Library (libnl) + * + * Socket handling, connection management, sending and receiving of data, + * message construction and parsing, object caching system, ... + * + * This is the API reference of the core library. It is not meant as a guide + * but as a reference. Please refer to the core library guide for detailed + * documentation on the library architecture and examples: + * + * * @ref_asciidoc{core,_,Netlink Core Library Development Guide} + * + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include + +#include "nl-core.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-aux-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" + +/** + * @defgroup core_types Data Types + * + * Core library data types + * @{ + * @} + * + * @defgroup send_recv Send & Receive Data + * + * Connection management, sending & receiving of data + * + * Related sections in the development guide: + * - @core_doc{core_send_recv, Sending & Receiving} + * - @core_doc{core_sockets, Sockets} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +/** + * @name Connection Management + * @{ + */ + +/** + * Create file descriptor and bind socket. + * @arg sk Netlink socket (required) + * @arg protocol Netlink protocol to use (required) + * + * Creates a new Netlink socket using `socket()` and binds the socket to the + * protocol and local port specified in the `sk` socket object. Fails if + * the socket is already connected. + * + * @note If available, the `close-on-exec` (`SOCK_CLOEXEC`) feature is enabled + * automatically on the new file descriptor. This causes the socket to + * be closed automatically if any of the `exec` family functions succeed. + * This is essential for multi threaded programs. + * + * @note The local port (`nl_socket_get_local_port()`) is unspecified after + * creating a new socket. It only gets determined when accessing the + * port the first time or during `nl_connect()`. When nl_connect() + * fails during `bind()` due to `ADDRINUSE`, it will retry with + * different ports if the port is unspecified. Unless you want to enforce + * the use of a specific local port, don't access the local port (or + * reset it to `unspecified` by calling `nl_socket_set_local_port(sk, 0)`). + * This capability is indicated by + * `%NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE`. + * + * @note nl_connect() creates and sets the file descriptor. You can setup the file + * descriptor yourself by creating and binding it, and then calling + * nl_socket_set_fd(). The result will be the same. + * + * @see nl_socket_alloc() + * @see nl_close() + * @see nl_socket_set_fd() + * + * @return 0 on success or a negative error code. + * + * @retval -NLE_BAD_SOCK Socket is already connected + */ +int nl_connect(struct nl_sock *sk, int protocol) +{ + int err, flags = 0; + int errsv; + socklen_t addrlen; + struct sockaddr_nl local = { 0 }; + int try_bind = 1; + +#ifdef SOCK_CLOEXEC + flags |= SOCK_CLOEXEC; +#endif + + if (sk->s_fd != -1) + return -NLE_BAD_SOCK; + + sk->s_fd = socket(AF_NETLINK, SOCK_RAW | flags, protocol); + if (sk->s_fd < 0) { + errsv = errno; + NL_DBG(4, "nl_connect(%p): socket() failed with %d (%s)\n", sk, errsv, + nl_strerror_l(errsv)); + err = -nl_syserr2nlerr(errsv); + goto errout; + } + + err = nl_socket_set_buffer_size(sk, 0, 0); + if (err < 0) + goto errout; + + if (_nl_socket_is_local_port_unspecified (sk)) { + uint32_t port; + uint32_t used_ports[32] = { 0 }; + int ntries = 0; + + while (1) { + if (ntries++ > 5) { + /* try only a few times. We hit this only if many ports are already in + * use but allocated *outside* libnl/generate_local_port(). */ + _nl_socket_set_local_port_no_release (sk, 0); + break; + } + + port = _nl_socket_set_local_port_no_release(sk, 1); + if (port == 0) + break; + + err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local, + sizeof(sk->s_local)); + if (err == 0) { + try_bind = 0; + break; + } + + errsv = errno; + if (errsv == EADDRINUSE) { + NL_DBG(4, "nl_connect(%p): local port %u already in use. Retry.\n", sk, (unsigned) port); + _nl_socket_used_ports_set(used_ports, port); + } else { + NL_DBG(4, "nl_connect(%p): bind() for port %u failed with %d (%s)\n", + sk, (unsigned) port, errsv, nl_strerror_l(errsv)); + _nl_socket_used_ports_release_all(used_ports); + err = -nl_syserr2nlerr(errsv); + goto errout; + } + } + _nl_socket_used_ports_release_all(used_ports); + } + if (try_bind) { + err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local, + sizeof(sk->s_local)); + if (err != 0) { + errsv = errno; + NL_DBG(4, "nl_connect(%p): bind() failed with %d (%s)\n", + sk, errsv, nl_strerror_l(errsv)); + err = -nl_syserr2nlerr(errsv); + goto errout; + } + } + + addrlen = sizeof(local); + err = getsockname(sk->s_fd, (struct sockaddr *) &local, + &addrlen); + if (err < 0) { + NL_DBG(4, "nl_connect(%p): getsockname() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + err = -nl_syserr2nlerr(errno); + goto errout; + } + + if (addrlen != sizeof(local)) { + err = -NLE_NOADDR; + goto errout; + } + + if (local.nl_family != AF_NETLINK) { + err = -NLE_AF_NOSUPPORT; + goto errout; + } + + if (sk->s_local.nl_pid != local.nl_pid) { + /* The port id is different. That can happen if the port id was zero + * and kernel assigned a local port. */ + nl_socket_set_local_port (sk, local.nl_pid); + } + sk->s_local = local; + sk->s_proto = protocol; + + return 0; +errout: + if (sk->s_fd != -1) { + close(sk->s_fd); + sk->s_fd = -1; + } + + return err; +} + +/** + * Close Netlink socket + * @arg sk Netlink socket (required) + * + * Closes the Netlink socket using `close()`. + * + * @note The socket is closed automatically if a `struct nl_sock` object is + * freed using `nl_socket_free()`. + * + * @see nl_connect() + */ +void nl_close(struct nl_sock *sk) +{ + if (sk->s_fd >= 0) { + close(sk->s_fd); + sk->s_fd = -1; + } + + sk->s_proto = 0; +} + +/** @} */ + +/** + * @name Send + * @{ + */ + +/** + * Transmit raw data over Netlink socket. + * @arg sk Netlink socket (required) + * @arg buf Buffer carrying data to send (required) + * @arg size Size of buffer (required) + * + * Transmits "raw" data over the specified Netlink socket. Unlike the other + * transmit functions it does not modify the data in any way. It directly + * passes the buffer \c buf of \c size to sendto(). + * + * The message is addressed to the peer as specified in the socket by either + * the nl_socket_set_peer_port() or nl_socket_set_peer_groups() function. + * + * @note Because there is no indication on the message boundaries of the data + * being sent, the \c NL_CB_MSG_OUT callback handler will not be invoked + * for data that is being sent using this function. + * + * @see nl_socket_set_peer_port() + * @see nl_socket_set_peer_groups() + * @see nl_sendmsg() + * + * @return Number of bytes sent or a negative error code. + */ +int nl_sendto(struct nl_sock *sk, void *buf, size_t size) +{ + int ret; + + if (!buf) + return -NLE_INVAL; + + if (sk->s_fd < 0) + return -NLE_BAD_SOCK; + + ret = sendto(sk->s_fd, buf, size, 0, (struct sockaddr *) + &sk->s_peer, sizeof(sk->s_peer)); + if (ret < 0) { + NL_DBG(4, "nl_sendto(%p): sendto() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + return ret; +} + +/** + * Transmit Netlink message using sendmsg() + * @arg sk Netlink socket (required) + * @arg msg Netlink message to be sent (required) + * @arg hdr sendmsg() message header (required) + * + * Transmits the message specified in \c hdr over the Netlink socket using the + * sendmsg() system call. + * + * @attention + * The `msg` argument will *not* be used to derive the message payload that + * is being sent out. The `msg` argument is *only* passed on to the + * `NL_CB_MSG_OUT` callback. The caller is responsible to initialize the + * `hdr` struct properly and have it point to the message payload and + * socket address. + * + * @note + * This function uses `nlmsg_set_src()` to modify the `msg` argument prior to + * invoking the `NL_CB_MSG_OUT` callback to provide the local port number. + * + * @callback This function triggers the `NL_CB_MSG_OUT` callback. + * + * @attention + * Think twice before using this function. It provides a low level access to + * the Netlink socket. Among other limitations, it does not add credentials + * even if enabled or respect the destination address specified in the `msg` + * object. + * + * @see nl_socket_set_local_port() + * @see nl_send_auto() + * @see nl_send_iovec() + * + * @return Number of bytes sent on success or a negative error code. + * + * @lowlevel + */ +int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr) +{ + struct nl_cb *cb; + int ret; + + if (sk->s_fd < 0) + return -NLE_BAD_SOCK; + + nlmsg_set_src(msg, &sk->s_local); + + cb = sk->s_cb; + if (cb->cb_set[NL_CB_MSG_OUT]) + if ((ret = nl_cb_call(cb, NL_CB_MSG_OUT, msg)) != NL_OK) + return ret; + + ret = sendmsg(sk->s_fd, hdr, 0); + if (ret < 0) { + NL_DBG(4, "nl_sendmsg(%p): sendmsg() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + NL_DBG(4, "sent %d bytes\n", ret); + return ret; +} + + +/** + * Transmit Netlink message (taking IO vector) + * @arg sk Netlink socket (required) + * @arg msg Netlink message to be sent (required) + * @arg iov IO vector to be sent (required) + * @arg iovlen Number of struct iovec to be sent (required) + * + * This function is identical to nl_send() except that instead of taking a + * `struct nl_msg` object it takes an IO vector. Please see the description + * of `nl_send()`. + * + * @callback This function triggers the `NL_CB_MSG_OUT` callback. + * + * @see nl_send() + * + * @return Number of bytes sent on success or a negative error code. + * + * @lowlevel + */ +int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen) +{ + struct sockaddr_nl *dst; + struct ucred *creds; + struct msghdr hdr = { + .msg_name = (void *) &sk->s_peer, + .msg_namelen = sizeof(struct sockaddr_nl), + .msg_iov = iov, + .msg_iovlen = iovlen, + }; + char buf[CMSG_SPACE(sizeof(struct ucred))]; + + /* Overwrite destination if specified in the message itself, defaults + * to the peer address of the socket. + */ + dst = nlmsg_get_dst(msg); + if (dst->nl_family == AF_NETLINK) + hdr.msg_name = dst; + + /* Add credentials if present. */ + creds = nlmsg_get_creds(msg); + if (creds != NULL) { + struct cmsghdr *cmsg; + + hdr.msg_control = buf; + hdr.msg_controllen = sizeof(buf); + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + memcpy(CMSG_DATA(cmsg), creds, sizeof(struct ucred)); + } + + return nl_sendmsg(sk, msg, &hdr); +} + +/** + * Transmit Netlink message + * @arg sk Netlink socket (required) + * @arg msg Netlink message (required) + * + * Transmits the Netlink message `msg` over the Netlink socket using the + * `sendmsg()` system call. This function is based on `nl_send_iovec()` but + * takes care of initializing a `struct iovec` based on the `msg` object. + * + * The message is addressed to the peer as specified in the socket by either + * the nl_socket_set_peer_port() or nl_socket_set_peer_groups() function. + * The peer address can be overwritten by specifying an address in the `msg` + * object using nlmsg_set_dst(). + * + * If present in the `msg`, credentials set by the nlmsg_set_creds() function + * are added to the control buffer of the message. + * + * @par Overwriting Capability: + * Calls to this function can be overwritten by providing an alternative using + * the nl_cb_overwrite_send() function. + * + * @callback This function triggers the `NL_CB_MSG_OUT` callback. + * + * @attention + * Unlike `nl_send_auto()`, this function does *not* finalize the message in + * terms of automatically adding needed flags or filling out port numbers. + * + * @see nl_send_auto() + * @see nl_send_iovec() + * @see nl_socket_set_peer_port() + * @see nl_socket_set_peer_groups() + * @see nlmsg_set_dst() + * @see nlmsg_set_creds() + * @see nl_cb_overwrite_send() + * + * @return Number of bytes sent on success or a negative error code. +*/ +int nl_send(struct nl_sock *sk, struct nl_msg *msg) +{ + struct nl_cb *cb = sk->s_cb; + + if (cb->cb_send_ow) + return cb->cb_send_ow(sk, msg); + else { + struct iovec iov = { + .iov_base = (void *) nlmsg_hdr(msg), + .iov_len = nlmsg_hdr(msg)->nlmsg_len, + }; + + return nl_send_iovec(sk, msg, &iov, 1); + } +} + +/** + * Finalize Netlink message + * @arg sk Netlink socket (required) + * @arg msg Netlink message (required) + * + * This function finalizes a Netlink message by completing the message with + * desirable flags and values depending on the socket configuration. + * + * - If not yet filled out, the source address of the message (`nlmsg_pid`) + * will be set to the local port number of the socket. + * - If not yet specified, the next available sequence number is assigned + * to the message (`nlmsg_seq`). + * - If not yet specified, the protocol field of the message will be set to + * the protocol field of the socket. + * - The `NLM_F_REQUEST` Netlink message flag will be set. + * - The `NLM_F_ACK` flag will be set if Auto-ACK mode is enabled on the + * socket. + */ +void nl_complete_msg(struct nl_sock *sk, struct nl_msg *msg) +{ + struct nlmsghdr *nlh; + + nlh = nlmsg_hdr(msg); + if (nlh->nlmsg_pid == NL_AUTO_PORT) + nlh->nlmsg_pid = nl_socket_get_local_port(sk); + + if (nlh->nlmsg_seq == NL_AUTO_SEQ) + nlh->nlmsg_seq = nl_socket_use_seq(sk); + + if (msg->nm_protocol == -1) + msg->nm_protocol = sk->s_proto; + + nlh->nlmsg_flags |= NLM_F_REQUEST; + + if (!(sk->s_flags & NL_NO_AUTO_ACK)) + nlh->nlmsg_flags |= NLM_F_ACK; +} + +/** + * Finalize and transmit Netlink message + * @arg sk Netlink socket (required) + * @arg msg Netlink message (required) + * + * Finalizes the message by passing it to `nl_complete_msg()` and transmits it + * by passing it to `nl_send()`. + * + * @callback This function triggers the `NL_CB_MSG_OUT` callback. + * + * @see nl_complete_msg() + * @see nl_send() + * + * @return Number of bytes sent or a negative error code. + */ +int nl_send_auto(struct nl_sock *sk, struct nl_msg *msg) +{ + nl_complete_msg(sk, msg); + + return nl_send(sk, msg); +} + +/** + * Finalize and transmit Netlink message and wait for ACK or error message + * @arg sk Netlink socket (required) + * @arg msg Netlink message (required) + * + * Passes the `msg` to `nl_send_auto()` to finalize and transmit it. Frees the + * message and waits (sleeps) for the ACK or error message to be received. + * + * @attention + * Disabling Auto-ACK (nl_socket_disable_auto_ack()) will cause this function + * to return immediately after transmitting the message. However, the peer may + * still be returning an error message in response to the request. It is the + * responsibility of the caller to handle such messages. + * + * @callback This function triggers the `NL_CB_MSG_OUT` callback. + * + * @attention + * This function frees the `msg` object after transmitting it by calling + * `nlmsg_free()`. + * + * @see nl_send_auto(). + * @see nl_wait_for_ack() + * + * @return 0 on success or a negative error code. + */ +int nl_send_sync(struct nl_sock *sk, struct nl_msg *msg) +{ + int err; + + err = nl_send_auto(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** + * Construct and transmit a Netlink message + * @arg sk Netlink socket (required) + * @arg type Netlink message type (required) + * @arg flags Netlink message flags (optional) + * @arg buf Data buffer (optional) + * @arg size Size of data buffer (optional) + * + * Allocates a new Netlink message based on `type` and `flags`. If `buf` + * points to payload of length `size` that payload will be appended to the + * message. + * + * Sends out the message using `nl_send_auto()` and frees the message + * afterwards. + * + * @see nl_send_auto() + * + * @return Number of characters sent on success or a negative error code. + * @retval -NLE_NOMEM Unable to allocate Netlink message + */ +int nl_send_simple(struct nl_sock *sk, int type, int flags, void *buf, + size_t size) +{ + int err; + struct nl_msg *msg; + + msg = nlmsg_alloc_simple(type, flags); + if (!msg) + return -NLE_NOMEM; + + if (buf && size) { + err = nlmsg_append(msg, buf, size, NLMSG_ALIGNTO); + if (err < 0) + goto errout; + } + + err = nl_send_auto(sk, msg); +errout: + nlmsg_free(msg); + + return err; +} + +/** @} */ + +/** + * @name Receive + * @{ + */ + +/** + * Receive data from netlink socket + * @arg sk Netlink socket (required) + * @arg nla Netlink socket structure to hold address of peer (required) + * @arg buf Destination pointer for message content (required) + * @arg creds Destination pointer for credentials (optional) + * + * Receives data from a connected netlink socket using recvmsg() and returns + * the number of bytes read. The read data is stored in a newly allocated + * buffer that is assigned to \c *buf. The peer's netlink address will be + * stored in \c *nla. + * + * This function blocks until data is available to be read unless the socket + * has been put into non-blocking mode using nl_socket_set_nonblocking() in + * which case this function will return immediately with a return value of + * -NLA_AGAIN (versions before 3.2.22 returned instead 0, in which case you + * should check first clear errno and then check for errno EAGAIN). + * + * The buffer size used when reading from the netlink socket and thus limiting + * the maximum size of a netlink message that can be read defaults to the size + * of a memory page (getpagesize()). The buffer size can be modified on a per + * socket level using the function nl_socket_set_msg_buf_size(). + * + * If message peeking is enabled using nl_socket_enable_msg_peek() the size of + * the message to be read will be determined using the MSG_PEEK flag prior to + * performing the actual read. This leads to an additional recvmsg() call for + * every read operation which has performance implications and is not + * recommended for high throughput protocols. + * + * An eventual interruption of the recvmsg() system call is automatically + * handled by retrying the operation. + * + * If receiving of credentials has been enabled using the function + * nl_socket_set_passcred(), this function will allocate a new struct ucred + * filled with the received credentials and assign it to \c *creds. The caller + * is responsible for freeing the buffer. + * + * @note The caller is responsible to free the returned data buffer and if + * enabled, the credentials buffer. + * + * @see nl_socket_set_nonblocking() + * @see nl_socket_set_msg_buf_size() + * @see nl_socket_enable_msg_peek() + * @see nl_socket_set_passcred() + * + * @return Number of bytes read, 0 on EOF, 0 on no data event (non-blocking + * mode), or a negative error code. + */ +int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla, + unsigned char **buf, struct ucred **creds) +{ + ssize_t n; + int flags = 0; + static int page_size = 0; /* GLOBAL! */ + struct iovec iov; + struct msghdr msg = { + .msg_name = (void *) nla, + .msg_namelen = sizeof(struct sockaddr_nl), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + struct ucred* tmpcreds = NULL; + int retval = 0; + + if (!buf || !nla) + return -NLE_INVAL; + + if ( (sk->s_flags & NL_MSG_PEEK) + || (!(sk->s_flags & NL_MSG_PEEK_EXPLICIT) && sk->s_bufsize == 0)) + flags |= MSG_PEEK | MSG_TRUNC; + + if (page_size == 0) + page_size = getpagesize() * 4; + + iov.iov_len = sk->s_bufsize ? sk->s_bufsize : ((size_t)page_size); + iov.iov_base = malloc(iov.iov_len); + + if (!iov.iov_base) { + retval = -NLE_NOMEM; + goto abort; + } + + if (creds && (sk->s_flags & NL_SOCK_PASSCRED)) { + msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred)); + msg.msg_control = malloc(msg.msg_controllen); + if (!msg.msg_control) { + retval = -NLE_NOMEM; + goto abort; + } + } +retry: + + n = recvmsg(sk->s_fd, &msg, flags); + if (!n) { + retval = 0; + goto abort; + } + if (n < 0) { + if (errno == EINTR) { + NL_DBG(3, "recvmsg() returned EINTR, retrying\n"); + goto retry; + } + + NL_DBG(4, "recvmsg(%p): nl_recv() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + retval = -nl_syserr2nlerr(errno); + goto abort; + } + + if (msg.msg_flags & MSG_CTRUNC) { + void *tmp; + + if (msg.msg_controllen == 0) { + retval = -NLE_MSG_TRUNC; + NL_DBG(4, "recvmsg(%p): Received unexpected control data", sk); + goto abort; + } + + msg.msg_controllen *= 2; + tmp = realloc(msg.msg_control, msg.msg_controllen); + if (!tmp) { + retval = -NLE_NOMEM; + goto abort; + } + msg.msg_control = tmp; + goto retry; + } + + if (iov.iov_len < ((size_t)n) || (msg.msg_flags & MSG_TRUNC)) { + void *tmp; + + /* respond with error to an incomplete message */ + if (flags == 0) { + retval = -NLE_MSG_TRUNC; + goto abort; + } + + /* Provided buffer is not long enough, enlarge it + * to size of n (which should be total length of the message) + * and try again. */ + iov.iov_len = n; + tmp = realloc(iov.iov_base, iov.iov_len); + if (!tmp) { + retval = -NLE_NOMEM; + goto abort; + } + iov.iov_base = tmp; + flags = 0; + goto retry; + } + + if (flags != 0) { + /* Buffer is big enough, do the actual reading */ + flags = 0; + goto retry; + } + + if (msg.msg_namelen != sizeof(struct sockaddr_nl)) { + retval = -NLE_NOADDR; + goto abort; + } + + if (creds && (sk->s_flags & NL_SOCK_PASSCRED)) { + struct cmsghdr *cmsg; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level != SOL_SOCKET) + continue; + if (cmsg->cmsg_type != SCM_CREDENTIALS) + continue; + tmpcreds = malloc(sizeof(*tmpcreds)); + if (!tmpcreds) { + retval = -NLE_NOMEM; + goto abort; + } + memcpy(tmpcreds, CMSG_DATA(cmsg), sizeof(*tmpcreds)); + break; + } + } + + retval = n; +abort: + free(msg.msg_control); + + if (retval <= 0) { + free(iov.iov_base); + iov.iov_base = NULL; + free(tmpcreds); + tmpcreds = NULL; + } else + *buf = iov.iov_base; + + if (creds) + *creds = tmpcreds; + + return retval; +} + +/** @cond SKIP */ +#define NL_CB_CALL(cb, type, msg) \ +do { \ + err = nl_cb_call(cb, type, msg); \ + switch (err) { \ + case NL_OK: \ + err = 0; \ + break; \ + case NL_SKIP: \ + goto skip; \ + case NL_STOP: \ + goto stop; \ + default: \ + goto out; \ + } \ +} while (0) +/** @endcond */ + +static int recvmsgs(struct nl_sock *sk, struct nl_cb *cb) +{ + int n, err = 0, multipart = 0, interrupted = 0, nrecv = 0; + unsigned char *buf = NULL; + struct nlmsghdr *hdr; + + /* + nla is passed on to not only to nl_recv() but may also be passed + to a function pointer provided by the caller which may or may not + initialize the variable. Thomas Graf. + */ + struct sockaddr_nl nla = {0}; + struct nl_msg *msg = NULL; + struct ucred *creds = NULL; + +continue_reading: + NL_DBG(3, "Attempting to read from %p\n", sk); + if (cb->cb_recv_ow) + n = cb->cb_recv_ow(sk, &nla, &buf, &creds); + else + n = nl_recv(sk, &nla, &buf, &creds); + + if (n <= 0) + return n; + + NL_DBG(3, "recvmsgs(%p): Read %d bytes\n", sk, n); + + hdr = (struct nlmsghdr *) buf; + while (nlmsg_ok(hdr, n)) { + NL_DBG(3, "recvmsgs(%p): Processing valid message...\n", sk); + + nlmsg_free(msg); + msg = nlmsg_convert(hdr); + if (!msg) { + err = -NLE_NOMEM; + goto out; + } + + nlmsg_set_proto(msg, sk->s_proto); + nlmsg_set_src(msg, &nla); + if (creds) + nlmsg_set_creds(msg, creds); + + nrecv++; + + /* Raw callback is the first, it gives the most control + * to the user and he can do his very own parsing. */ + if (cb->cb_set[NL_CB_MSG_IN]) + NL_CB_CALL(cb, NL_CB_MSG_IN, msg); + + /* Sequence number checking. The check may be done by + * the user, otherwise a very simple check is applied + * enforcing strict ordering */ + if (cb->cb_set[NL_CB_SEQ_CHECK]) { + NL_CB_CALL(cb, NL_CB_SEQ_CHECK, msg); + + /* Only do sequence checking if auto-ack mode is enabled */ + } else if (!(sk->s_flags & NL_NO_AUTO_ACK)) { + if (hdr->nlmsg_seq != sk->s_seq_expect) { + if (cb->cb_set[NL_CB_INVALID]) + NL_CB_CALL(cb, NL_CB_INVALID, msg); + else { + err = -NLE_SEQ_MISMATCH; + goto out; + } + } + } + + if (hdr->nlmsg_type == NLMSG_DONE || + hdr->nlmsg_type == NLMSG_ERROR || + hdr->nlmsg_type == NLMSG_NOOP || + hdr->nlmsg_type == NLMSG_OVERRUN) { + /* We can't check for !NLM_F_MULTI since some netlink + * users in the kernel are broken. */ + sk->s_seq_expect++; + NL_DBG(3, "recvmsgs(%p): Increased expected " \ + "sequence number to %d\n", + sk, sk->s_seq_expect); + } + + if (hdr->nlmsg_flags & NLM_F_MULTI) + multipart = 1; + + if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) { + if (cb->cb_set[NL_CB_DUMP_INTR]) + NL_CB_CALL(cb, NL_CB_DUMP_INTR, msg); + else { + /* + * We have to continue reading to clear + * all messages until a NLMSG_DONE is + * received and report the inconsistency. + */ + interrupted = 1; + } + } + + /* Other side wishes to see an ack for this message */ + if (hdr->nlmsg_flags & NLM_F_ACK) { + if (cb->cb_set[NL_CB_SEND_ACK]) + NL_CB_CALL(cb, NL_CB_SEND_ACK, msg); + else { + /* FIXME: implement */ + } + } + + /* messages terminates a multipart message, this is + * usually the end of a message and therefore we slip + * out of the loop by default. the user may overrule + * this action by skipping this packet. */ + if (hdr->nlmsg_type == NLMSG_DONE) { + multipart = 0; + if (cb->cb_set[NL_CB_FINISH]) + NL_CB_CALL(cb, NL_CB_FINISH, msg); + } + + /* Message to be ignored, the default action is to + * skip this message if no callback is specified. The + * user may overrule this action by returning + * NL_PROCEED. */ + else if (hdr->nlmsg_type == NLMSG_NOOP) { + if (cb->cb_set[NL_CB_SKIPPED]) + NL_CB_CALL(cb, NL_CB_SKIPPED, msg); + else + goto skip; + } + + /* Data got lost, report back to user. The default action is to + * quit parsing. The user may overrule this action by retuning + * NL_SKIP or NL_PROCEED (dangerous) */ + else if (hdr->nlmsg_type == NLMSG_OVERRUN) { + if (cb->cb_set[NL_CB_OVERRUN]) + NL_CB_CALL(cb, NL_CB_OVERRUN, msg); + else { + err = -NLE_MSG_OVERFLOW; + goto out; + } + } + + /* Message carries a nlmsgerr */ + else if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *e = nlmsg_data(hdr); + + if (hdr->nlmsg_len < + ((unsigned)nlmsg_size(sizeof(*e)))) { + /* Truncated error message, the default action + * is to stop parsing. The user may overrule + * this action by returning NL_SKIP or + * NL_PROCEED (dangerous) */ + if (cb->cb_set[NL_CB_INVALID]) + NL_CB_CALL(cb, NL_CB_INVALID, msg); + else { + err = -NLE_MSG_TRUNC; + goto out; + } + } else if (e->error) { + NL_DBG(4, "recvmsgs(%p): RTNETLINK responded with %d (%s)\n", + sk, -e->error, nl_strerror_l(-e->error)); + + /* Error message reported back from kernel. */ + if (cb->cb_err) { + err = cb->cb_err(&nla, e, + cb->cb_err_arg); + if (err < 0) + goto out; + else if (err == NL_SKIP) + goto skip; + else if (err == NL_STOP) { + err = -nl_syserr2nlerr(e->error); + goto out; + } + } else { + err = -nl_syserr2nlerr(e->error); + goto out; + } + } else if (cb->cb_set[NL_CB_ACK]) + NL_CB_CALL(cb, NL_CB_ACK, msg); + } else { + /* Valid message (not checking for MULTIPART bit to + * get along with broken kernels. NL_SKIP has no + * effect on this. */ + if (cb->cb_set[NL_CB_VALID]) + NL_CB_CALL(cb, NL_CB_VALID, msg); + } +skip: + err = 0; + hdr = nlmsg_next(hdr, &n); + } + + nlmsg_free(msg); + free(buf); + free(creds); + buf = NULL; + msg = NULL; + creds = NULL; + + if (multipart) { + /* Multipart message not yet complete, continue reading */ + goto continue_reading; + } +stop: + err = 0; +out: + nlmsg_free(msg); + free(buf); + free(creds); + + if (interrupted) + err = -NLE_DUMP_INTR; + + if (!err) + err = nrecv; + + return err; +} + +/** + * Receive a set of messages from a netlink socket and report parsed messages + * @arg sk Netlink socket. + * @arg cb set of callbacks to control behaviour. + * + * This function is identical to nl_recvmsgs() to the point that it will + * return the number of parsed messages instead of 0 on success. + * + * @see nl_recvmsgs() + * + * @return Number of received messages or a negative error code from nl_recv(). + */ +int nl_recvmsgs_report(struct nl_sock *sk, struct nl_cb *cb) +{ + if (cb->cb_recvmsgs_ow) + return cb->cb_recvmsgs_ow(sk, cb); + else + return recvmsgs(sk, cb); +} + +/** + * Receive a set of messages from a netlink socket. + * @arg sk Netlink socket. + * @arg cb set of callbacks to control behaviour. + * + * Repeatedly calls nl_recv() or the respective replacement if provided + * by the application (see nl_cb_overwrite_recv()) and parses the + * received data as netlink messages. Stops reading if one of the + * callbacks returns NL_STOP or nl_recv returns either 0 or a negative error code. + * + * A non-blocking sockets causes the function to return immediately if + * no data is available. + * + * @see nl_recvmsgs_report() + * + * @return 0 on success or a negative error code from nl_recv(). + */ +int nl_recvmsgs(struct nl_sock *sk, struct nl_cb *cb) +{ + int err; + + if ((err = nl_recvmsgs_report(sk, cb)) > 0) + err = 0; + + return err; +} + +/** + * Receive a set of message from a netlink socket using handlers in nl_sock. + * @arg sk Netlink socket. + * + * Calls nl_recvmsgs() with the handlers configured in the netlink socket. + */ +int nl_recvmsgs_default(struct nl_sock *sk) +{ + return nl_recvmsgs(sk, sk->s_cb); + +} + +static int ack_wait_handler(struct nl_msg *msg, void *arg) +{ + return NL_STOP; +} + +/** + * Wait for ACK. + * @arg sk Netlink socket. + * @pre The netlink socket must be in blocking state. + * + * Waits until an ACK is received for the latest not yet acknowledged + * netlink message. + */ +int nl_wait_for_ack(struct nl_sock *sk) +{ + int err; + struct nl_cb *cb; + + cb = nl_cb_clone(sk->s_cb); + if (cb == NULL) + return -NLE_NOMEM; + + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_wait_handler, NULL); + err = nl_recvmsgs(sk, cb); + nl_cb_put(cb); + + return err; +} + +/** @cond SKIP */ +struct pickup_param +{ + int (*parser)(struct nl_cache_ops *, struct sockaddr_nl *, + struct nlmsghdr *, struct nl_parser_param *); + struct nl_object *result; + int *syserror; +}; + +static int __store_answer(struct nl_object *obj, struct nl_parser_param *p) +{ + struct pickup_param *pp = p->pp_arg; + /* + * the parser will put() the object at the end, expecting the cache + * to take the reference. + */ + nl_object_get(obj); + pp->result = obj; + + return 0; +} + +static int __pickup_answer(struct nl_msg *msg, void *arg) +{ + struct pickup_param *pp = arg; + struct nl_parser_param parse_arg = { + .pp_cb = __store_answer, + .pp_arg = pp, + }; + + return pp->parser(NULL, &msg->nm_src, msg->nm_nlh, &parse_arg); +} + +static int __pickup_answer_syserr(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg) +{ + *(((struct pickup_param *) arg)->syserror) = nlerr->error; + + return -nl_syserr2nlerr(nlerr->error); +} + +/** @endcond */ + +/** + * Pickup netlink answer, parse is and return object + * @arg sk Netlink socket + * @arg parser Parser function to parse answer + * @arg result Result pointer to return parsed object + * + * @return 0 on success or a negative error code. + */ +int nl_pickup(struct nl_sock *sk, + int (*parser)(struct nl_cache_ops *, struct sockaddr_nl *, + struct nlmsghdr *, struct nl_parser_param *), + struct nl_object **result) +{ + return nl_pickup_keep_syserr(sk, parser, result, NULL); +} + +/** + * Pickup netlink answer, parse is and return object with preserving system error + * @arg sk Netlink socket + * @arg parser Parser function to parse answer + * @arg result Result pointer to return parsed object + * @arg syserr Result pointer for the system error in case of failure + * + * @return 0 on success or a negative error code. + */ +int nl_pickup_keep_syserr(struct nl_sock *sk, + int (*parser)(struct nl_cache_ops *, struct sockaddr_nl *, + struct nlmsghdr *, struct nl_parser_param *), + struct nl_object **result, + int *syserror) +{ + struct nl_cb *cb; + int err; + struct pickup_param pp = { + .parser = parser, + }; + + cb = nl_cb_clone(sk->s_cb); + if (cb == NULL) + return -NLE_NOMEM; + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, __pickup_answer, &pp); + if (syserror) { + *syserror = 0; + pp.syserror = syserror; + nl_cb_err(cb, NL_CB_CUSTOM, __pickup_answer_syserr, &pp); + } + + err = nl_recvmsgs(sk, cb); + if (err < 0) + goto errout; + + *result = pp.result; +errout: + nl_cb_put(cb); + + return err; +} + +/** @} */ + +/** + * @name Deprecated + * @{ + */ + +/** + * @deprecated Please use nl_complete_msg() + */ +void nl_auto_complete(struct nl_sock *sk, struct nl_msg *msg) +{ + nl_complete_msg(sk, msg); +} + +/** + * @deprecated Please use nl_send_auto() + */ +int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg) +{ + return nl_send_auto(sk, msg); +} + + +/** @} */ + +/** @} */ + +/** @} */ diff --git a/libnl/lib/object.c b/libnl/lib/object.c new file mode 100644 index 0000000..9b147d6 --- /dev/null +++ b/libnl/lib/object.c @@ -0,0 +1,579 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup core_types + * @defgroup object Object (Cacheable) + * + * Generic object data type, for inheritance purposes to implement cacheable + * data types. + * + * Related sections in the development guide: + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#include "nl-default.h" + +#include +#include +#include +#include + +#include "nl-core.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/object-api.h" +#include "nl-priv-dynamic-core/cache-api.h" +#include "nl-aux-core/nl-core.h" + +static inline struct nl_object_ops *obj_ops(struct nl_object *obj) +{ + if (!obj->ce_ops) + BUG(); + + return obj->ce_ops; +} + +/** + * @name Object Creation/Deletion + * @{ + */ + +/** + * Allocate a new object of kind specified by the operations handle + * @arg ops cache operations handle + * @return The new object or NULL + */ +struct nl_object *nl_object_alloc(struct nl_object_ops *ops) +{ + struct nl_object *new; + + if (ops->oo_size < sizeof(*new)) + BUG(); + + new = calloc(1, ops->oo_size); + if (!new) + return NULL; + + new->ce_refcnt = 1; + nl_init_list_head(&new->ce_list); + + new->ce_ops = ops; + if (ops->oo_constructor) + ops->oo_constructor(new); + + NL_DBG(4, "Allocated new object %p\n", new); + + return new; +} + +/** + * Allocate new object of kind specified by the name + * @arg kind name of object type + * @arg result Result pointer + * + * @return 0 on success or a negative error code. + */ +int nl_object_alloc_name(const char *kind, struct nl_object **result) +{ + struct nl_cache_ops *ops; + + ops = nl_cache_ops_lookup_safe(kind); + if (!ops) + return -NLE_OPNOTSUPP; + + *result = nl_object_alloc(ops->co_obj_ops); + nl_cache_ops_put(ops); + if (!*result) + return -NLE_NOMEM; + + return 0; +} + +struct nl_derived_object { + NLHDR_COMMON + char data; +}; + +/** + * Allocate a new object and copy all data from an existing object + * @arg obj object to inherite data from + * @return The new object or NULL. + */ +struct nl_object *nl_object_clone(struct nl_object *obj) +{ + struct nl_object *new; + struct nl_object_ops *ops; + int doff = offsetof(struct nl_derived_object, data); + int size; + + if (!obj) + return NULL; + + ops = obj_ops(obj); + new = nl_object_alloc(ops); + if (!new) + return NULL; + + size = ops->oo_size - doff; + if (size < 0) + BUG(); + + new->ce_ops = obj->ce_ops; + new->ce_msgtype = obj->ce_msgtype; + new->ce_mask = obj->ce_mask; + + if (size) + memcpy((char *)new + doff, (char *)obj + doff, size); + + /* Note that the base implementation already initializes @new via memcpy(). + * That means, simple fields don't need to be handled via oo_clone(). + * However, this is only a shallow-copy, so oo_clone() MUST fix all + * pointer values accordingly. */ + + if (ops->oo_clone) { + if (ops->oo_clone(new, obj) < 0) { + nl_object_free(new); + return NULL; + } + } else if (size && ops->oo_free_data) + BUG(); + + return new; +} + +/** + * Merge a cacheable object + * @arg dst object to be merged into + * @arg src new object to be merged into dst + * + * @return 0 or a negative error code. + */ +int nl_object_update(struct nl_object *dst, struct nl_object *src) +{ + struct nl_object_ops *ops = obj_ops(dst); + + if (ops->oo_update) + return ops->oo_update(dst, src); + + return -NLE_OPNOTSUPP; +} + +/** + * Free a cacheable object + * @arg obj object to free + * + * @return 0 or a negative error code. + */ +void nl_object_free(struct nl_object *obj) +{ + struct nl_object_ops *ops; + + if (!obj) + return; + + ops = obj_ops(obj); + + if (obj->ce_refcnt > 0) + NL_DBG(1, "Warning: Freeing object in use...\n"); + + if (obj->ce_cache) + nl_cache_remove(obj); + + if (ops->oo_free_data) + ops->oo_free_data(obj); + + NL_DBG(4, "Freed object %p\n", obj); + + free(obj); +} + +/** @} */ + +/** + * @name Reference Management + * @{ + */ + +/** + * Acquire a reference on a object + * @arg obj object to acquire reference from + */ +void nl_object_get(struct nl_object *obj) +{ + obj->ce_refcnt++; + NL_DBG(4, "New reference to object %p, total %d\n", + obj, obj->ce_refcnt); +} + +/** + * Release a reference from an object + * @arg obj object to release reference from + */ +void nl_object_put(struct nl_object *obj) +{ + if (!obj) + return; + + obj->ce_refcnt--; + NL_DBG(4, "Returned object reference %p, %d remaining\n", + obj, obj->ce_refcnt); + + if (obj->ce_refcnt < 0) + BUG(); + + if (obj->ce_refcnt <= 0) + nl_object_free(obj); +} + +/** + * Check whether this object is used by multiple users + * @arg obj object to check + * @return true or false + */ +int nl_object_shared(struct nl_object *obj) +{ + return obj->ce_refcnt > 1; +} + +/** @} */ + +/** + * @name Marks + * @{ + */ + +/** + * Add mark to object + * @arg obj Object to mark + */ +void nl_object_mark(struct nl_object *obj) +{ + obj->ce_flags |= NL_OBJ_MARK; +} + +/** + * Remove mark from object + * @arg obj Object to unmark + */ +void nl_object_unmark(struct nl_object *obj) +{ + obj->ce_flags &= ~NL_OBJ_MARK; +} + +/** + * Return true if object is marked + * @arg obj Object to check + * @return true if object is marked, otherwise false + */ +int nl_object_is_marked(struct nl_object *obj) +{ + return (obj->ce_flags & NL_OBJ_MARK); +} + +/** @} */ + +/** + * @name Utillities + * @{ + */ + +/** + * Dump this object according to the specified parameters + * @arg obj object to dump + * @arg params dumping parameters + */ +void nl_object_dump(struct nl_object *obj, struct nl_dump_params *params) +{ + if (params->dp_buf) + memset(params->dp_buf, 0, params->dp_buflen); + + dump_from_ops(obj, params); +} + +void nl_object_dump_buf(struct nl_object *obj, char *buf, size_t len) +{ + struct nl_dump_params dp = { + .dp_buf = buf, + .dp_buflen = len, + }; + + nl_object_dump(obj, &dp); +} + +/** + * Check if the identifiers of two objects are identical + * @arg a an object + * @arg b another object of same type + * + * @return true if both objects have equal identifiers, otherwise false. + */ +int nl_object_identical(struct nl_object *a, struct nl_object *b) +{ + struct nl_object_ops *ops; + uint64_t req_attrs_a; + uint64_t req_attrs_b; + + if (a == b) + return 1; + + /* Both objects must be of same type */ + ops = obj_ops(a); + if (ops != obj_ops(b)) + return 0; + + /* Can't judge unless we can compare */ + if (ops->oo_compare == NULL) + return 0; + + if (ops->oo_id_attrs_get) { + req_attrs_a = ops->oo_id_attrs_get(a); + req_attrs_b = ops->oo_id_attrs_get(b); + } else if (ops->oo_id_attrs) { + req_attrs_a = ops->oo_id_attrs; + req_attrs_b = req_attrs_a; + } else { + req_attrs_a = UINT64_MAX; + req_attrs_b = req_attrs_a; + } + + req_attrs_a &= a->ce_mask; + req_attrs_b &= b->ce_mask; + + /* Both objects must provide all required attributes to uniquely + * identify an object */ + if (req_attrs_a != req_attrs_b) + return 0; + + return !(ops->oo_compare(a, b, req_attrs_a, ID_COMPARISON)); +} + +/** + * Compute bitmask representing difference in attribute values + * @arg a an object + * @arg b another object of same type + * + * The bitmask returned is specific to an object type, each bit set represents + * an attribute which mismatches in either of the two objects. Unavailability + * of an attribute in one object and presence in the other is regarded a + * mismatch as well. + * + * @return Bitmask describing differences or 0 if they are completely identical. + */ +uint64_t nl_object_diff64(struct nl_object *a, struct nl_object *b) +{ + struct nl_object_ops *ops = obj_ops(a); + + if (ops != obj_ops(b) || ops->oo_compare == NULL) + return UINT64_MAX; + + return ops->oo_compare(a, b, UINT64_MAX, 0); +} + +/** + * Compute 32-bit bitmask representing difference in attribute values + * @arg a an object + * @arg b another object of same type + * + * The bitmask returned is specific to an object type, each bit set represents + * an attribute which mismatches in either of the two objects. Unavailability + * of an attribute in one object and presence in the other is regarded a + * mismatch as well. + * + * @return Bitmask describing differences or 0 if they are completely identical. + * 32nd bit indicates if higher bits from the 64-bit compare were + * different. + */ +uint32_t nl_object_diff(struct nl_object *a, struct nl_object *b) +{ + uint64_t diff; + + diff = nl_object_diff64(a, b); + + return (diff & ~((uint64_t) 0xFFFFFFFF)) + ? (uint32_t) diff | (((uint32_t ) 1u) << 31) + : (uint32_t) diff; +} + +/** + * Match a filter against an object + * @arg obj object to check + * @arg filter object of same type acting as filter + * + * @return 1 if the object matches the filter or 0 + * if no filter procedure is available or if the + * filter does not match. + */ +int nl_object_match_filter(struct nl_object *obj, struct nl_object *filter) +{ + struct nl_object_ops *ops = obj_ops(obj); + + if (ops != obj_ops(filter) || ops->oo_compare == NULL) + return 0; + + return !(ops->oo_compare(obj, filter, filter->ce_mask, + LOOSE_COMPARISON)); +} + +/** + * Convert bitmask of attributes to a character string + * @arg obj object of same type as attribute bitmask + * @arg attrs bitmask of attribute types + * @arg buf destination buffer + * @arg len length of destination buffer + * + * Converts the bitmask of attribute types into a list of attribute + * names separated by comas. + * + * @return destination buffer. + */ +char *nl_object_attrs2str(struct nl_object *obj, uint32_t attrs, + char *buf, size_t len) +{ + struct nl_object_ops *ops = obj_ops(obj); + + if (ops->oo_attrs2str != NULL) + return ops->oo_attrs2str(attrs, buf, len); + else { + memset(buf, 0, len); + return buf; + } +} + +/** + * Return list of attributes present in an object + * @arg obj an object + * @arg buf destination buffer + * @arg len length of destination buffer + * + * @return destination buffer. + */ +char *nl_object_attr_list(struct nl_object *obj, char *buf, size_t len) +{ + return nl_object_attrs2str(obj, obj->ce_mask, buf, len); +} + +/** + * Generate object hash key + * @arg obj the object + * @arg hashkey destination buffer to be used for key stream + * @arg hashtbl_sz hash table size + * + * @return hash key in destination buffer + */ +void nl_object_keygen(struct nl_object *obj, uint32_t *hashkey, + uint32_t hashtbl_sz) +{ + struct nl_object_ops *ops = obj_ops(obj); + + if (ops->oo_keygen) + ops->oo_keygen(obj, hashkey, hashtbl_sz); + else + *hashkey = 0; + + return; +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +/** + * Return number of references held + * @arg obj object + * + * @return The number of references held to this object + */ +int nl_object_get_refcnt(struct nl_object *obj) +{ + return obj->ce_refcnt; +} + +/** + * Return cache the object is associated with + * @arg obj object + * + * @note The returned pointer is not protected with a reference counter, + * it is your responsibility. + * + * @return Pointer to cache or NULL if not associated with a cache. + */ +struct nl_cache *nl_object_get_cache(struct nl_object *obj) +{ + return obj->ce_cache; +} + +/** + * Return the object's type + * @arg obj object + * + * FIXME: link to list of object types + * + * @return Name of the object type + */ +const char *nl_object_get_type(const struct nl_object *obj) +{ + if (!obj->ce_ops) + BUG(); + + return obj->ce_ops->oo_name; +} + +/** + * Return the netlink message type the object was derived from + * @arg obj object + * + * @return Netlink message type or 0. + */ +int nl_object_get_msgtype(const struct nl_object *obj) +{ + return obj->ce_msgtype; +} + +/** + * Return object operations structure + * @arg obj object + * + * @return Pointer to the object operations structure + */ +struct nl_object_ops *nl_object_get_ops(const struct nl_object *obj) +{ + return obj->ce_ops; +} + +/** + * Return object id attribute mask + * @arg obj object + * + * @return object id attribute mask + */ +uint32_t nl_object_get_id_attrs(struct nl_object *obj) +{ + struct nl_object_ops *ops = obj_ops(obj); + uint32_t id_attrs; + + if (!ops) + return 0; + + if (ops->oo_id_attrs_get) + id_attrs = ops->oo_id_attrs_get(obj); + else + id_attrs = ops->oo_id_attrs; + + return id_attrs; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/route/act.c b/libnl/lib/route/act.c new file mode 100644 index 0000000..670810f --- /dev/null +++ b/libnl/lib/route/act.c @@ -0,0 +1,631 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +/** + * @ingroup tc + * @defgroup act Action + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include + +#include "nl-route.h" +#include "tc-api.h" +#include "nl-priv-dynamic-core/object-api.h" +#include "nl-priv-dynamic-core/cache-api.h" +#include "nl-aux-route/nl-route.h" + +static struct nl_object_ops act_obj_ops; +static struct nl_cache_ops rtnl_act_ops; + +struct rtnl_act * rtnl_act_next(struct rtnl_act *act) +{ + if (act == NULL) { + return NULL; + } + + return act->a_next; +} + +int rtnl_act_append(struct rtnl_act **head, struct rtnl_act *new) +{ + struct rtnl_act *p_act; + int count = 1; + + if (*head == NULL) { + *head = new; + return 0; + } + + p_act = *head; + while (p_act->a_next) { + ++count; + p_act = p_act->a_next; + } + + if (count > TCA_ACT_MAX_PRIO) + return -NLE_RANGE; + + p_act->a_next = new; + return 0; +} + +int rtnl_act_remove(struct rtnl_act **head, struct rtnl_act *act) +{ + struct rtnl_act *a, **ap; + + for (ap = head; (a = *ap) != NULL; ap = &a->a_next) + if (a == act) + break; + if (a) { + *ap = a->a_next; + a->a_next = NULL; + return 0; + } + + return -NLE_OBJ_NOTFOUND; +} + +static int rtnl_act_fill_one(struct nl_msg *msg, struct rtnl_act *act, int order) +{ + struct rtnl_tc *tc = TC_CAST(act); + struct rtnl_tc_ops *ops; + struct nlattr *nest; + int err = -NLE_NOMEM; + + nest = nla_nest_start(msg, order); + if (!nest) + goto nla_put_failure; + + if (tc->ce_mask & TCA_ATTR_KIND) + NLA_PUT_STRING(msg, TCA_ACT_KIND, tc->tc_kind); + + ops = rtnl_tc_get_ops(tc); + if (ops && (ops->to_msg_fill || ops->to_msg_fill_raw)) { + struct nlattr *opts; + void *data = rtnl_tc_data(tc); + + if (ops->to_msg_fill) { + if (!(opts = nla_nest_start(msg, TCA_ACT_OPTIONS))) + goto nla_put_failure; + + if ((err = ops->to_msg_fill(tc, data, msg)) < 0) + goto nla_put_failure; + + nla_nest_end(msg, opts); + } else if ((err = ops->to_msg_fill_raw(tc, data, msg)) < 0) + goto nla_put_failure; + } + nla_nest_end(msg, nest); + return 0; + +nla_put_failure: + return err; +} + +int rtnl_act_fill(struct nl_msg *msg, int attrtype, struct rtnl_act *act) +{ + struct rtnl_act *p_act = act; + struct nlattr *nest; + int err, order = 0; + + nest = nla_nest_start(msg, attrtype); + if (!nest) + return -NLE_MSGSIZE; + + while (p_act) { + err = rtnl_act_fill_one(msg, p_act, ++order); + if (err < 0) + return err; + p_act = p_act->a_next; + } + + nla_nest_end(msg, nest); + return 0; +} + +static int rtnl_act_msg_build(struct rtnl_act *act, int type, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + struct tcamsg tcahdr = { + .tca_family = AF_UNSPEC, + }; + int err = -NLE_MSGSIZE; + + msg = nlmsg_alloc_simple(type, flags); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &tcahdr, sizeof(tcahdr), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + err = rtnl_act_fill(msg, TCA_ACT_TAB, act); + if (err < 0) + goto nla_put_failure; + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return err; +} + +static int act_build(struct rtnl_act *act, int type, int flags, + struct nl_msg **result) +{ + int err; + + err = rtnl_act_msg_build(act, type, flags, result); + if (err < 0) + return err; + return 0; +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_act *rtnl_act_alloc(void) +{ + struct rtnl_tc *tc; + + tc = TC_CAST(nl_object_alloc(&act_obj_ops)); + if (tc) + tc->tc_type = RTNL_TC_TYPE_ACT; + + return (struct rtnl_act *) tc; +} + +void rtnl_act_get(struct rtnl_act *act) +{ + nl_object_get(OBJ_CAST(act)); +} + +void rtnl_act_put(struct rtnl_act *act) +{ + nl_object_put((struct nl_object *) act); +} + +/** @} */ + +/** + * @name Addition/Modification/Deletion + * @{ + */ + +/** + * Build a netlink message requesting the addition of an action + * @arg act Action to add + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_act_add() with + * the exception that it will not send the message but return it int the + * provided return pointer instead. + * + * @see rtnl_act_add() + * + * @return 0 on success or a negative error code. + */ +int rtnl_act_build_add_request(struct rtnl_act *act, int flags, + struct nl_msg **result) +{ + return act_build(act, RTM_NEWACTION, flags, result); +} + +/** + * Add/Update action + * @arg sk Netlink socket + * @arg act Action to add/update + * @arg flags Additional netlink message flags + * + * Builds a \c RTM_NEWACTION netlink message requesting the addition + * of a new action and sends the message to the kernel. The + * configuration of the action is derived from the attributes of + * the specified traffic class. + * + * The following flags may be specified: + * - \c NLM_F_CREATE: Create action if it does not exist, + * otherwise -NLE_OBJ_NOTFOUND is returned. + * - \c NLM_F_EXCL: Return -NLE_EXISTS if an action with + * matching handle exists already. + * + * Existing actions with matching handles will be updated, unless + * the flag \c NLM_F_EXCL is specified. If no matching action + * exists, it will be created if the flag \c NLM_F_CREATE is set, + * otherwise the error -NLE_OBJ_NOTFOUND is returned. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_act_add(struct nl_sock *sk, struct rtnl_act *act, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_act_build_add_request(act, flags, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build a netlink message to change action attributes + * @arg act Action to change + * @arg flags additional netlink message flags + * @arg result Pointer to store resulting message. + * + * Builds a new netlink message requesting a change of a neigh + * attributes. The netlink message header isn't fully equipped with + * all relevant fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. + * + * @return 0 on success or a negative error code. + */ +int rtnl_act_build_change_request(struct rtnl_act *act, int flags, + struct nl_msg **result) +{ + return act_build(act, RTM_NEWACTION, NLM_F_REPLACE | flags, result); +} + +/** + * Change an action + * @arg sk Netlink socket. + * @arg act action to change + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_act_build_change_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been processed. + * + * @return 0 on success or a negative error if an error occured. + */ +int rtnl_act_change(struct nl_sock *sk, struct rtnl_act *act, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_act_build_change_request(act, flags, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build netlink message requesting the deletion of an action + * @arg act Action to delete + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_act_delete() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_act_delete() + * + * @return 0 on success or a negative error code. + */ +int rtnl_act_build_delete_request(struct rtnl_act *act, int flags, + struct nl_msg **result) +{ + return act_build(act, RTM_DELACTION, flags, result); +} + +/** + * Delete action + * @arg sk Netlink socket + * @arg act Action to delete + * @arg flags Additional netlink message flags + * + * Builds a \c RTM_DELACTION netlink message requesting the deletion + * of an action and sends the message to the kernel. + * + * The message is constructed out of the following attributes: + * - \c ifindex (required) + * - \c prio (required) + * - \c protocol (required) + * - \c handle (required) + * - \c parent (optional, if not specified parent equals root-qdisc) + * - \c kind (optional, must match if provided) + * + * All other action attributes including all class type specific + * attributes are ignored. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_act_delete(struct nl_sock *sk, struct rtnl_act *act, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_act_build_delete_request(act, flags, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** @} */ + +static void act_dump_line(struct rtnl_tc *tc, struct nl_dump_params *p) +{ +} + +void rtnl_act_put_all(struct rtnl_act **head) +{ + struct rtnl_act *curr, *next; + + curr = *head; + while (curr) { + next = curr->a_next; + rtnl_act_put(curr); + curr = next; + } + *head = NULL; +} + +static struct nla_policy tc_act_stats_policy[TCA_STATS_MAX+1] = { + [TCA_STATS_BASIC] = { .minlen = sizeof(struct gnet_stats_basic) }, + [TCA_STATS_QUEUE] = { .minlen = sizeof(struct gnet_stats_queue) }, + [TCA_STATS_RATE_EST] = { .minlen = sizeof(struct gnet_stats_rate_est) }, + [TCA_STATS_RATE_EST64] = { .minlen = sizeof(struct gnet_stats_rate_est64) }, +}; + +int rtnl_act_parse(struct rtnl_act **head, struct nlattr *tb) +{ + _nl_auto_rtnl_act_all struct rtnl_act *tmp_head = NULL; + struct rtnl_tc_ops *ops; + struct nlattr *tb2[TCA_ACT_MAX + 1]; + struct nlattr *nla[TCA_ACT_MAX_PRIO + 1]; + char kind[TCKINDSIZ]; + int err, i; + + err = nla_parse(nla, TCA_ACT_MAX_PRIO, nla_data(tb), + NLMSG_ALIGN(nla_len(tb)), NULL); + if (err < 0) + return err; + + for (i = 0; i < TCA_ACT_MAX_PRIO; i++) { + _nl_auto_rtnl_act struct rtnl_act *act = NULL; + struct rtnl_tc *tc; + + if (nla[i] == NULL) + continue; + + act = rtnl_act_alloc(); + if (!act) + return -NLE_NOMEM; + + tc = TC_CAST(act); + err = nla_parse(tb2, TCA_ACT_MAX, nla_data(nla[i]), + nla_len(nla[i]), NULL); + if (err < 0) + return err; + + if (tb2[TCA_ACT_KIND] == NULL) + return -NLE_MISSING_ATTR; + + nla_strlcpy(kind, tb2[TCA_ACT_KIND], sizeof(kind)); + rtnl_tc_set_kind(tc, kind); + + if (tb2[TCA_ACT_OPTIONS]) { + tc->tc_opts = nl_data_alloc_attr(tb2[TCA_ACT_OPTIONS]); + if (!tc->tc_opts) + return -NLE_NOMEM; + tc->ce_mask |= TCA_ATTR_OPTS; + } + + if (tb2[TCA_ACT_STATS]) { + struct nlattr *tb3[TCA_STATS_MAX + 1]; + + err = nla_parse_nested(tb3, TCA_STATS_MAX, tb2[TCA_ACT_STATS], + tc_act_stats_policy); + if (err < 0) + return err; + + if (tb3[TCA_STATS_BASIC]) { + struct gnet_stats_basic bs; + + memcpy(&bs, nla_data(tb3[TCA_STATS_BASIC]), + sizeof(bs)); + tc->tc_stats[RTNL_TC_BYTES] = bs.bytes; + tc->tc_stats[RTNL_TC_PACKETS] = bs.packets; + } + if (tb3[TCA_STATS_RATE_EST64]) { + struct gnet_stats_rate_est64 re; + + memcpy(&re, nla_data(tb3[TCA_STATS_RATE_EST64]), + sizeof(re)); + tc->tc_stats[RTNL_TC_RATE_BPS] = re.bps; + tc->tc_stats[RTNL_TC_RATE_PPS] = re.pps; + } else if (tb3[TCA_STATS_RATE_EST]) { + struct gnet_stats_rate_est *re; + + re = nla_data(tb3[TCA_STATS_RATE_EST]); + tc->tc_stats[RTNL_TC_RATE_BPS] = re->bps; + tc->tc_stats[RTNL_TC_RATE_PPS] = re->pps; + } + if (tb3[TCA_STATS_QUEUE]) { + struct gnet_stats_queue *q; + + q = nla_data(tb3[TCA_STATS_QUEUE]); + tc->tc_stats[RTNL_TC_DROPS] = q->drops; + tc->tc_stats[RTNL_TC_OVERLIMITS] = q->overlimits; + } + } + + ops = rtnl_tc_get_ops(tc); + if (ops && ops->to_msg_parser) { + void *data = rtnl_tc_data(tc); + + if (!data) + return -NLE_NOMEM; + + err = ops->to_msg_parser(tc, data); + if (err < 0) + return err; + } + err = _rtnl_act_append_take(&tmp_head, _nl_steal_pointer(&act)); + if (err < 0) + return err; + } + + *head = _nl_steal_pointer(&tmp_head); + return 0; +} + +static int rtnl_act_msg_parse(struct nlmsghdr *n, struct rtnl_act **act) +{ + struct rtnl_tc *tc = TC_CAST(*act); + struct nl_cache *link_cache; + struct nlattr *tb[TCAA_MAX + 1]; + struct tcamsg *tm; + int err; + + tc->ce_msgtype = n->nlmsg_type; + + err = nlmsg_parse(n, sizeof(*tm), tb, TCAA_MAX, NULL); + if (err < 0) + return err; + + tm = nlmsg_data(n); + tc->tc_family = tm->tca_family; + + if (tb[TCA_ACT_TAB] == NULL) + return -NLE_MISSING_ATTR; + + err = rtnl_act_parse(act, tb[TCA_ACT_TAB]); + if (err < 0) + return err; + + if ((link_cache = __nl_cache_mngt_require("route/link"))) { + struct rtnl_link *link; + + if ((link = rtnl_link_get(link_cache, tc->tc_ifindex))) { + rtnl_tc_set_link(tc, link); + + /* rtnl_tc_set_link incs refcnt */ + rtnl_link_put(link); + } + } + + return 0; +} +static int act_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct rtnl_act *act, *p_act; + int err; + + if (!(act = rtnl_act_alloc())) + return -NLE_NOMEM; + + if ((err = rtnl_act_msg_parse(nlh, &act)) < 0) + goto errout; + + p_act = act; + while(p_act) { + err = pp->pp_cb(OBJ_CAST(act), pp); + if (err) { + if (err > 0) { + _nl_assert_not_reached(); + err = -NLE_FAILURE; + } + break; + } + p_act = p_act->a_next; + } +errout: + rtnl_act_put(act); + + return err; +} + +static int act_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + struct tcamsg tcahdr = { + .tca_family = AF_UNSPEC, + }; + + return nl_send_simple(sk, RTM_GETACTION, NLM_F_DUMP, &tcahdr, + sizeof(tcahdr)); +} + +static struct rtnl_tc_type_ops act_ops = { + .tt_type = RTNL_TC_TYPE_ACT, + .tt_dump_prefix = "act", + .tt_dump = { + [NL_DUMP_LINE] = act_dump_line, + }, +}; + +static struct nl_cache_ops rtnl_act_ops = { + .co_name = "route/act", + .co_hdrsize = sizeof(struct tcmsg), + .co_msgtypes = { + { RTM_NEWACTION, NL_ACT_NEW, "new" }, + { RTM_DELACTION, NL_ACT_DEL, "del" }, + { RTM_GETACTION, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_request_update = act_request_update, + .co_msg_parser = act_msg_parser, + .co_obj_ops = &act_obj_ops, +}; + +static struct nl_object_ops act_obj_ops = { + .oo_name = "route/act", + .oo_size = sizeof(struct rtnl_act), + .oo_free_data = rtnl_tc_free_data, + .oo_clone = rtnl_tc_clone, + .oo_dump = { + [NL_DUMP_LINE] = rtnl_tc_dump_line, + [NL_DUMP_DETAILS] = rtnl_tc_dump_details, + [NL_DUMP_STATS] = rtnl_tc_dump_stats, + }, + .oo_compare = rtnl_tc_compare, + .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE), +}; + +static void _nl_init act_init(void) +{ + rtnl_tc_type_register(&act_ops); + nl_cache_mngt_register(&rtnl_act_ops); +} + +static void _nl_exit act_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_act_ops); + rtnl_tc_type_unregister(&act_ops); +} + +/** @} */ diff --git a/libnl/lib/route/act/gact.c b/libnl/lib/route/act/gact.c new file mode 100644 index 0000000..2e04c5a --- /dev/null +++ b/libnl/lib/route/act/gact.c @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Sushma Sitaram + */ + +/** + * @ingroup act + * @defgroup act_gact GACT Editing + * + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include + +#include "tc-api.h" + +struct rtnl_gact { + struct tc_gact g_parm; +}; + +static struct nla_policy gact_policy[TCA_GACT_MAX + 1] = { + [TCA_GACT_PARMS] = { .minlen = sizeof(struct tc_gact) }, +}; + +static int gact_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_gact *u = data; + struct nlattr *tb[TCA_GACT_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_GACT_MAX, tc, gact_policy); + if (err < 0) + return err; + + if (!tb[TCA_GACT_PARMS]) + return -NLE_MISSING_ATTR; + + nla_memcpy(&u->g_parm, tb[TCA_GACT_PARMS], sizeof(u->g_parm)); + + return 0; +} + +static void gact_free_data(struct rtnl_tc *tc, void *data) +{ +} + +static void gact_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_gact *u = data; + + if (!u) + return; + + switch(u->g_parm.action){ + case TC_ACT_UNSPEC: + nl_dump(p, " continue"); + break; + case TC_ACT_SHOT: + nl_dump(p, " drop"); + break; + case TC_ACT_RECLASSIFY: + nl_dump(p, " reclassify"); + break; + case TC_ACT_OK: + nl_dump(p, " pass"); + break; + } + +} + +static void gact_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ +} + +static void gact_dump_stats(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_gact *u = data; + + if (!u) + return; + /* TODO */ +} + + +static int gact_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_gact *u = data; + + if (!u) + return 0; + + NLA_PUT(msg, TCA_GACT_PARMS, sizeof(u->g_parm), &u->g_parm); + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +/** + * @name Attribute Modifications + * @{ + */ + +int rtnl_gact_set_action(struct rtnl_act *act, int action) +{ + struct rtnl_gact *u; + + if (!(u = (struct rtnl_gact *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + u->g_parm.action = action; + + return 0; +} + +int rtnl_gact_get_action(struct rtnl_act *act) +{ + struct rtnl_gact *u; + + if (!(u = (struct rtnl_gact *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + return u->g_parm.action; +} + + +/** @} */ + +static struct rtnl_tc_ops gact_ops = { + .to_kind = "gact", + .to_type = RTNL_TC_TYPE_ACT, + .to_size = sizeof(struct rtnl_gact), + .to_msg_parser = gact_msg_parser, + .to_free_data = gact_free_data, + .to_clone = NULL, + .to_msg_fill = gact_msg_fill, + .to_dump = { + [NL_DUMP_LINE] = gact_dump_line, + [NL_DUMP_DETAILS] = gact_dump_details, + [NL_DUMP_STATS] = gact_dump_stats, + }, +}; + +static void _nl_init gact_init(void) +{ + rtnl_tc_register(&gact_ops); +} + +static void _nl_exit gact_exit(void) +{ + rtnl_tc_unregister(&gact_ops); +} + +/** @} */ diff --git a/libnl/lib/route/act/mirred.c b/libnl/lib/route/act/mirred.c new file mode 100644 index 0000000..72ead6b --- /dev/null +++ b/libnl/lib/route/act/mirred.c @@ -0,0 +1,222 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +/** + * @ingroup act + * @defgroup act_mirred Mirror and Redirect + * + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include + +#include "tc-api.h" + +struct rtnl_mirred { + struct tc_mirred m_parm; +}; + +static struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = { + [TCA_MIRRED_PARMS] = { .minlen = sizeof(struct tc_mirred) }, +}; + +static int mirred_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_mirred *u = data; + struct nlattr *tb[TCA_MIRRED_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_MIRRED_MAX, tc, mirred_policy); + if (err < 0) + return err; + + if (!tb[TCA_MIRRED_PARMS]) + return -NLE_MISSING_ATTR; + + nla_memcpy(&u->m_parm, tb[TCA_MIRRED_PARMS], sizeof(u->m_parm)); + return 0; +} + +static void mirred_free_data(struct rtnl_tc *tc, void *data) +{ +} + +static void mirred_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_mirred *u = data; + if (!u) + return; + + nl_dump(p, " index %u", u->m_parm.ifindex); + + if (u->m_parm.eaction == TCA_EGRESS_MIRROR) + nl_dump(p, " egress mirror"); + else if (u->m_parm.eaction == TCA_EGRESS_REDIR) + nl_dump(p, " egress redirect"); + + switch(u->m_parm.action) { + case TC_ACT_UNSPEC: + nl_dump(p, " unspecified"); + break; + case TC_ACT_PIPE: + nl_dump(p, " pipe"); + break; + case TC_ACT_STOLEN: + nl_dump(p, " stolen"); + break; + case TC_ACT_SHOT: + nl_dump(p, " shot"); + break; + case TC_ACT_QUEUED: + nl_dump(p, " queued"); + break; + case TC_ACT_REPEAT: + nl_dump(p, " repeat"); + break; + } +} + +static void mirred_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ +} + +static void mirred_dump_stats(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_mirred *u = data; + + if (!u) + return; + /* TODO */ +} + + +static int mirred_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_mirred *u = data; + + if (!u) + return 0; + + NLA_PUT(msg, TCA_MIRRED_PARMS, sizeof(u->m_parm), &u->m_parm); + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +/** + * @name Attribute Modifications + * @{ + */ + +int rtnl_mirred_set_action(struct rtnl_act *act, int action) +{ + struct rtnl_mirred *u; + + if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + if (action > TCA_INGRESS_MIRROR || action < TCA_EGRESS_REDIR) + return -NLE_INVAL; + + switch (action) { + case TCA_EGRESS_MIRROR: + case TCA_EGRESS_REDIR: + u->m_parm.eaction = action; + break; + case TCA_INGRESS_REDIR: + case TCA_INGRESS_MIRROR: + default: + return NLE_OPNOTSUPP; + } + return 0; +} + +int rtnl_mirred_get_action(struct rtnl_act *act) +{ + struct rtnl_mirred *u; + + if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + return u->m_parm.eaction; +} + +int rtnl_mirred_set_ifindex(struct rtnl_act *act, uint32_t ifindex) +{ + struct rtnl_mirred *u; + + if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + u->m_parm.ifindex = ifindex; + return 0; +} + +uint32_t rtnl_mirred_get_ifindex(struct rtnl_act *act) +{ + struct rtnl_mirred *u; + + if ((u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act)))) + return u->m_parm.ifindex; + return 0; +} + +int rtnl_mirred_set_policy(struct rtnl_act *act, int policy) +{ + struct rtnl_mirred *u; + + if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + u->m_parm.action = policy; + + return 0; +} + +int rtnl_mirred_get_policy(struct rtnl_act *act) +{ + struct rtnl_mirred *u; + + if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + return u->m_parm.action; +} + +/** @} */ + +static struct rtnl_tc_ops mirred_ops = { + .to_kind = "mirred", + .to_type = RTNL_TC_TYPE_ACT, + .to_size = sizeof(struct rtnl_mirred), + .to_msg_parser = mirred_msg_parser, + .to_free_data = mirred_free_data, + .to_clone = NULL, + .to_msg_fill = mirred_msg_fill, + .to_dump = { + [NL_DUMP_LINE] = mirred_dump_line, + [NL_DUMP_DETAILS] = mirred_dump_details, + [NL_DUMP_STATS] = mirred_dump_stats, + }, +}; + +static void _nl_init mirred_init(void) +{ + rtnl_tc_register(&mirred_ops); +} + +static void _nl_exit mirred_exit(void) +{ + rtnl_tc_unregister(&mirred_ops); +} + +/** @} */ diff --git a/libnl/lib/route/act/nat.c b/libnl/lib/route/act/nat.c new file mode 100644 index 0000000..ffc9d2b --- /dev/null +++ b/libnl/lib/route/act/nat.c @@ -0,0 +1,289 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Magnus Öberg + */ + +/** + * @ingroup act + * @defgroup act_nat NAT + * + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include + +#include "tc-api.h" + +static struct nla_policy nat_policy[TCA_NAT_MAX + 1] = { + [TCA_NAT_PARMS] = { .minlen = sizeof(struct tc_nat) }, +}; + +/** + * nat operations + */ + +static int nat_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct tc_nat *nat = data; + struct nlattr *tb[TCA_NAT_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_NAT_MAX, tc, nat_policy); + if (err < 0) + return err; + + if (!tb[TCA_NAT_PARMS]) + return -NLE_MISSING_ATTR; + + nla_memcpy(nat, tb[TCA_NAT_PARMS], sizeof(*nat)); + + return NLE_SUCCESS; +} + +static void nat_free_data(struct rtnl_tc *tc, void *data) +{ +} + +static int nat_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct tc_nat *nat = data; + + if (!nat) + return -NLE_OBJ_NOTFOUND; + + NLA_PUT(msg, TCA_NAT_PARMS, sizeof(*nat), nat); + + return NLE_SUCCESS; + +nla_put_failure: + return -NLE_NOMEM; +} + +static void nat_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct tc_nat *nat = data; + char buf[32]; + uint32_t mask; + int pfx = 0; + + if (!nat) + return; + + if (nat->flags & TCA_NAT_FLAG_EGRESS) + nl_dump(p, " egress"); + else + nl_dump(p, " ingress"); + + mask = nat->mask; + while (mask > 0) { + mask = mask >> 1; + pfx++; + } + + inet_ntop(AF_INET, &nat->old_addr, buf, sizeof(buf)); + nl_dump(p, " %s", buf); + if (pfx < 32) + nl_dump(p, "/%d", pfx); + + inet_ntop(AF_INET, &nat->new_addr, buf, sizeof(buf)); + nl_dump(p, " %s", buf); + if (pfx < 32) + nl_dump(p, "/%d", pfx); +} + +/** + * @name Attribute Modifications + * @{ + */ + +/** + * Set old IPv4 address on a netlink NAT action object + * @arg act Action object + * @arg addr Binary IPv4 address in host byte order + * + * @return 0 on success or negative error code in case of an error. + */ +int rtnl_nat_set_old_addr(struct rtnl_act *act, in_addr_t addr) +{ + struct tc_nat *nat; + + if (!(nat = (struct tc_nat *)rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + nat->old_addr = addr; + + return NLE_SUCCESS; +} + +int rtnl_nat_get_old_addr(struct rtnl_act *act, in_addr_t *addr) +{ + struct tc_nat *nat; + + if (!(nat = (struct tc_nat *)rtnl_tc_data_peek(TC_CAST(act)))) + return -NLE_NOATTR; + + *addr = nat->old_addr; + + return NLE_SUCCESS; +} + +/** + * Set new IPv4 address on a netlink NAT action object + * @arg act Action object + * @arg addr Binary IPv4 address in host byte order + * + * @return 0 on success or negative error code in case of an error. + */ +int rtnl_nat_set_new_addr(struct rtnl_act *act, in_addr_t addr) +{ + struct tc_nat *nat; + + if (!(nat = (struct tc_nat *)rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + nat->new_addr = addr; + + return NLE_SUCCESS; +} + +int rtnl_nat_get_new_addr(struct rtnl_act *act, in_addr_t *addr) +{ + struct tc_nat *nat; + + if (!(nat = (struct tc_nat *)rtnl_tc_data_peek(TC_CAST(act)))) + return -NLE_NOATTR; + + *addr = nat->new_addr; + + return NLE_SUCCESS; +} + +/** + * Set IPv4 address mask on a netlink NAT action object + * @arg act Action object + * @arg mask IPv4 address mask + * + * @return 0 on success or negative error code in case of an error. + */ +int rtnl_nat_set_mask(struct rtnl_act *act, in_addr_t bitmask) +{ + struct tc_nat *nat; + + if (!(nat = (struct tc_nat *)rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + nat->mask = bitmask; + + return NLE_SUCCESS; +} + +int rtnl_nat_get_mask(struct rtnl_act *act, in_addr_t *bitmask) +{ + struct tc_nat *nat; + + if (!(nat = (struct tc_nat *)rtnl_tc_data_peek(TC_CAST(act)))) + return -NLE_NOATTR; + + *bitmask = nat->mask; + + return NLE_SUCCESS; +} + +/** + * Set flags for a netlink NAT action object + * @arg act Action object + * @arg flags TCA_NAT_FLAG_* flags. + * + * Currently only TCA_NAT_FLAG_EGRESS is defined. Selects NAT on + * egress/IP src if set, ingress/IP dst otherwise. + * + * @return 0 on success or negative error code in case of an error. + */ +int rtnl_nat_set_flags(struct rtnl_act *act, uint32_t flags) +{ + struct tc_nat *nat; + + if (!(nat = (struct tc_nat *)rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + nat->flags = flags; + + return NLE_SUCCESS; +} + +int rtnl_nat_get_flags(struct rtnl_act *act, uint32_t *flags) +{ + struct tc_nat *nat; + + if (!(nat = (struct tc_nat *)rtnl_tc_data_peek(TC_CAST(act)))) + return -NLE_NOATTR; + + *flags = nat->flags; + + return NLE_SUCCESS; +} + +int rtnl_nat_set_action(struct rtnl_act *act, int action) +{ + struct tc_nat *nat; + + if (!(nat = (struct tc_nat *)rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + if (action < TC_ACT_UNSPEC) + return -NLE_INVAL; + + nat->action = action; + + return NLE_SUCCESS; +} + +int rtnl_nat_get_action(struct rtnl_act *act, int *action) +{ + struct tc_nat *nat; + + if (!(nat = (struct tc_nat *)rtnl_tc_data_peek(TC_CAST(act)))) + return -NLE_NOATTR; + + *action = nat->action; + + return NLE_SUCCESS; +} + +/** + * @} + */ + +static struct rtnl_tc_ops nat_ops = { + .to_kind = "nat", + .to_type = RTNL_TC_TYPE_ACT, + .to_size = sizeof(struct tc_nat), + .to_msg_parser = nat_msg_parser, + .to_free_data = nat_free_data, + .to_clone = NULL, + .to_msg_fill = nat_msg_fill, + .to_dump = { + [NL_DUMP_LINE] = nat_dump_line, + }, +}; + +static void _nl_init nat_init(void) +{ + rtnl_tc_register(&nat_ops); +} + +static void _nl_exit nat_exit(void) +{ + rtnl_tc_unregister(&nat_ops); +} + +/** + * @} + */ diff --git a/libnl/lib/route/act/skbedit.c b/libnl/lib/route/act/skbedit.c new file mode 100644 index 0000000..32a3506 --- /dev/null +++ b/libnl/lib/route/act/skbedit.c @@ -0,0 +1,284 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2015 Cong Wang + */ + +/** + * @ingroup act + * @defgroup act_skbedit SKB Editing + * + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include + +#include "nl-route.h" +#include "tc-api.h" + +struct rtnl_skbedit { + struct tc_skbedit s_parm; + uint32_t s_flags; + uint32_t s_mark; + uint32_t s_prio; + uint16_t s_queue_mapping; +}; + +static struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = { + [TCA_SKBEDIT_PARMS] = { .minlen = sizeof(struct tc_skbedit) }, + [TCA_SKBEDIT_PRIORITY] = { .type = NLA_U32 }, + [TCA_SKBEDIT_QUEUE_MAPPING] = { .type = NLA_U16 }, + [TCA_SKBEDIT_MARK] = { .type = NLA_U32 }, +}; + +static int skbedit_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_skbedit *u = data; + struct nlattr *tb[TCA_SKBEDIT_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_SKBEDIT_MAX, tc, skbedit_policy); + if (err < 0) + return err; + + if (!tb[TCA_SKBEDIT_PARMS]) + return -NLE_MISSING_ATTR; + + u->s_flags = 0; + if (tb[TCA_SKBEDIT_PRIORITY] != NULL) { + u->s_flags |= SKBEDIT_F_PRIORITY; + u->s_prio = nla_get_u32(tb[TCA_SKBEDIT_PRIORITY]); + } + + if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) { + u->s_flags |= SKBEDIT_F_QUEUE_MAPPING; + u->s_queue_mapping = nla_get_u16(tb[TCA_SKBEDIT_QUEUE_MAPPING]); + } + + if (tb[TCA_SKBEDIT_MARK] != NULL) { + u->s_flags |= SKBEDIT_F_MARK; + u->s_mark = nla_get_u32(tb[TCA_SKBEDIT_MARK]); + } + + return 0; +} + +static void skbedit_free_data(struct rtnl_tc *tc, void *data) +{ +} + +static void skbedit_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_skbedit *u = data; + + if (!u) + return; + + if (u->s_flags & SKBEDIT_F_PRIORITY) + nl_dump(p, " priority %u", u->s_prio); + + if (u->s_flags & SKBEDIT_F_MARK) + nl_dump(p, " mark %u", u->s_mark); + + if (u->s_flags & SKBEDIT_F_QUEUE_MAPPING) + nl_dump(p, " queue_mapping %u", u->s_queue_mapping); + + switch(u->s_parm.action){ + case TC_ACT_UNSPEC: + nl_dump(p, " unspecified"); + break; + case TC_ACT_PIPE: + nl_dump(p, " pipe"); + break; + case TC_ACT_STOLEN: + nl_dump(p, " stolen"); + break; + case TC_ACT_SHOT: + nl_dump(p, " shot"); + break; + case TC_ACT_QUEUED: + nl_dump(p, " queued"); + break; + case TC_ACT_REPEAT: + nl_dump(p, " repeat"); + break; + } +} + +static void skbedit_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ +} + +static void skbedit_dump_stats(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_skbedit *u = data; + + if (!u) + return; + /* TODO */ +} + + +static int skbedit_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_skbedit *u = data; + + if (!u) + return 0; + + NLA_PUT(msg, TCA_SKBEDIT_PARMS, sizeof(u->s_parm), &u->s_parm); + + if (u->s_flags & SKBEDIT_F_MARK) + NLA_PUT_U32(msg, TCA_SKBEDIT_MARK, u->s_mark); + + if (u->s_flags & SKBEDIT_F_PRIORITY) + NLA_PUT_U32(msg, TCA_SKBEDIT_PRIORITY, u->s_prio); + + if (u->s_flags & SKBEDIT_F_QUEUE_MAPPING) + NLA_PUT_U32(msg, TCA_SKBEDIT_QUEUE_MAPPING, u->s_queue_mapping); + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +/** + * @name Attribute Modifications + * @{ + */ + +int rtnl_skbedit_set_action(struct rtnl_act *act, int action) +{ + struct rtnl_skbedit *u; + + if (!(u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + u->s_parm.action = action; + + return 0; +} + +int rtnl_skbedit_get_action(struct rtnl_act *act) +{ + struct rtnl_skbedit *u; + + if (!(u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + return u->s_parm.action; +} + +int rtnl_skbedit_set_queue_mapping(struct rtnl_act *act, uint16_t index) +{ + struct rtnl_skbedit *u; + + if (!(u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + u->s_queue_mapping = index; + u->s_flags |= SKBEDIT_F_QUEUE_MAPPING; + return 0; +} + +int rtnl_skbedit_get_queue_mapping(struct rtnl_act *act, uint16_t *index) +{ + struct rtnl_skbedit *u; + + u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act)); + if (!u) + return -NLE_NOMEM; + if (!(u->s_flags & SKBEDIT_F_QUEUE_MAPPING)) + return -NLE_NOATTR; + + *index = u->s_queue_mapping; + return 0; +} + +int rtnl_skbedit_set_mark(struct rtnl_act *act, uint32_t mark) +{ + struct rtnl_skbedit *u; + + if (!(u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + u->s_mark = mark; + u->s_flags |= SKBEDIT_F_MARK; + return 0; +} + +int rtnl_skbedit_get_mark(struct rtnl_act *act, uint32_t *mark) +{ + struct rtnl_skbedit *u; + + u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act)); + if (!u) + return -NLE_NOMEM; + if (!(u->s_flags & SKBEDIT_F_MARK)) + return -NLE_NOATTR; + + *mark = u->s_mark; + return 0; +} + +int rtnl_skbedit_set_priority(struct rtnl_act *act, uint32_t prio) +{ + struct rtnl_skbedit *u; + + if (!(u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + u->s_prio = prio; + u->s_flags |= SKBEDIT_F_PRIORITY; + return 0; +} + +int rtnl_skbedit_get_priority(struct rtnl_act *act, uint32_t *prio) +{ + struct rtnl_skbedit *u; + + u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act)); + if (!u) + return -NLE_NOMEM; + if (!(u->s_flags & SKBEDIT_F_PRIORITY)) + return -NLE_NOATTR; + + *prio = u->s_prio; + return 0; +} + +/** @} */ + +static struct rtnl_tc_ops skbedit_ops = { + .to_kind = "skbedit", + .to_type = RTNL_TC_TYPE_ACT, + .to_size = sizeof(struct rtnl_skbedit), + .to_msg_parser = skbedit_msg_parser, + .to_free_data = skbedit_free_data, + .to_clone = NULL, + .to_msg_fill = skbedit_msg_fill, + .to_dump = { + [NL_DUMP_LINE] = skbedit_dump_line, + [NL_DUMP_DETAILS] = skbedit_dump_details, + [NL_DUMP_STATS] = skbedit_dump_stats, + }, +}; + +static void _nl_init skbedit_init(void) +{ + rtnl_tc_register(&skbedit_ops); +} + +static void _nl_exit skbedit_exit(void) +{ + rtnl_tc_unregister(&skbedit_ops); +} + +/** @} */ diff --git a/libnl/lib/route/act/vlan.c b/libnl/lib/route/act/vlan.c new file mode 100644 index 0000000..71f16e5 --- /dev/null +++ b/libnl/lib/route/act/vlan.c @@ -0,0 +1,423 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2018 Volodymyr Bendiuga + */ + +/** + * @ingroup act + * @defgroup act_vlan VLAN Manipulation + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include + +#include "tc-api.h" + +struct rtnl_vlan +{ + struct tc_vlan v_parm; + uint16_t v_vid; + uint16_t v_proto; + uint8_t v_prio; + uint32_t v_flags; +}; + +#define VLAN_F_VID (1 << 0) +#define VLAN_F_PROTO (1 << 1) +#define VLAN_F_PRIO (1 << 2) +#define VLAN_F_ACT (1 << 3) +#define VLAN_F_MODE (1 << 4) + +static struct nla_policy vlan_policy[TCA_VLAN_MAX + 1] = { + [TCA_VLAN_PARMS] = { .minlen = sizeof(struct tc_vlan) }, + [TCA_VLAN_PUSH_VLAN_ID] = { .type = NLA_U16 }, + [TCA_VLAN_PUSH_VLAN_PROTOCOL] = { .type = NLA_U16 }, + [TCA_VLAN_PUSH_VLAN_PRIORITY] = { .type = NLA_U8 }, +}; + +static int vlan_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_vlan *v = data; + struct nlattr *tb[TCA_VLAN_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_VLAN_MAX, tc, vlan_policy); + if (err < 0) + return err; + + v->v_flags = 0; + if (!tb[TCA_VLAN_PARMS]) + return -NLE_MISSING_ATTR; + else { + nla_memcpy(&v->v_parm, tb[TCA_VLAN_PARMS], sizeof(v->v_parm)); + v->v_flags |= VLAN_F_ACT; + v->v_flags |= VLAN_F_MODE; + } + + if (tb[TCA_VLAN_PUSH_VLAN_ID]) { + v->v_vid = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_ID]); + v->v_flags |= VLAN_F_VID; + } + + if (tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]) { + v->v_proto = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]); + v->v_flags |= VLAN_F_PROTO; + } + + if (tb[TCA_VLAN_PUSH_VLAN_PRIORITY]) { + v->v_prio = nla_get_u8(tb[TCA_VLAN_PUSH_VLAN_PRIORITY]); + v->v_flags |= VLAN_F_PRIO; + } + + return 0; +} + +static int vlan_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_vlan *v = data; + + if (!v) + return 0; + if (!(v->v_flags & VLAN_F_MODE)) + return -NLE_MISSING_ATTR; + + NLA_PUT(msg, TCA_VLAN_PARMS, sizeof(v->v_parm), &v->v_parm); + + /* vid is required for PUSH & MODIFY modes */ + if ((v->v_parm.v_action != TCA_VLAN_ACT_POP) && !(v->v_flags & VLAN_F_VID)) + return -NLE_MISSING_ATTR; + + if (v->v_flags & VLAN_F_VID) + NLA_PUT_U16(msg, TCA_VLAN_PUSH_VLAN_ID, v->v_vid); + + if (v->v_flags & VLAN_F_PROTO) + NLA_PUT_U16(msg, TCA_VLAN_PUSH_VLAN_PROTOCOL, v->v_proto); + + if (v->v_flags & VLAN_F_PRIO) + NLA_PUT_U8(msg, TCA_VLAN_PUSH_VLAN_PRIORITY, v->v_prio); + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +static void vlan_free_data(struct rtnl_tc *tc, void *data) +{ +} + +static void vlan_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_vlan *v = data; + + if (!v) + return; + + if (!(v->v_flags & VLAN_F_ACT)) + return; + + if (TC_ACT_EXT_CMP(v->v_parm.action, TC_ACT_GOTO_CHAIN)) + nl_dump(p, " goto chain %u", v->v_parm.action & TC_ACT_EXT_VAL_MASK); + + if (TC_ACT_EXT_CMP(v->v_parm.action, TC_ACT_JUMP)) + nl_dump(p, " jump %u", v->v_parm.action & TC_ACT_EXT_VAL_MASK); + + switch(v->v_parm.action){ + case TC_ACT_UNSPEC: + nl_dump(p, " unspecified"); + break; + case TC_ACT_PIPE: + nl_dump(p, " pipe"); + break; + case TC_ACT_STOLEN: + nl_dump(p, " stolen"); + break; + case TC_ACT_SHOT: + nl_dump(p, " shot"); + break; + case TC_ACT_QUEUED: + nl_dump(p, " queued"); + break; + case TC_ACT_REPEAT: + nl_dump(p, " repeat"); + break; + } +} + +static void vlan_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_vlan *v = data; + + if (!v) + return; + + if (v->v_flags & VLAN_F_MODE) { + switch (v->v_parm.v_action) { + case TCA_VLAN_ACT_POP: + nl_dump(p, " mode POP"); + break; + case TCA_VLAN_ACT_PUSH: + nl_dump(p, " mode PUSH"); + break; + case TCA_VLAN_ACT_MODIFY: + nl_dump(p, " mode MODIFY"); + break; + } + } + + if (v->v_flags & VLAN_F_VID) + nl_dump(p, " vlan id %u", v->v_vid); + + if (v->v_flags & VLAN_F_PRIO) + nl_dump(p, " priority %u", v->v_prio); + + if (v->v_flags & VLAN_F_PROTO) + nl_dump(p, " protocol %u", v->v_proto); +} + +/** + * @name Attribute Modifications + * @{ + */ + +/** + * Set vlan mode + * @arg act vlan action + * @arg mode one of (TCA_VLAN_ACT_*: POP, PUSH, MODIFY) + * @return 0 on success or a negative error code. + */ +int rtnl_vlan_set_mode(struct rtnl_act *act, int mode) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + if (mode > TCA_VLAN_ACT_MODIFY) + return -NLE_RANGE; + + v->v_parm.v_action = mode; + v->v_flags |= VLAN_F_MODE; + + return 0; +} + +/** + * Get vlan mode + * @arg act vlan action + * @arg out_mode vlan mode output paramter + * @return 0 on success if the vlan mode was returned or a negative error code. +*/ +int rtnl_vlan_get_mode(struct rtnl_act *act, int *out_mode) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act)))) + return -NLE_INVAL; + + if (!(v->v_flags & VLAN_F_MODE)) + return -NLE_MISSING_ATTR; + + *out_mode = v->v_parm.v_action; + return 0; +} + +/** + * Set general action + * @arg act vlan action + * @arg action one of (TCA_ACT_*: PIPE, SHOT, GOTO_CHAIN, etc) + * @return 0 on success or a negative error code. + */ +int rtnl_vlan_set_action(struct rtnl_act *act, int action) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + v->v_parm.action = action; + v->v_flags |= VLAN_F_ACT; + + return 0; +} + +/** + * Get general action + * @arg act vlan action + * @arg out_action output parameter + * @return general 0 if out_action was set or a negative error code. +*/ +int rtnl_vlan_get_action(struct rtnl_act *act, int *out_action) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act)))) + return -NLE_INVAL; + + if (!(v->v_flags & VLAN_F_ACT)) + return -NLE_MISSING_ATTR; + + *out_action = v->v_parm.action; + return 0; +} + +/** + * Set protocol + * @arg act vlan action + * @arg protocol one of (ETH_P_8021Q || ETH_P_8021AD) + * @return 0 on success or a negative error code. + */ +int rtnl_vlan_set_protocol(struct rtnl_act *act, uint16_t protocol) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + v->v_proto = protocol; + v->v_flags |= VLAN_F_PROTO; + + return 0; +} + +/** + * Get protocol + * @arg act vlan action + * @arg out_protocol protocol output argument + * @return 0 if the protocol was returned or a negative error code. +*/ +int rtnl_vlan_get_protocol(struct rtnl_act *act, uint16_t *out_protocol) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act)))) + return -NLE_INVAL; + + if (!(v->v_flags & VLAN_F_PROTO)) + return -NLE_MISSING_ATTR; + + *out_protocol = v->v_proto; + return 0; +} + +/** + * Set vlan id + * @arg act vlan action + * @arg vid vlan id + * @return 0 on success or a negative error code. + */ +int rtnl_vlan_set_vlan_id(struct rtnl_act *act, uint16_t vid) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + if (vid > 4095) + return -NLE_RANGE; + + v->v_vid = vid; + v->v_flags |= VLAN_F_VID; + + return 0; +} + +/** + * Get vlan id + * @arg act vlan action + * @arg out_vid output vlan id + * @return 0 if the vlan id was returned or a negative error code. +*/ +int rtnl_vlan_get_vlan_id(struct rtnl_act *act, uint16_t *out_vid) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act)))) + return -NLE_INVAL; + + if (!(v->v_flags & VLAN_F_VID)) + return -NLE_MISSING_ATTR; + + *out_vid = v->v_vid; + return 0; +} + +/** + * Set vlan prio + * @arg act vlan action + * @arg prio vlan priority (0 - 7) + * @return 0 on success or a negative error code. + */ +int rtnl_vlan_set_vlan_prio(struct rtnl_act *act, uint8_t prio) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + if (prio > 7) + return -NLE_RANGE; + + v->v_prio = prio; + v->v_flags |= VLAN_F_PRIO; + + return 0; +} + +/** + * Get vlan prio + * @arg act vlan action + * @arg out_prio the output vlan prio + * @return 0 if the vlan prio was returned or a negative error code. +*/ +int rtnl_vlan_get_vlan_prio(struct rtnl_act *act, uint8_t *out_prio) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act)))) + return -NLE_INVAL; + + if (!(v->v_flags & VLAN_F_PRIO)) + return -NLE_MISSING_ATTR; + + *out_prio = v->v_prio; + return 0; +} + +/** @} */ + +static struct rtnl_tc_ops vlan_ops = { + .to_kind = "vlan", + .to_type = RTNL_TC_TYPE_ACT, + .to_size = sizeof(struct rtnl_vlan), + .to_msg_parser = vlan_msg_parser, + .to_free_data = vlan_free_data, + .to_clone = NULL, + .to_msg_fill = vlan_msg_fill, + .to_dump = { + [NL_DUMP_LINE] = vlan_dump_line, + [NL_DUMP_DETAILS] = vlan_dump_details, + }, +}; + +static void _nl_init vlan_init(void) +{ + rtnl_tc_register(&vlan_ops); +} + +static void _nl_exit vlan_exit(void) +{ + rtnl_tc_unregister(&vlan_ops); +} + +/** @} */ diff --git a/libnl/lib/route/addr.c b/libnl/lib/route/addr.c new file mode 100644 index 0000000..34b834e --- /dev/null +++ b/libnl/lib/route/addr.c @@ -0,0 +1,1251 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + * Copyright (c) 2003-2006 Baruch Even + * Copyright (c) 2003-2006 Mediatrix Telecom, inc. + */ + +/** + * @ingroup rtnl + * @defgroup rtaddr Addresses + * @brief + * + * @note The maximum size of an address label is IFNAMSIZ. + * + * @note The address may not contain a prefix length if the peer address + * has been specified already. + * + * @par 1) Address Addition + * @code + * // Allocate an empty address object to be filled out with the attributes + * // of the new address. + * struct rtnl_addr *addr = rtnl_addr_alloc(); + * + * // Fill out the mandatory attributes of the new address. Setting the + * // local address will automatically set the address family and the + * // prefix length to the correct values. + * rtnl_addr_set_ifindex(addr, ifindex); + * rtnl_addr_set_local(addr, local_addr); + * + * // The label of the address can be specified, currently only supported + * // by IPv4 and DECnet. + * rtnl_addr_set_label(addr, "mylabel"); + * + * // The peer address can be specified if necessary, in either case a peer + * // address will be sent to the kernel in order to fullfil the interface + * // requirements. If none is set, it will equal the local address. + * // Note: Real peer addresses are only supported by IPv4 for now. + * rtnl_addr_set_peer(addr, peer_addr); + * + * // In case you want to have the address have a scope other than global + * // it may be overwritten using rtnl_addr_set_scope(). The scope currently + * // cannot be set for IPv6 addresses. + * rtnl_addr_set_scope(addr, rtnl_str2scope("site")); + * + * // Broadcast address may be specified using the relevant + * // functions, the address family will be verified if one of the other + * // addresses has been set already. Currently only works for IPv4. + * rtnl_addr_set_broadcast(addr, broadcast_addr); + * + * // Build the netlink message and send it to the kernel, the operation will + * // block until the operation has been completed. Alternatively the required + * // netlink message can be built using rtnl_addr_build_add_request() to be + * // sent out using nl_send_auto_complete(). + * rtnl_addr_add(sk, addr, 0); + * + * // Free the memory + * rtnl_addr_put(addr); + * @endcode + * + * @par 2) Address Deletion + * @code + * // Allocate an empty address object to be filled out with the attributes + * // matching the address to be deleted. Alternatively a fully equipped + * // address object out of a cache can be used instead. + * struct rtnl_addr *addr = rtnl_addr_alloc(); + * + * // The only mandatory parameter besides the address family is the interface + * // index the address is on, i.e. leaving out all other parameters will + * // result in all addresses of the specified address family interface tuple + * // to be deleted. + * rtnl_addr_set_ifindex(addr, ifindex); + * + * // Specyfing the address family manually is only required if neither the + * // local nor peer address have been specified. + * rtnl_addr_set_family(addr, AF_INET); + * + * // Specyfing the local address is optional but the best choice to delete + * // specific addresses. + * rtnl_addr_set_local(addr, local_addr); + * + * // The label of the address can be specified, currently only supported + * // by IPv4 and DECnet. + * rtnl_addr_set_label(addr, "mylabel"); + * + * // The peer address can be specified if necessary, in either case a peer + * // address will be sent to the kernel in order to fullfil the interface + * // requirements. If none is set, it will equal the local address. + * // Note: Real peer addresses are only supported by IPv4 for now. + * rtnl_addr_set_peer(addr, peer_addr); + * + * // Build the netlink message and send it to the kernel, the operation will + * // block until the operation has been completed. Alternatively the required + * // netlink message can be built using rtnl_addr_build_delete_request() + * // to be sent out using nl_send_auto_complete(). + * rtnl_addr_delete(sk, addr, 0); + * + * // Free the memory + * rtnl_addr_put(addr); + * @endcode + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" + +/** @cond SKIP */ +struct rtnl_addr_cacheinfo { + /* Preferred lifetime in seconds, ticking from when the message gets constructed */ + uint32_t aci_prefered; + + /* Valid lifetime in seconds, ticking from when the message gets constructed */ + uint32_t aci_valid; + + /* Timestamp of creation in 1/100s since boottime, clock_gettime(CLOCK_MONOTONIC) */ + uint32_t aci_cstamp; + + /* Timestamp of last update in 1/100s since boottime, clock_gettime(CLOCK_MONOTONIC) */ + uint32_t aci_tstamp; +}; + +struct rtnl_addr { + NLHDR_COMMON + + uint8_t a_family; + uint8_t a_prefixlen; + uint8_t a_scope; + uint32_t a_flags; + uint32_t a_ifindex; + + struct nl_addr *a_peer; + struct nl_addr *a_local; + struct nl_addr *a_bcast; + struct nl_addr *a_anycast; + struct nl_addr *a_multicast; + + struct rtnl_addr_cacheinfo a_cacheinfo; + + char a_label[IFNAMSIZ]; + uint32_t a_flag_mask; + struct rtnl_link *a_link; +}; + +#define ADDR_ATTR_FAMILY 0x0001 +#define ADDR_ATTR_PREFIXLEN 0x0002 +#define ADDR_ATTR_FLAGS 0x0004 +#define ADDR_ATTR_SCOPE 0x0008 +#define ADDR_ATTR_IFINDEX 0x0010 +#define ADDR_ATTR_LABEL 0x0020 +#define ADDR_ATTR_CACHEINFO 0x0040 +#define ADDR_ATTR_PEER 0x0080 +#define ADDR_ATTR_LOCAL 0x0100 +#define ADDR_ATTR_BROADCAST 0x0200 +#define ADDR_ATTR_MULTICAST 0x0400 +#define ADDR_ATTR_ANYCAST 0x0800 + +static struct nl_cache_ops rtnl_addr_ops; +static struct nl_object_ops addr_obj_ops; +/** @endcond */ + +static void addr_constructor(struct nl_object *obj) +{ + struct rtnl_addr *addr = nl_object_priv(obj); + + addr->a_scope = RT_SCOPE_NOWHERE; +} + +static void addr_free_data(struct nl_object *obj) +{ + struct rtnl_addr *addr = nl_object_priv(obj); + + if (!addr) + return; + + nl_addr_put(addr->a_peer); + nl_addr_put(addr->a_local); + nl_addr_put(addr->a_bcast); + nl_addr_put(addr->a_multicast); + nl_addr_put(addr->a_anycast); + rtnl_link_put(addr->a_link); +} + +static int addr_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_addr *dst = nl_object_priv(_dst); + struct rtnl_addr *src = nl_object_priv(_src); + + dst->a_peer = NULL; + dst->a_local = NULL; + dst->a_bcast = NULL; + dst->a_anycast = NULL; + dst->a_multicast = NULL; + dst->a_link = NULL; + + if (src->a_link) { + nl_object_get(OBJ_CAST(src->a_link)); + dst->a_link = src->a_link; + } + + if (src->a_peer) + if (!(dst->a_peer = nl_addr_clone(src->a_peer))) + return -NLE_NOMEM; + + if (src->a_local) + if (!(dst->a_local = nl_addr_clone(src->a_local))) + return -NLE_NOMEM; + + if (src->a_bcast) + if (!(dst->a_bcast = nl_addr_clone(src->a_bcast))) + return -NLE_NOMEM; + + if (src->a_multicast) + if (!(dst->a_multicast = nl_addr_clone(src->a_multicast))) + return -NLE_NOMEM; + + if (src->a_anycast) + if (!(dst->a_anycast = nl_addr_clone(src->a_anycast))) + return -NLE_NOMEM; + + return 0; +} + +static struct nla_policy addr_policy[IFA_MAX+1] = { + [IFA_LABEL] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, + [IFA_CACHEINFO] = { .minlen = sizeof(struct ifa_cacheinfo) }, +}; + +static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct rtnl_addr *addr; + struct ifaddrmsg *ifa; + struct nlattr *tb[IFA_MAX+1]; + int err, family; + struct nl_cache *link_cache; + struct nl_addr *plen_addr = NULL; + + addr = rtnl_addr_alloc(); + if (!addr) + return -NLE_NOMEM; + + addr->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(*ifa), tb, IFA_MAX, addr_policy); + if (err < 0) + goto errout; + + ifa = nlmsg_data(nlh); + addr->a_family = family = ifa->ifa_family; + addr->a_prefixlen = ifa->ifa_prefixlen; + addr->a_scope = ifa->ifa_scope; + addr->a_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : + ifa->ifa_flags; + addr->a_ifindex = ifa->ifa_index; + + addr->ce_mask = (ADDR_ATTR_FAMILY | ADDR_ATTR_PREFIXLEN | + ADDR_ATTR_FLAGS | ADDR_ATTR_SCOPE | ADDR_ATTR_IFINDEX); + + if (tb[IFA_LABEL]) { + nla_strlcpy(addr->a_label, tb[IFA_LABEL], IFNAMSIZ); + addr->ce_mask |= ADDR_ATTR_LABEL; + } + + /* IPv6 only */ + if (tb[IFA_CACHEINFO]) { + struct ifa_cacheinfo *ca; + + ca = nla_data(tb[IFA_CACHEINFO]); + addr->a_cacheinfo.aci_prefered = ca->ifa_prefered; + addr->a_cacheinfo.aci_valid = ca->ifa_valid; + addr->a_cacheinfo.aci_cstamp = ca->cstamp; + addr->a_cacheinfo.aci_tstamp = ca->tstamp; + addr->ce_mask |= ADDR_ATTR_CACHEINFO; + } + + if (family == AF_INET) { + uint32_t null = 0; + + /* for IPv4/AF_INET, kernel always sets IFA_LOCAL and IFA_ADDRESS, unless it + * is effectively 0.0.0.0. */ + if (tb[IFA_LOCAL]) + addr->a_local = nl_addr_alloc_attr(tb[IFA_LOCAL], family); + else + addr->a_local = nl_addr_build(family, &null, sizeof (null)); + if (!addr->a_local) + goto errout_nomem; + addr->ce_mask |= ADDR_ATTR_LOCAL; + + if (tb[IFA_ADDRESS]) + addr->a_peer = nl_addr_alloc_attr(tb[IFA_ADDRESS], family); + else + addr->a_peer = nl_addr_build(family, &null, sizeof (null)); + if (!addr->a_peer) + goto errout_nomem; + + if (!nl_addr_cmp (addr->a_local, addr->a_peer)) { + /* having IFA_ADDRESS equal to IFA_LOCAL does not really mean + * there is no peer. It means the peer is equal to the local address, + * which is the case for "normal" addresses. + * + * Still, clear the peer and pretend it is unset for backward + * compatibility. */ + nl_addr_put(addr->a_peer); + addr->a_peer = NULL; + } else + addr->ce_mask |= ADDR_ATTR_PEER; + + plen_addr = addr->a_local; + } else { + if (tb[IFA_LOCAL]) { + addr->a_local = nl_addr_alloc_attr(tb[IFA_LOCAL], family); + if (!addr->a_local) + goto errout_nomem; + addr->ce_mask |= ADDR_ATTR_LOCAL; + plen_addr = addr->a_local; + } + + if (tb[IFA_ADDRESS]) { + struct nl_addr *a; + + a = nl_addr_alloc_attr(tb[IFA_ADDRESS], family); + if (!a) + goto errout_nomem; + + /* IPv6 sends the local address as IFA_ADDRESS with + * no IFA_LOCAL, IPv4 sends both IFA_LOCAL and IFA_ADDRESS + * with IFA_ADDRESS being the peer address if they differ */ + if (!tb[IFA_LOCAL] || !nl_addr_cmp(a, addr->a_local)) { + nl_addr_put(addr->a_local); + addr->a_local = a; + addr->ce_mask |= ADDR_ATTR_LOCAL; + } else { + addr->a_peer = a; + addr->ce_mask |= ADDR_ATTR_PEER; + } + + plen_addr = a; + } + } + + if (plen_addr) + nl_addr_set_prefixlen(plen_addr, addr->a_prefixlen); + + /* IPv4 only */ + if (tb[IFA_BROADCAST]) { + addr->a_bcast = nl_addr_alloc_attr(tb[IFA_BROADCAST], family); + if (!addr->a_bcast) + goto errout_nomem; + + addr->ce_mask |= ADDR_ATTR_BROADCAST; + } + + /* IPv6 only */ + if (tb[IFA_MULTICAST]) { + addr->a_multicast = nl_addr_alloc_attr(tb[IFA_MULTICAST], + family); + if (!addr->a_multicast) + goto errout_nomem; + + addr->ce_mask |= ADDR_ATTR_MULTICAST; + } + + /* IPv6 only */ + if (tb[IFA_ANYCAST]) { + addr->a_anycast = nl_addr_alloc_attr(tb[IFA_ANYCAST], + family); + if (!addr->a_anycast) + goto errout_nomem; + + addr->ce_mask |= ADDR_ATTR_ANYCAST; + } + + if ((link_cache = __nl_cache_mngt_require("route/link"))) { + struct rtnl_link *link; + + if ((link = rtnl_link_get(link_cache, addr->a_ifindex))) { + rtnl_addr_set_link(addr, link); + + /* rtnl_addr_set_link incs refcnt */ + rtnl_link_put(link); + } + } + + err = pp->pp_cb((struct nl_object *) addr, pp); +errout: + rtnl_addr_put(addr); + + return err; + +errout_nomem: + err = -NLE_NOMEM; + goto errout; +} + +static int addr_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + return nl_rtgen_request(sk, RTM_GETADDR, AF_UNSPEC, NLM_F_DUMP); +} + +static void addr_dump_line(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_addr *addr = (struct rtnl_addr *) obj; + struct nl_cache *link_cache; + char buf[128]; + + link_cache = nl_cache_mngt_require_safe("route/link"); + + if (addr->ce_mask & ADDR_ATTR_LOCAL) + nl_dump_line(p, "%s", + nl_addr2str(addr->a_local, buf, sizeof(buf))); + else + nl_dump_line(p, "none"); + + if (addr->ce_mask & ADDR_ATTR_PEER) + nl_dump(p, " peer %s", + nl_addr2str(addr->a_peer, buf, sizeof(buf))); + + nl_dump(p, " %s ", nl_af2str(addr->a_family, buf, sizeof(buf))); + + if (link_cache) + nl_dump(p, "dev %s ", + rtnl_link_i2name(link_cache, addr->a_ifindex, + buf, sizeof(buf))); + else + nl_dump(p, "dev %d ", addr->a_ifindex); + + nl_dump(p, "scope %s", + rtnl_scope2str(addr->a_scope, buf, sizeof(buf))); + + rtnl_addr_flags2str(addr->a_flags, buf, sizeof(buf)); + if (buf[0]) + nl_dump(p, " <%s>", buf); + + nl_dump(p, "\n"); + + if (link_cache) + nl_cache_put(link_cache); +} + +static void addr_dump_details(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_addr *addr = (struct rtnl_addr *) obj; + char buf[128]; + + addr_dump_line(obj, p); + + if (addr->ce_mask & (ADDR_ATTR_LABEL | ADDR_ATTR_BROADCAST | + ADDR_ATTR_MULTICAST)) { + nl_dump_line(p, " "); + + if (addr->ce_mask & ADDR_ATTR_LABEL) + nl_dump(p, " label %s", addr->a_label); + + if (addr->ce_mask & ADDR_ATTR_BROADCAST) + nl_dump(p, " broadcast %s", + nl_addr2str(addr->a_bcast, buf, sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_MULTICAST) + nl_dump(p, " multicast %s", + nl_addr2str(addr->a_multicast, buf, + sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_ANYCAST) + nl_dump(p, " anycast %s", + nl_addr2str(addr->a_anycast, buf, + sizeof(buf))); + + nl_dump(p, "\n"); + } + + if (addr->ce_mask & ADDR_ATTR_CACHEINFO) { + struct rtnl_addr_cacheinfo *ci = &addr->a_cacheinfo; + + nl_dump_line(p, " valid-lifetime %s", + ci->aci_valid == 0xFFFFFFFFU ? "forever" : + nl_msec2str(ci->aci_valid * 1000, + buf, sizeof(buf))); + + nl_dump(p, " preferred-lifetime %s\n", + ci->aci_prefered == 0xFFFFFFFFU ? "forever" : + nl_msec2str(ci->aci_prefered * 1000, + buf, sizeof(buf))); + + nl_dump_line(p, " created boot-time+%s ", + nl_msec2str(addr->a_cacheinfo.aci_cstamp * 10, + buf, sizeof(buf))); + + nl_dump(p, "last-updated boot-time+%s\n", + nl_msec2str(addr->a_cacheinfo.aci_tstamp * 10, + buf, sizeof(buf))); + } +} + +static void addr_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + addr_dump_details(obj, p); +} + +static uint32_t addr_id_attrs_get(struct nl_object *obj) +{ + struct rtnl_addr *addr = (struct rtnl_addr *)obj; + uint32_t rv; + + switch (addr->a_family) { + case AF_INET: + rv = (ADDR_ATTR_FAMILY | ADDR_ATTR_IFINDEX | + ADDR_ATTR_LOCAL | ADDR_ATTR_PREFIXLEN); + if (addr->a_peer) + rv |= ADDR_ATTR_PEER; + return rv; + case AF_INET6: + return (ADDR_ATTR_FAMILY | ADDR_ATTR_IFINDEX | + ADDR_ATTR_LOCAL); + default: + return (ADDR_ATTR_FAMILY | ADDR_ATTR_IFINDEX | + ADDR_ATTR_LOCAL | ADDR_ATTR_PREFIXLEN); + } +} + +static uint64_t addr_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct rtnl_addr *a = (struct rtnl_addr *) _a; + struct rtnl_addr *b = (struct rtnl_addr *) _b; + uint64_t diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(ADDR_ATTR_IFINDEX, a->a_ifindex != b->a_ifindex); + diff |= _DIFF(ADDR_ATTR_FAMILY, a->a_family != b->a_family); + diff |= _DIFF(ADDR_ATTR_SCOPE, a->a_scope != b->a_scope); + diff |= _DIFF(ADDR_ATTR_LABEL, strcmp(a->a_label, b->a_label)); + if (attrs & ADDR_ATTR_PEER) { + if ((flags & ID_COMPARISON) && a->a_family == AF_INET && + b->a_family == AF_INET && a->a_peer && b->a_peer && + a->a_prefixlen == b->a_prefixlen) { + /* when comparing two IPv4 addresses for id-equality, the network part + * of the PEER address shall be compared. + */ + diff |= _DIFF(ADDR_ATTR_PEER, + nl_addr_cmp_prefix(a->a_peer, b->a_peer)); + } else + diff |= _DIFF(ADDR_ATTR_PEER, + nl_addr_cmp(a->a_peer, b->a_peer)); + } + diff |= _DIFF(ADDR_ATTR_LOCAL, nl_addr_cmp(a->a_local, b->a_local)); + diff |= _DIFF(ADDR_ATTR_MULTICAST, + nl_addr_cmp(a->a_multicast, b->a_multicast)); + diff |= _DIFF(ADDR_ATTR_BROADCAST, nl_addr_cmp(a->a_bcast, b->a_bcast)); + diff |= _DIFF(ADDR_ATTR_ANYCAST, + nl_addr_cmp(a->a_anycast, b->a_anycast)); + diff |= _DIFF(ADDR_ATTR_CACHEINFO, + memcmp(&a->a_cacheinfo, &b->a_cacheinfo, + sizeof(a->a_cacheinfo))); + + if (flags & LOOSE_COMPARISON) + diff |= _DIFF(ADDR_ATTR_FLAGS, + (a->a_flags ^ b->a_flags) & b->a_flag_mask); + else + diff |= _DIFF(ADDR_ATTR_FLAGS, a->a_flags != b->a_flags); +#undef _DIFF + + return diff; +} + +static const struct trans_tbl addr_attrs[] = { + __ADD(ADDR_ATTR_FAMILY, family), + __ADD(ADDR_ATTR_PREFIXLEN, prefixlen), + __ADD(ADDR_ATTR_FLAGS, flags), + __ADD(ADDR_ATTR_SCOPE, scope), + __ADD(ADDR_ATTR_IFINDEX, ifindex), + __ADD(ADDR_ATTR_LABEL, label), + __ADD(ADDR_ATTR_CACHEINFO, cacheinfo), + __ADD(ADDR_ATTR_PEER, peer), + __ADD(ADDR_ATTR_LOCAL, local), + __ADD(ADDR_ATTR_BROADCAST, broadcast), + __ADD(ADDR_ATTR_MULTICAST, multicast), +}; + +static char *addr_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, addr_attrs, + ARRAY_SIZE(addr_attrs)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_addr *rtnl_addr_alloc(void) +{ + return (struct rtnl_addr *) nl_object_alloc(&addr_obj_ops); +} + +void rtnl_addr_put(struct rtnl_addr *addr) +{ + nl_object_put((struct nl_object *) addr); +} + +/** @} */ + +/** + * @name Cache Management + * @{ + */ + +int rtnl_addr_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&rtnl_addr_ops, sk, result); +} + +/** + * Search address in cache + * @arg cache Address cache + * @arg ifindex Interface index of address + * @arg addr Local address part + * + * Searches address cache previously allocated with rtnl_addr_alloc_cache() + * for an address with a matching local address. + * + * The reference counter is incremented before returning the address, therefore + * the reference must be given back with rtnl_addr_put() after usage. + * + * @return Address object or NULL if no match was found. + */ +struct rtnl_addr *rtnl_addr_get(struct nl_cache *cache, int ifindex, + struct nl_addr *addr) +{ + struct rtnl_addr *a; + + if (cache->c_ops != &rtnl_addr_ops) + return NULL; + + nl_list_for_each_entry(a, &cache->c_items, ce_list) { + if (ifindex != 0 && a->a_ifindex != ((unsigned)ifindex)) + continue; + + if (a->ce_mask & ADDR_ATTR_LOCAL && + !nl_addr_cmp(a->a_local, addr)) { + nl_object_get((struct nl_object *) a); + return a; + } + } + + return NULL; +} + +/** @} */ + +static int build_addr_msg(struct rtnl_addr *tmpl, int cmd, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + struct ifaddrmsg am = { + .ifa_family = tmpl->a_family, + .ifa_index = tmpl->a_ifindex, + .ifa_prefixlen = tmpl->a_prefixlen, + .ifa_flags = tmpl->a_flags, + }; + + if (tmpl->ce_mask & ADDR_ATTR_SCOPE) + am.ifa_scope = tmpl->a_scope; + else { + /* compatibility hack */ + if (tmpl->a_family == AF_INET && + tmpl->ce_mask & ADDR_ATTR_LOCAL && + *((char *) nl_addr_get_binary_addr(tmpl->a_local)) == 127) + am.ifa_scope = RT_SCOPE_HOST; + else + am.ifa_scope = RT_SCOPE_UNIVERSE; + } + + msg = nlmsg_alloc_simple(cmd, flags); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &am, sizeof(am), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (tmpl->ce_mask & ADDR_ATTR_LOCAL) + NLA_PUT_ADDR(msg, IFA_LOCAL, tmpl->a_local); + + if (tmpl->ce_mask & ADDR_ATTR_PEER) + NLA_PUT_ADDR(msg, IFA_ADDRESS, tmpl->a_peer); + else if (tmpl->ce_mask & ADDR_ATTR_LOCAL) + NLA_PUT_ADDR(msg, IFA_ADDRESS, tmpl->a_local); + + if (tmpl->ce_mask & ADDR_ATTR_LABEL) + NLA_PUT_STRING(msg, IFA_LABEL, tmpl->a_label); + + if (tmpl->ce_mask & ADDR_ATTR_BROADCAST) + NLA_PUT_ADDR(msg, IFA_BROADCAST, tmpl->a_bcast); + + if (tmpl->ce_mask & ADDR_ATTR_CACHEINFO) { + struct ifa_cacheinfo ca = { + .ifa_valid = tmpl->a_cacheinfo.aci_valid, + .ifa_prefered = tmpl->a_cacheinfo.aci_prefered, + }; + + NLA_PUT(msg, IFA_CACHEINFO, sizeof(ca), &ca); + } + + if (tmpl->a_flags & ~0xFF) { + /* only set the IFA_FLAGS attribute, if they actually contain additional + * flags that are not already set to am.ifa_flags. + * + * Older kernels refuse RTM_NEWADDR and RTM_NEWROUTE messages with EINVAL + * if they contain unknown netlink attributes. See net/core/rtnetlink.c, which + * was fixed by kernel commit 661d2967b3f1b34eeaa7e212e7b9bbe8ee072b59. + * + * With this workaround, libnl will function correctly with older kernels, + * unless there is a new libnl user that wants to set these flags. In this + * case it's up to the user to workaround this issue. */ + NLA_PUT_U32(msg, IFA_FLAGS, tmpl->a_flags); + } + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +/** + * @name Addition + * @{ + */ + +/** + * Build netlink request message to request addition of new address + * @arg addr Address object representing the new address. + * @arg flags Additional netlink message flags. + * @arg result Pointer to store resulting message. + * + * Builds a new netlink message requesting the addition of a new + * address. The netlink message header isn't fully equipped with + * all relevant fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. + * + * Minimal required attributes: + * - interface index (rtnl_addr_set_ifindex()) + * - local address (rtnl_addr_set_local()) + * + * The scope will default to universe except for loopback addresses in + * which case a host scope is used if not specified otherwise. + * + * @note Free the memory after usage using nlmsg_free(). + * + * @return 0 on success or a negative error code. + */ +int rtnl_addr_build_add_request(struct rtnl_addr *addr, int flags, + struct nl_msg **result) +{ + uint32_t required = ADDR_ATTR_IFINDEX | ADDR_ATTR_FAMILY | + ADDR_ATTR_PREFIXLEN | ADDR_ATTR_LOCAL; + + if ((addr->ce_mask & required) != required) + return -NLE_MISSING_ATTR; + + return build_addr_msg(addr, RTM_NEWADDR, NLM_F_CREATE | flags, result); +} + +/** + * Request addition of new address + * @arg sk Netlink socket. + * @arg addr Address object representing the new address. + * @arg flags Additional netlink message flags. + * + * Builds a netlink message by calling rtnl_addr_build_add_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been fullfilled. + * + * @see rtnl_addr_build_add_request() + * + * @return 0 on success or a negative error if an error occured. + */ +int rtnl_addr_add(struct nl_sock *sk, struct rtnl_addr *addr, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_addr_build_add_request(addr, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** @} */ + +/** + * @name Deletion + * @{ + */ + +/** + * Build a netlink request message to request deletion of an address + * @arg addr Address object to be deleteted. + * @arg flags Additional netlink message flags. + * @arg result Pointer to store resulting message. + * + * Builds a new netlink message requesting a deletion of an address. + * The netlink message header isn't fully equipped with all relevant + * fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. + * + * Minimal required attributes: + * - interface index (rtnl_addr_set_ifindex()) + * - address family (rtnl_addr_set_family()) + * + * Optional attributes: + * - local address (rtnl_addr_set_local()) + * - label (rtnl_addr_set_label(), IPv4/DECnet only) + * - peer address (rtnl_addr_set_peer(), IPv4 only) + * + * @note Free the memory after usage using nlmsg_free(). + * + * @return 0 on success or a negative error code. + */ +int rtnl_addr_build_delete_request(struct rtnl_addr *addr, int flags, + struct nl_msg **result) +{ + uint32_t required = ADDR_ATTR_IFINDEX | ADDR_ATTR_FAMILY; + + if ((addr->ce_mask & required) != required) + return -NLE_MISSING_ATTR; + + return build_addr_msg(addr, RTM_DELADDR, flags, result); +} + +/** + * Request deletion of an address + * @arg sk Netlink socket. + * @arg addr Address object to be deleted. + * @arg flags Additional netlink message flags. + * + * Builds a netlink message by calling rtnl_addr_build_delete_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been fullfilled. + * + * @see rtnl_addr_build_delete_request(); + * + * @return 0 on success or a negative error if an error occured. + */ +int rtnl_addr_delete(struct nl_sock *sk, struct rtnl_addr *addr, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_addr_build_delete_request(addr, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +int rtnl_addr_set_label(struct rtnl_addr *addr, const char *label) +{ + if (strlen(label) > sizeof(addr->a_label) - 1) + return -NLE_RANGE; + + strcpy(addr->a_label, label); + addr->ce_mask |= ADDR_ATTR_LABEL; + + return 0; +} + +char *rtnl_addr_get_label(struct rtnl_addr *addr) +{ + if (addr->ce_mask & ADDR_ATTR_LABEL) + return addr->a_label; + else + return NULL; +} + +void rtnl_addr_set_ifindex(struct rtnl_addr *addr, int ifindex) +{ + addr->a_ifindex = ifindex; + addr->ce_mask |= ADDR_ATTR_IFINDEX; +} + +int rtnl_addr_get_ifindex(struct rtnl_addr *addr) +{ + return addr->a_ifindex; +} + +void rtnl_addr_set_link(struct rtnl_addr *addr, struct rtnl_link *link) +{ + rtnl_link_put(addr->a_link); + + if (!link) + return; + + nl_object_get(OBJ_CAST(link)); + addr->a_link = link; + addr->a_ifindex = link->l_index; + addr->ce_mask |= ADDR_ATTR_IFINDEX; +} + +struct rtnl_link *rtnl_addr_get_link(struct rtnl_addr *addr) +{ + if (addr->a_link) { + nl_object_get(OBJ_CAST(addr->a_link)); + return addr->a_link; + } + + return NULL; +} + +void rtnl_addr_set_family(struct rtnl_addr *addr, int family) +{ + addr->a_family = family; + addr->ce_mask |= ADDR_ATTR_FAMILY; +} + +int rtnl_addr_get_family(struct rtnl_addr *addr) +{ + return addr->a_family; +} + +/** + * Set the prefix length / netmask + * @arg addr Address + * @arg prefixlen Length of prefix (netmask) + * + * Modifies the length of the prefix. If the address object contains a peer + * address the prefix length will apply to it, otherwise the prefix length + * will apply to the local address of the address. + * + * If the address object contains a peer or local address the corresponding + * `struct nl_addr` will be updated with the new prefix length. + * + * @note Specifying a length of 0 will remove the prefix length alltogether. + * + * @see rtnl_addr_get_prefixlen() + */ +void rtnl_addr_set_prefixlen(struct rtnl_addr *addr, int prefixlen) +{ + addr->a_prefixlen = prefixlen; + + if (prefixlen) + addr->ce_mask |= ADDR_ATTR_PREFIXLEN; + else + addr->ce_mask &= ~ADDR_ATTR_PREFIXLEN; + + /* + * The prefix length always applies to the peer address if + * a peer address is present. + */ + if (addr->a_peer) + nl_addr_set_prefixlen(addr->a_peer, prefixlen); + else if (addr->a_local) + nl_addr_set_prefixlen(addr->a_local, prefixlen); +} + +int rtnl_addr_get_prefixlen(struct rtnl_addr *addr) +{ + return addr->a_prefixlen; +} + +void rtnl_addr_set_scope(struct rtnl_addr *addr, int scope) +{ + addr->a_scope = scope; + addr->ce_mask |= ADDR_ATTR_SCOPE; +} + +int rtnl_addr_get_scope(struct rtnl_addr *addr) +{ + return addr->a_scope; +} + +void rtnl_addr_set_flags(struct rtnl_addr *addr, unsigned int flags) +{ + addr->a_flag_mask |= flags; + addr->a_flags |= flags; + addr->ce_mask |= ADDR_ATTR_FLAGS; +} + +void rtnl_addr_unset_flags(struct rtnl_addr *addr, unsigned int flags) +{ + addr->a_flag_mask |= flags; + addr->a_flags &= ~flags; + addr->ce_mask |= ADDR_ATTR_FLAGS; +} + +unsigned int rtnl_addr_get_flags(struct rtnl_addr *addr) +{ + return addr->a_flags; +} + +static inline int __assign_addr(struct rtnl_addr *addr, struct nl_addr **pos, + struct nl_addr *new, int flag) +{ + if (new) { + if (addr->ce_mask & ADDR_ATTR_FAMILY) { + if (new->a_family != addr->a_family) + return -NLE_AF_MISMATCH; + } else + addr->a_family = new->a_family; + + if (*pos) + nl_addr_put(*pos); + + *pos = nl_addr_get(new); + addr->ce_mask |= (flag | ADDR_ATTR_FAMILY); + } else { + if (*pos) + nl_addr_put(*pos); + + *pos = NULL; + addr->ce_mask &= ~flag; + } + + return 0; +} + +int rtnl_addr_set_local(struct rtnl_addr *addr, struct nl_addr *local) +{ + int err; + + /* Prohibit local address with prefix length if peer address is present */ + if ((addr->ce_mask & ADDR_ATTR_PEER) && local && + nl_addr_get_prefixlen(local)) + return -NLE_INVAL; + + err = __assign_addr(addr, &addr->a_local, local, ADDR_ATTR_LOCAL); + if (err < 0) + return err; + + /* Never overwrite the prefix length if a peer address is present */ + if (!(addr->ce_mask & ADDR_ATTR_PEER)) + rtnl_addr_set_prefixlen(addr, local ? nl_addr_get_prefixlen(local) : 0); + + return 0; +} + +struct nl_addr *rtnl_addr_get_local(struct rtnl_addr *addr) +{ + return addr->a_local; +} + +int rtnl_addr_set_peer(struct rtnl_addr *addr, struct nl_addr *peer) +{ + int err; + + if (peer && peer->a_family != AF_INET) + return -NLE_AF_NOSUPPORT; + + err = __assign_addr(addr, &addr->a_peer, peer, ADDR_ATTR_PEER); + if (err < 0) + return err; + + rtnl_addr_set_prefixlen(addr, peer ? nl_addr_get_prefixlen(peer) : 0); + + return 0; +} + +struct nl_addr *rtnl_addr_get_peer(struct rtnl_addr *addr) +{ + return addr->a_peer; +} + +int rtnl_addr_set_broadcast(struct rtnl_addr *addr, struct nl_addr *bcast) +{ + if (bcast && bcast->a_family != AF_INET) + return -NLE_AF_NOSUPPORT; + + return __assign_addr(addr, &addr->a_bcast, bcast, ADDR_ATTR_BROADCAST); +} + +struct nl_addr *rtnl_addr_get_broadcast(struct rtnl_addr *addr) +{ + return addr->a_bcast; +} + +int rtnl_addr_set_multicast(struct rtnl_addr *addr, struct nl_addr *multicast) +{ + if (multicast && multicast->a_family != AF_INET6) + return -NLE_AF_NOSUPPORT; + + return __assign_addr(addr, &addr->a_multicast, multicast, + ADDR_ATTR_MULTICAST); +} + +struct nl_addr *rtnl_addr_get_multicast(struct rtnl_addr *addr) +{ + return addr->a_multicast; +} + +int rtnl_addr_set_anycast(struct rtnl_addr *addr, struct nl_addr *anycast) +{ + if (anycast && anycast->a_family != AF_INET6) + return -NLE_AF_NOSUPPORT; + + return __assign_addr(addr, &addr->a_anycast, anycast, + ADDR_ATTR_ANYCAST); +} + +struct nl_addr *rtnl_addr_get_anycast(struct rtnl_addr *addr) +{ + return addr->a_anycast; +} + +uint32_t rtnl_addr_get_valid_lifetime(struct rtnl_addr *addr) +{ + if (addr->ce_mask & ADDR_ATTR_CACHEINFO) + return addr->a_cacheinfo.aci_valid; + else + return 0xFFFFFFFFU; +} + +void rtnl_addr_set_valid_lifetime(struct rtnl_addr *addr, uint32_t lifetime) +{ + addr->a_cacheinfo.aci_valid = lifetime; + addr->ce_mask |= ADDR_ATTR_CACHEINFO; +} + +uint32_t rtnl_addr_get_preferred_lifetime(struct rtnl_addr *addr) +{ + if (addr->ce_mask & ADDR_ATTR_CACHEINFO) + return addr->a_cacheinfo.aci_prefered; + else + return 0xFFFFFFFFU; +} + +void rtnl_addr_set_preferred_lifetime(struct rtnl_addr *addr, uint32_t lifetime) +{ + addr->a_cacheinfo.aci_prefered = lifetime; + addr->ce_mask |= ADDR_ATTR_CACHEINFO; +} + +uint32_t rtnl_addr_get_create_time(struct rtnl_addr *addr) +{ + return addr->a_cacheinfo.aci_cstamp; +} + +uint32_t rtnl_addr_get_last_update_time(struct rtnl_addr *addr) +{ + return addr->a_cacheinfo.aci_tstamp; +} + +/** @} */ + +/** + * @name Flags Translations + * @{ + */ + +static const struct trans_tbl addr_flags[] = { + __ADD(IFA_F_SECONDARY, secondary), + __ADD(IFA_F_NODAD, nodad), + __ADD(IFA_F_OPTIMISTIC, optimistic), + __ADD(IFA_F_DADFAILED, dadfailed), + __ADD(IFA_F_HOMEADDRESS, homeaddress), + __ADD(IFA_F_DEPRECATED, deprecated), + __ADD(IFA_F_TENTATIVE, tentative), + __ADD(IFA_F_PERMANENT, permanent), + __ADD(IFA_F_MANAGETEMPADDR, mngtmpaddr), + __ADD(IFA_F_NOPREFIXROUTE, noprefixroute), +}; + +char *rtnl_addr_flags2str(int flags, char *buf, size_t size) +{ + return __flags2str(flags, buf, size, addr_flags, + ARRAY_SIZE(addr_flags)); +} + +int rtnl_addr_str2flags(const char *name) +{ + return __str2flags(name, addr_flags, ARRAY_SIZE(addr_flags)); +} + +/** @} */ + +static struct nl_object_ops addr_obj_ops = { + .oo_name = "route/addr", + .oo_size = sizeof(struct rtnl_addr), + .oo_constructor = addr_constructor, + .oo_free_data = addr_free_data, + .oo_clone = addr_clone, + .oo_dump = { + [NL_DUMP_LINE] = addr_dump_line, + [NL_DUMP_DETAILS] = addr_dump_details, + [NL_DUMP_STATS] = addr_dump_stats, + }, + .oo_compare = addr_compare, + .oo_attrs2str = addr_attrs2str, + .oo_id_attrs_get = addr_id_attrs_get, + .oo_id_attrs = (ADDR_ATTR_FAMILY | ADDR_ATTR_IFINDEX | + ADDR_ATTR_LOCAL | ADDR_ATTR_PREFIXLEN), +}; + +static struct nl_af_group addr_groups[] = { + { AF_INET, RTNLGRP_IPV4_IFADDR }, + { AF_INET6, RTNLGRP_IPV6_IFADDR }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops rtnl_addr_ops = { + .co_name = "route/addr", + .co_hdrsize = sizeof(struct ifaddrmsg), + .co_msgtypes = { + { RTM_NEWADDR, NL_ACT_NEW, "new" }, + { RTM_DELADDR, NL_ACT_DEL, "del" }, + { RTM_GETADDR, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = addr_groups, + .co_request_update = addr_request_update, + .co_msg_parser = addr_msg_parser, + .co_obj_ops = &addr_obj_ops, +}; + +static void _nl_init addr_init(void) +{ + nl_cache_mngt_register(&rtnl_addr_ops); +} + +static void _nl_exit addr_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_addr_ops); +} + +/** @} */ diff --git a/libnl/lib/route/class.c b/libnl/lib/route/class.c new file mode 100644 index 0000000..c04b112 --- /dev/null +++ b/libnl/lib/route/class.c @@ -0,0 +1,508 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +/** + * @ingroup tc + * @defgroup class Traffic Classes + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "tc-api.h" + +struct rtnl_class { + NL_TC_GENERIC(c); +}; + +static struct nl_cache_ops rtnl_class_ops; +static struct nl_object_ops class_obj_ops; + +static void class_dump_details(struct rtnl_tc *tc, struct nl_dump_params *p) +{ + struct rtnl_class *class = (struct rtnl_class *) tc; + char buf[32]; + + if (class->c_info) + nl_dump(p, "child-qdisc %s ", + rtnl_tc_handle2str(class->c_info, buf, sizeof(buf))); +} + + +static int class_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct rtnl_class *class; + int err; + + if (!(class = rtnl_class_alloc())) + return -NLE_NOMEM; + + if ((err = rtnl_tc_msg_parse(nlh, TC_CAST(class))) < 0) + goto errout; + + err = pp->pp_cb(OBJ_CAST(class), pp); +errout: + rtnl_class_put(class); + + return err; +} + +static int class_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + struct tcmsg tchdr = { + .tcm_family = AF_UNSPEC, + .tcm_ifindex = cache->c_iarg1, + }; + + return nl_send_simple(sk, RTM_GETTCLASS, NLM_F_DUMP, &tchdr, + sizeof(tchdr)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_class *rtnl_class_alloc(void) +{ + struct rtnl_tc *tc; + + tc = TC_CAST(nl_object_alloc(&class_obj_ops)); + if (tc) + tc->tc_type = RTNL_TC_TYPE_CLASS; + + return (struct rtnl_class *) tc; +} + +void rtnl_class_put(struct rtnl_class *class) +{ + nl_object_put((struct nl_object *) class); +} + +/** @} */ + + +/** + * @name Addition/Modification/Deletion + * @{ + */ + +static int class_build(struct rtnl_class *class, int type, int flags, + struct nl_msg **result) +{ + uint32_t needed = TCA_ATTR_PARENT | TCA_ATTR_HANDLE; + + if ((class->ce_mask & needed) == needed && + TC_H_MAJ(class->c_parent) && TC_H_MAJ(class->c_handle) && + TC_H_MAJ(class->c_parent) != TC_H_MAJ(class->c_handle)) { + APPBUG("TC_H_MAJ(parent) must match TC_H_MAJ(handle)"); + return -NLE_INVAL; + } + + return rtnl_tc_msg_build(TC_CAST(class), type, flags, result); +} + +/** + * Build a netlink message requesting the addition of a traffic class + * @arg class Traffic class to add + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_class_add() with + * the exception that it will not send the message but return it int the + * provided return pointer instead. + * + * @see rtnl_class_add() + * + * @return 0 on success or a negative error code. + */ +int rtnl_class_build_add_request(struct rtnl_class *class, int flags, + struct nl_msg **result) +{ + return class_build(class, RTM_NEWTCLASS, flags, result); +} + +/** + * Add/Update traffic class + * @arg sk Netlink socket + * @arg class Traffic class to add + * @arg flags Additional netlink message flags + * + * Builds a \c RTM_NEWTCLASS netlink message requesting the addition + * of a new traffic class and sends the message to the kernel. The + * configuration of the traffic class is derived from the attributes + * of the specified traffic class. + * + * The following flags may be specified: + * - \c NLM_F_CREATE: Create traffic class if it does not exist, + * otherwise -NLE_OBJ_NOTFOUND is returned. + * - \c NLM_F_EXCL: Return -NLE_EXISTS if a traffic class with + * matching handle exists already. + * + * Existing traffic classes with matching handles will be updated, + * unless the flag \c NLM_F_EXCL is specified. If no matching traffic + * class exists, it will be created if the flag \c NLM_F_CREATE is set, + * otherwise the error -NLE_OBJ_NOTFOUND is returned. + * + * If the parent qdisc does not support classes, the error + * \c NLE_OPNOTSUPP is returned. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_class_add(struct nl_sock *sk, struct rtnl_class *class, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_class_build_add_request(class, flags, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build netlink message requesting the deletion of a traffic class + * @arg class Traffic class to delete + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_class_delete() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_class_delete() + * + * @return 0 on success or a negative error code. + */ +int rtnl_class_build_delete_request(struct rtnl_class *class, struct nl_msg **result) +{ + struct nl_msg *msg; + struct tcmsg tchdr; + uint32_t required = TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE; + + if ((class->ce_mask & required) != required) { + APPBUG("ifindex and handle must be specified"); + return -NLE_MISSING_ATTR; + } + + if (!(msg = nlmsg_alloc_simple(RTM_DELTCLASS, 0))) + return -NLE_NOMEM; + + memset(&tchdr, 0, sizeof(tchdr)); + tchdr.tcm_family = AF_UNSPEC; + tchdr.tcm_ifindex = class->c_ifindex; + tchdr.tcm_handle = class->c_handle; + + if (class->ce_mask & TCA_ATTR_PARENT) + tchdr.tcm_parent = class->c_parent; + + if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) { + nlmsg_free(msg); + return -NLE_MSGSIZE; + } + + *result = msg; + return 0; +} + +/** + * Delete traffic class + * @arg sk Netlink socket + * @arg class Traffic class to delete + * + * Builds a \c RTM_DELTCLASS netlink message requesting the deletion + * of a traffic class and sends the message to the kernel. + * + * The message is constructed out of the following attributes: + * - \c ifindex and \c handle (required) + * - \c parent (optional, must match if provided) + * + * All other class attributes including all class type specific + * attributes are ignored. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_class_delete(struct nl_sock *sk, struct rtnl_class *class) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_class_build_delete_request(class, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** @} */ + +/** + * @name Leaf Qdisc + * @{ + */ + +/** + * Lookup the leaf qdisc of a traffic class + * @arg class the parent traffic class + * @arg cache a qdisc cache allocated using rtnl_qdisc_alloc_cache() + * + * @return Matching Qdisc or NULL if the traffic class has no leaf qdisc + */ +struct rtnl_qdisc *rtnl_class_leaf_qdisc(struct rtnl_class *class, + struct nl_cache *cache) +{ + struct rtnl_qdisc *leaf; + + if (!class->c_info) + return NULL; + + leaf = rtnl_qdisc_get_by_parent(cache, class->c_ifindex, + class->c_handle); + if (!leaf || leaf->q_handle != class->c_info) + return NULL; + + return leaf; +} + +/** @} */ + +/** + * @name Cache Related Functions + * @{ + */ + +/** + * Allocate a cache and fill it with all configured traffic classes + * @arg sk Netlink socket + * @arg ifindex Interface index of the network device + * @arg result Pointer to store the created cache + * + * Allocates a new traffic class cache and fills it with a list of all + * configured traffic classes on a specific network device. Release the + * cache with nl_cache_free(). + * + * @return 0 on success or a negative error code. + */ +int rtnl_class_alloc_cache(struct nl_sock *sk, int ifindex, + struct nl_cache **result) +{ + struct nl_cache * cache; + int err; + + if (!ifindex) { + APPBUG("ifindex must be specified"); + return -NLE_INVAL; + } + + if (!(cache = nl_cache_alloc(&rtnl_class_ops))) + return -NLE_NOMEM; + + cache->c_iarg1 = ifindex; + + if (sk && (err = nl_cache_refill(sk, cache)) < 0) { + nl_cache_free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** + * Search traffic class by interface index and handle + * @arg cache Traffic class cache + * @arg ifindex Interface index + * @arg handle ID of traffic class + * + * Searches a traffic class cache previously allocated with + * rtnl_class_alloc_cache() and searches for a traffi class matching + * the interface index and handle. + * + * The reference counter is incremented before returning the traffic + * class, therefore the reference must be given back with rtnl_class_put() + * after usage. + * + * @return Traffic class or NULL if no match was found. + */ +struct rtnl_class *rtnl_class_get(struct nl_cache *cache, int ifindex, + uint32_t handle) +{ + struct rtnl_class *class; + + if (cache->c_ops != &rtnl_class_ops) + return NULL; + + nl_list_for_each_entry(class, &cache->c_items, ce_list) { + if (class->c_handle == handle && + class->c_ifindex == ((unsigned)ifindex)) { + nl_object_get((struct nl_object *) class); + return class; + } + } + return NULL; +} + +/** + * Search class by interface index and parent + * @arg cache Traffic class cache + * @arg ifindex Interface index + * @arg parent Handle of parent qdisc + * + * Searches a class cache previously allocated with rtnl_class_alloc_cache() + * and searches for a class matching the interface index and parent qdisc. + * + * The reference counter is incremented before returning the class, therefore + * the reference must be given back with rtnl_class_put() after usage. + * + * @return pointer to class inside the cache or NULL if no match was found. + */ +struct rtnl_class *rtnl_class_get_by_parent(struct nl_cache *cache, int ifindex, + uint32_t parent) +{ + struct rtnl_class *class; + + if (cache->c_ops != &rtnl_class_ops) + return NULL; + + nl_list_for_each_entry(class, &cache->c_items, ce_list) { + if (class->c_parent == parent && + class->c_ifindex == ((unsigned)ifindex)) { + nl_object_get((struct nl_object *) class); + return class; + } + } + + return NULL; +} + +/** @} */ + +/** + * @name Deprecated Functions + * @{ + */ + +/** + * Call a callback for each child of a class + * + * @deprecated Use of this function is deprecated, it does not allow + * to handle the out of memory situation that can occur. + */ +void rtnl_class_foreach_child(struct rtnl_class *class, struct nl_cache *cache, + void (*cb)(struct nl_object *, void *), void *arg) +{ + struct rtnl_class *filter; + + filter = rtnl_class_alloc(); + if (!filter) + return; + + rtnl_tc_set_parent(TC_CAST(filter), class->c_handle); + rtnl_tc_set_ifindex(TC_CAST(filter), class->c_ifindex); + rtnl_tc_set_kind(TC_CAST(filter), class->c_kind); + + nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg); + rtnl_class_put(filter); +} + +/** + * Call a callback for each classifier attached to the class + * + * @deprecated Use of this function is deprecated, it does not allow + * to handle the out of memory situation that can occur. + */ +void rtnl_class_foreach_cls(struct rtnl_class *class, struct nl_cache *cache, + void (*cb)(struct nl_object *, void *), void *arg) +{ + struct rtnl_cls *filter; + + filter = rtnl_cls_alloc(); + if (!filter) + return; + + rtnl_tc_set_ifindex((struct rtnl_tc *) filter, class->c_ifindex); + rtnl_tc_set_parent((struct rtnl_tc *) filter, class->c_parent); + + nl_cache_foreach_filter(cache, (struct nl_object *) filter, cb, arg); + rtnl_cls_put(filter); +} + +/** @} */ + +static struct rtnl_tc_type_ops class_ops = { + .tt_type = RTNL_TC_TYPE_CLASS, + .tt_dump_prefix = "class", + .tt_dump = { + [NL_DUMP_DETAILS] = class_dump_details, + }, +}; + +static struct nl_object_ops class_obj_ops = { + .oo_name = "route/class", + .oo_size = sizeof(struct rtnl_class), + .oo_free_data = rtnl_tc_free_data, + .oo_clone = rtnl_tc_clone, + .oo_dump = { + [NL_DUMP_LINE] = rtnl_tc_dump_line, + [NL_DUMP_DETAILS] = rtnl_tc_dump_details, + [NL_DUMP_STATS] = rtnl_tc_dump_stats, + }, + .oo_compare = rtnl_tc_compare, + .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE), +}; + +static struct nl_cache_ops rtnl_class_ops = { + .co_name = "route/class", + .co_hdrsize = sizeof(struct tcmsg), + .co_msgtypes = { + { RTM_NEWTCLASS, NL_ACT_NEW, "new" }, + { RTM_DELTCLASS, NL_ACT_DEL, "del" }, + { RTM_GETTCLASS, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = tc_groups, + .co_request_update = &class_request_update, + .co_msg_parser = &class_msg_parser, + .co_obj_ops = &class_obj_ops, +}; + +static void _nl_init class_init(void) +{ + rtnl_tc_type_register(&class_ops); + nl_cache_mngt_register(&rtnl_class_ops); +} + +static void _nl_exit class_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_class_ops); + rtnl_tc_type_unregister(&class_ops); +} + +/** @} */ diff --git a/libnl/lib/route/classid.c b/libnl/lib/route/classid.c new file mode 100644 index 0000000..f5a75f0 --- /dev/null +++ b/libnl/lib/route/classid.c @@ -0,0 +1,457 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2013 Thomas Graf + */ + +/** + * @ingroup tc + * @defgroup classid ClassID Management + * @{ + */ + +#include "nl-default.h" + +#include +#include + +#include +#include +#include + +#include "nl-route.h" +#include "nl-aux-core/nl-core.h" + +struct classid_map +{ + uint32_t classid; + char * name; + struct nl_list_head name_list; +}; + +#define CLASSID_NAME_HT_SIZ 256 + +static struct nl_list_head tbl_name[CLASSID_NAME_HT_SIZ]; + +static void *id_root = NULL; + +static int compare_id(const void *pa, const void *pb) +{ + const struct classid_map *ma = pa; + const struct classid_map *mb = pb; + + if (ma->classid < mb->classid) + return -1; + + if (ma->classid > mb->classid) + return 1; + + return 0; +} + +/* djb2 */ +static unsigned int classid_tbl_hash(const char *str) +{ + unsigned long hash = 5381; + int c; + + while ((c = *str++)) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + + return hash % CLASSID_NAME_HT_SIZ; +} + +static int classid_lookup(const char *name, uint32_t *result) +{ + struct classid_map *map; + int n = classid_tbl_hash(name); + + nl_list_for_each_entry(map, &tbl_name[n], name_list) { + if (!strcasecmp(map->name, name)) { + *result = map->classid; + return 0; + } + } + + return -NLE_OBJ_NOTFOUND; +} + +static char *name_lookup(const uint32_t classid) +{ + void *res; + struct classid_map cm = { + .classid = classid, + .name = "search entry", + }; + + if ((res = tfind(&cm, &id_root, &compare_id))) + return (*(struct classid_map **) res)->name; + + return NULL; +} + +/** + * @name Traffic Control Handle Translations + * @{ + */ + +/** + * Convert a traffic control handle to a character string (Reentrant). + * @arg handle traffic control handle + * @arg buf destination buffer + * @arg len buffer length + * + * Converts a tarffic control handle to a character string in the + * form of \c MAJ:MIN and stores it in the specified destination buffer. + * + * @return The destination buffer or the type encoded in hexidecimal + * form if no match was found. + */ +char *rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len) +{ + if (TC_H_ROOT == handle) + snprintf(buf, len, "root"); + else if (TC_H_UNSPEC == handle) + snprintf(buf, len, "none"); + else if (TC_H_INGRESS == handle) + snprintf(buf, len, "ingress"); + else { + char *name; + + if ((name = name_lookup(handle))) + snprintf(buf, len, "%s", name); + else if (0 == TC_H_MAJ(handle)) + snprintf(buf, len, ":%x", TC_H_MIN(handle)); + else if (0 == TC_H_MIN(handle)) + snprintf(buf, len, "%x:", TC_H_MAJ(handle) >> 16); + else + snprintf(buf, len, "%x:%x", + TC_H_MAJ(handle) >> 16, TC_H_MIN(handle)); + } + + return buf; +} + +/** + * Convert a charactering strint to a traffic control handle + * @arg str traffic control handle as character string + * @arg res destination buffer + * + * Converts the provided character string specifying a traffic + * control handle to the corresponding numeric value. + * + * The handle must be provided in one of the following formats: + * - NAME + * - root + * - none + * - MAJ: + * - :MIN + * - NAME:MIN + * - MAJ:MIN + * - MAJMIN + * + * @return 0 on success or a negative error code + */ +int rtnl_tc_str2handle(const char *str, uint32_t *res) +{ + char *colon, *end; + uint32_t h; + int err; + + if (!strcasecmp(str, "root")) { + *res = TC_H_ROOT; + return 0; + } + + if (!strcasecmp(str, "none")) { + *res = TC_H_UNSPEC; + return 0; + } + + if (!strcasecmp(str, "ingress")) { + *res = TC_H_INGRESS; + return 0; + } + + h = strtoul(str, &colon, 16); + + /* MAJ is not a number */ + if (colon == str) { +not_a_number: + if (*colon == ':') { + /* :YYYY */ + h = 0; + } else { + size_t len; + char name[64] = { 0 }; + + if (!(colon = strpbrk(str, ":"))) { + /* NAME */ + return classid_lookup(str, res); + } else { + /* NAME:YYYY */ + len = colon - str; + if (len >= sizeof(name)) + return -NLE_INVAL; + + memcpy(name, str, len); + + if ((err = classid_lookup(name, &h)) < 0) + return err; + + /* Name must point to a qdisc alias */ + if (TC_H_MIN(h)) + return -NLE_INVAL; + + /* NAME: is not allowed */ + if (colon[1] == '\0') + return -NLE_INVAL; + + goto update; + } + } + } + + if (':' == *colon) { + /* check if we would lose bits */ + if (TC_H_MAJ(h)) + return -NLE_RANGE; + h <<= 16; + + if ('\0' == colon[1]) { + /* XXXX: */ + *res = h; + } else { + /* XXXX:YYYY */ + uint32_t l; + +update: + l = strtoul(colon+1, &end, 16); + + /* check if we overlap with major part */ + if (TC_H_MAJ(l)) + return -NLE_RANGE; + + if ('\0' != *end) + return -NLE_INVAL; + + *res = (h | l); + } + } else if ('\0' == *colon) { + /* XXXXYYYY */ + *res = h; + } else + goto not_a_number; + + return 0; +} + +static void free_nothing(void *arg) +{ +} + +static void classid_map_free(struct classid_map *map) +{ + if (!map) + return; + + free(map->name); + free(map); +} + +static void clear_hashtable(void) +{ + int i; + + for (i = 0; i < CLASSID_NAME_HT_SIZ; i++) { + struct classid_map *map, *n; + + nl_list_for_each_entry_safe(map, n, &tbl_name[i], name_list) + classid_map_free(map); + + nl_init_list_head(&tbl_name[i]); + + } + + if (id_root) { + tdestroy(&id_root, &free_nothing); + id_root = NULL; + } +} + +static int classid_map_add(uint32_t classid, const char *name) +{ + struct classid_map *map; + int n; + + if (!(map = calloc(1, sizeof(*map)))) + return -NLE_NOMEM; + + map->classid = classid; + map->name = strdup(name); + + n = classid_tbl_hash(map->name); + nl_list_add_tail(&map->name_list, &tbl_name[n]); + + if (!tsearch((void *) map, &id_root, &compare_id)) { + classid_map_free(map); + return -NLE_NOMEM; + } + + return 0; +} + +/** + * (Re-)read classid file + * + * Rereads the contents of the classid file (typically found at the location + * /etc/libnl/classid) and refreshes the classid maps. + * + * @return 0 on success or a negative error code. + */ +int rtnl_tc_read_classid_file(void) +{ + static time_t last_read; + struct stat st; + char buf[256], *path; + FILE *fd; + int err; + + if (build_sysconf_path(&path, "classid") < 0) + return -NLE_NOMEM; + + /* if stat fails, just (re-)read the file */ + if (stat(path, &st) == 0) { + /* Don't re-read file if file is unchanged */ + if (last_read == st.st_mtime) { + err = 0; + goto errout; + } + } + + if (!(fd = fopen(path, "re"))) { + err = -nl_syserr2nlerr(errno); + goto errout; + } + + clear_hashtable(); + + while (fgets(buf, sizeof(buf), fd)) { + uint32_t classid; + char *ptr, *tok; + + /* ignore comments and empty lines */ + if (*buf == '#' || *buf == '\n' || *buf == '\r') + continue; + + /* token 1 */ + if (!(tok = strtok_r(buf, " \t", &ptr))) { + err = -NLE_INVAL; + goto errout_close; + } + + if ((err = rtnl_tc_str2handle(tok, &classid)) < 0) + goto errout_close; + + if (!(tok = strtok_r(NULL, " \t\n\r#", &ptr))) { + err = -NLE_INVAL; + goto errout_close; + } + + if ((err = classid_map_add(classid, tok)) < 0) + goto errout_close; + } + + err = 0; + last_read = st.st_mtime; + +errout_close: + fclose(fd); +errout: + free(path); + + return err; + +} + +int rtnl_classid_generate(const char *name, uint32_t *result, uint32_t parent) +{ + static uint32_t base = 0x4000 << 16; + uint32_t classid; + char *path; + FILE *fd; + int err = 0; + + if (parent == TC_H_ROOT || parent == TC_H_INGRESS) { + do { + base += (1 << 16); + if (base == TC_H_MAJ(TC_H_ROOT)) + base = 0x4000 << 16; + } while (name_lookup(base)); + + classid = base; + } else { + classid = TC_H_MAJ(parent); + do { + if (TC_H_MIN(++classid) == TC_H_MIN(TC_H_ROOT)) + return -NLE_RANGE; + } while (name_lookup(classid)); + } + + NL_DBG(2, "Generated new classid %#x\n", classid); + + if (build_sysconf_path(&path, "classid") < 0) + return -NLE_NOMEM; + + if (!(fd = fopen(path, "ae"))) { + err = -nl_syserr2nlerr(errno); + goto errout; + } + + fprintf(fd, "%x:", TC_H_MAJ(classid) >> 16); + if (TC_H_MIN(classid)) + fprintf(fd, "%x", TC_H_MIN(classid)); + fprintf(fd, "\t\t\t%s\n", name); + + fclose(fd); + + if (classid_map_add(classid, name) < 0) { + /* + * Error adding classid map, re-read classid file is best + * option here. It is likely to fail as well but better + * than nothing, entry was added to the file already anyway. + */ + rtnl_tc_read_classid_file(); + } + + *result = classid; + err = 0; +errout: + free(path); + + return err; +} + +/** @} */ + +static void _nl_init classid_init(void) +{ + int err, i; + + for (i = 0; i < CLASSID_NAME_HT_SIZ; i++) + nl_init_list_head(&tbl_name[i]); + + if ((err = rtnl_tc_read_classid_file()) < 0) + NL_DBG(1, "Failed to read classid file: %s\n", nl_geterror(err)); +} + +static void free_map(void *map) +{ + free(((struct classid_map *)map)->name); + free(map); +} + +static void _nl_exit classid_exit(void) +{ + tdestroy(id_root, free_map); +} +/** @} */ diff --git a/libnl/lib/route/cls.c b/libnl/lib/route/cls.c new file mode 100644 index 0000000..b207a09 --- /dev/null +++ b/libnl/lib/route/cls.c @@ -0,0 +1,541 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +/** + * @ingroup tc + * @defgroup cls Classifiers + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include + +#include "nl-route.h" +#include "tc-api.h" + +/** @cond SKIP */ +struct rtnl_cls { + NL_TC_GENERIC(c); + uint16_t c_prio; + uint16_t c_protocol; +}; + +#define CLS_ATTR_PRIO (TCA_ATTR_MAX << 1) +#define CLS_ATTR_PROTOCOL (TCA_ATTR_MAX << 2) +/** @endcond */ + +static struct nl_object_ops cls_obj_ops; +static struct nl_cache_ops rtnl_cls_ops; + + +static int cls_build(struct rtnl_cls *cls, int type, int flags, + struct nl_msg **result) +{ + int err, prio, proto; + struct tcmsg *tchdr; + uint32_t required = TCA_ATTR_IFINDEX; + + if ((cls->ce_mask & required) != required) { + APPBUG("ifindex must be specified"); + return -NLE_MISSING_ATTR; + } + + err = rtnl_tc_msg_build(TC_CAST(cls), type, flags, result); + if (err < 0) + return err; + + tchdr = nlmsg_data(nlmsg_hdr(*result)); + prio = rtnl_cls_get_prio(cls); + proto = rtnl_cls_get_protocol(cls); + tchdr->tcm_info = TC_H_MAKE(prio << 16, htons(proto)); + + return 0; +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_cls *rtnl_cls_alloc(void) +{ + struct rtnl_tc *tc; + + tc = TC_CAST(nl_object_alloc(&cls_obj_ops)); + if (tc) + tc->tc_type = RTNL_TC_TYPE_CLS; + + return (struct rtnl_cls *) tc; +} + +void rtnl_cls_put(struct rtnl_cls *cls) +{ + nl_object_put((struct nl_object *) cls); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void rtnl_cls_set_prio(struct rtnl_cls *cls, uint16_t prio) +{ + cls->c_prio = prio; + cls->ce_mask |= CLS_ATTR_PRIO; +} + +uint16_t rtnl_cls_get_prio(struct rtnl_cls *cls) +{ + if (cls->ce_mask & CLS_ATTR_PRIO) + return cls->c_prio; + else + return 0; +} + +void rtnl_cls_set_protocol(struct rtnl_cls *cls, uint16_t protocol) +{ + cls->c_protocol = protocol; + cls->ce_mask |= CLS_ATTR_PROTOCOL; +} + +uint16_t rtnl_cls_get_protocol(struct rtnl_cls *cls) +{ + if (cls->ce_mask & CLS_ATTR_PROTOCOL) + return cls->c_protocol; + else + return ETH_P_ALL; +} + +/** @} */ + + +/** + * @name Addition/Modification/Deletion + * @{ + */ + +/** + * Build a netlink message requesting the addition of a classifier + * @arg cls Classifier to add + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_cls_add() with + * the exception that it will not send the message but return it int the + * provided return pointer instead. + * + * @see rtnl_cls_add() + * + * @return 0 on success or a negative error code. + */ +int rtnl_cls_build_add_request(struct rtnl_cls *cls, int flags, + struct nl_msg **result) +{ + if (!(flags & NLM_F_CREATE) && !(cls->ce_mask & CLS_ATTR_PRIO)) { + APPBUG("prio must be specified if not a new classifier"); + return -NLE_MISSING_ATTR; + } + + return cls_build(cls, RTM_NEWTFILTER, flags, result); +} + +/** + * Add/Update classifier + * @arg sk Netlink socket + * @arg cls Classifier to add/update + * @arg flags Additional netlink message flags + * + * Builds a \c RTM_NEWTFILTER netlink message requesting the addition + * of a new classifier and sends the message to the kernel. The + * configuration of the classifier is derived from the attributes of + * the specified traffic class. + * + * The following flags may be specified: + * - \c NLM_F_CREATE: Create classifier if it does not exist, + * otherwise -NLE_OBJ_NOTFOUND is returned. + * - \c NLM_F_EXCL: Return -NLE_EXISTS if a classifier with + * matching handle exists already. + * + * Existing classifiers with matching handles will be updated, unless + * the flag \c NLM_F_EXCL is specified. If no matching classifier + * exists, it will be created if the flag \c NLM_F_CREATE is set, + * otherwise the error -NLE_OBJ_NOTFOUND is returned. + * + * If the parent qdisc does not support classes, the error + * \c NLE_OPNOTSUPP is returned. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_cls_add(struct nl_sock *sk, struct rtnl_cls *cls, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_cls_build_add_request(cls, flags, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build a netlink message to change classifier attributes + * @arg cls classifier to change + * @arg flags additional netlink message flags + * @arg result Pointer to store resulting message. + * + * Builds a new netlink message requesting a change of a neigh + * attributes. The netlink message header isn't fully equipped with + * all relevant fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. + * + * @return 0 on success or a negative error code. + */ +int rtnl_cls_build_change_request(struct rtnl_cls *cls, int flags, + struct nl_msg **result) +{ + return cls_build(cls, RTM_NEWTFILTER, NLM_F_REPLACE | flags, result); +} + +/** + * Change a classifier + * @arg sk Netlink socket. + * @arg cls classifier to change + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_cls_build_change_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been processed. + * + * @return 0 on success or a negative error if an error occured. + */ +int rtnl_cls_change(struct nl_sock *sk, struct rtnl_cls *cls, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_cls_build_change_request(cls, flags, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build netlink message requesting the deletion of a classifier + * @arg cls Classifier to delete + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_cls_delete() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_cls_delete() + * + * @return 0 on success or a negative error code. + */ +int rtnl_cls_build_delete_request(struct rtnl_cls *cls, int flags, + struct nl_msg **result) +{ + uint32_t required = CLS_ATTR_PRIO; + + if ((cls->ce_mask & required) != required) { + APPBUG("prio must be specified"); + return -NLE_MISSING_ATTR; + } + + return cls_build(cls, RTM_DELTFILTER, flags, result); +} + +/** + * Delete classifier + * @arg sk Netlink socket + * @arg cls Classifier to delete + * @arg flags Additional netlink message flags + * + * Builds a \c RTM_DELTFILTER netlink message requesting the deletion + * of a classifier and sends the message to the kernel. + * + * The message is constructed out of the following attributes: + * - \c ifindex (required) + * - \c prio (required) + * - \c protocol (required) + * - \c handle (required) + * - \c parent (optional, if not specified parent equals root-qdisc) + * - \c kind (optional, must match if provided) + * + * All other classifier attributes including all class type specific + * attributes are ignored. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_cls_delete(struct nl_sock *sk, struct rtnl_cls *cls, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_cls_build_delete_request(cls, flags, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** @} */ + +/** + * @name Cache Related Functions + * @{ + */ + +/** + * Allocate a cache and fill it with all configured classifiers + * @arg sk Netlink socket + * @arg ifindex Interface index of the network device + * @arg parent Parent qdisc/traffic class class + * @arg result Pointer to store the created cache + * + * Allocates a new classifier cache and fills it with a list of all + * configured classifier attached to the specified parent qdisc/traffic + * class on the specified network device. Release the cache with + * nl_cache_free(). + * + * @return 0 on success or a negative error code. + */ +int rtnl_cls_alloc_cache(struct nl_sock *sk, int ifindex, uint32_t parent, + struct nl_cache **result) +{ + struct nl_cache * cache; + int err; + + if (!(cache = nl_cache_alloc(&rtnl_cls_ops))) + return -NLE_NOMEM; + + cache->c_iarg1 = ifindex; + cache->c_iarg2 = parent; + + if (sk && (err = nl_cache_refill(sk, cache)) < 0) { + nl_cache_free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** + * Set interface index and parent handle for classifier cache. + * @arg cache Pointer to cache + * @arg parent Parent qdisc/traffic class class + * + * Set the interface index and parent handle of a classifier cache. + * This is useful for reusing some existed classifier cache to reduce + * the overhead introduced by memory allocation. + * + * @return void. + */ +void rtnl_cls_cache_set_tc_params(struct nl_cache *cache, + int ifindex, uint32_t parent) +{ + cache->c_iarg1 = ifindex; + cache->c_iarg2 = parent; +} + +/** + * Search classifier by interface index, parent and handle + * @arg cache Classifier cache + * @arg ifindex Interface index + * @arg parent Parent + * @arg handle Handle + * + * Searches a classifier cache previously allocated with rtnl_cls_alloc_cache() + * and searches for a classifier matching the interface index, parent + * and handle. + * + * The reference counter is incremented before returning the classifier, + * therefore the reference must be given back with rtnl_cls_put() after usage. + * + * @return Classifier or NULL if no match was found. + */ +struct rtnl_cls *rtnl_cls_find_by_handle(struct nl_cache *cache, int ifindex, uint32_t parent, + uint32_t handle) +{ + struct rtnl_cls *cls; + + if (cache->c_ops != &rtnl_cls_ops) + return NULL; + + nl_list_for_each_entry(cls, &cache->c_items, ce_list) { + if ((cls->c_parent == parent) && + cls->c_ifindex == ((unsigned)ifindex) && + (cls->c_handle == handle)) { + nl_object_get((struct nl_object *) cls); + return cls; + } + } + + return NULL; +} + +/** + * Search classifier by interface index, parent and priority + * @arg cache Classifier cache + * @arg ifindex Interface index + * @arg parent Parent + * @arg prio Priority + * + * Searches a classifier cache previously allocated with rtnl_cls_alloc_cache() + * and searches for a classifier matching the interface index, parent + * and prio. + * + * The reference counter is incremented before returning the classifier, + * therefore the reference must be given back with rtnl_cls_put() after usage. + * + * @return Classifier or NULL if no match was found. + */ +struct rtnl_cls *rtnl_cls_find_by_prio(struct nl_cache *cache, int ifindex, + uint32_t parent, uint16_t prio) +{ + struct rtnl_cls *cls; + + if (cache->c_ops != &rtnl_cls_ops) + return NULL; + + nl_list_for_each_entry(cls, &cache->c_items, ce_list) { + if ((cls->c_parent == parent) && + cls->c_ifindex == ((unsigned)ifindex) && + (cls->c_prio == prio)) { + nl_object_get((struct nl_object *)cls); + return cls; + } + } + + return NULL; +} + +/** @} */ + +static void cls_dump_line(struct rtnl_tc *tc, struct nl_dump_params *p) +{ + struct rtnl_cls *cls = (struct rtnl_cls *) tc; + char buf[32]; + + nl_dump(p, " prio %u protocol %s", cls->c_prio, + nl_ether_proto2str(cls->c_protocol, buf, sizeof(buf))); +} + +static int cls_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct rtnl_cls *cls; + int err; + + if (!(cls = rtnl_cls_alloc())) + return -NLE_NOMEM; + + if ((err = rtnl_tc_msg_parse(nlh, TC_CAST(cls))) < 0) + goto errout; + + cls->c_prio = TC_H_MAJ(cls->c_info) >> 16; + if (cls->c_prio) + cls->ce_mask |= CLS_ATTR_PRIO; + cls->c_protocol = ntohs(TC_H_MIN(cls->c_info)); + if (cls->c_protocol) + cls->ce_mask |= CLS_ATTR_PROTOCOL; + + err = pp->pp_cb(OBJ_CAST(cls), pp); +errout: + rtnl_cls_put(cls); + + return err; +} + +static int cls_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + struct tcmsg tchdr = { + .tcm_family = AF_UNSPEC, + .tcm_ifindex = cache->c_iarg1, + .tcm_parent = cache->c_iarg2, + }; + + return nl_send_simple(sk, RTM_GETTFILTER, NLM_F_DUMP, &tchdr, + sizeof(tchdr)); +} + +static struct rtnl_tc_type_ops cls_ops = { + .tt_type = RTNL_TC_TYPE_CLS, + .tt_dump_prefix = "cls", + .tt_dump = { + [NL_DUMP_LINE] = cls_dump_line, + }, +}; + +static struct nl_cache_ops rtnl_cls_ops = { + .co_name = "route/cls", + .co_hdrsize = sizeof(struct tcmsg), + .co_msgtypes = { + { RTM_NEWTFILTER, NL_ACT_NEW, "new" }, + { RTM_DELTFILTER, NL_ACT_DEL, "del" }, + { RTM_GETTFILTER, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = tc_groups, + .co_request_update = cls_request_update, + .co_msg_parser = cls_msg_parser, + .co_obj_ops = &cls_obj_ops, +}; + +static struct nl_object_ops cls_obj_ops = { + .oo_name = "route/cls", + .oo_size = sizeof(struct rtnl_cls), + .oo_free_data = rtnl_tc_free_data, + .oo_clone = rtnl_tc_clone, + .oo_dump = { + [NL_DUMP_LINE] = rtnl_tc_dump_line, + [NL_DUMP_DETAILS] = rtnl_tc_dump_details, + [NL_DUMP_STATS] = rtnl_tc_dump_stats, + }, + .oo_compare = rtnl_tc_compare, + .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE), +}; + +static void _nl_init cls_init(void) +{ + rtnl_tc_type_register(&cls_ops); + nl_cache_mngt_register(&rtnl_cls_ops); +} + +static void _nl_exit cls_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_cls_ops); + rtnl_tc_type_unregister(&cls_ops); +} + +/** @} */ diff --git a/libnl/lib/route/cls/basic.c b/libnl/lib/route/cls/basic.c new file mode 100644 index 0000000..dc19256 --- /dev/null +++ b/libnl/lib/route/cls/basic.c @@ -0,0 +1,295 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2013 Thomas Graf + */ + +/** + * @ingroup cls + * @defgroup cls_basic Basic Classifier + * + * @par Introduction + * The basic classifier is the simplest form of a classifier. It does + * not have any special classification capabilities, instead it can be + * used to classify exclusively based on extended matches or to + * create a "catch-all" filter. + * + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include + +#include "tc-api.h" +#include "nl-aux-route/nl-route.h" + +struct rtnl_basic +{ + uint32_t b_target; + struct rtnl_ematch_tree * b_ematch; + int b_mask; + struct rtnl_act * b_act; +}; + +/** @cond SKIP */ +#define BASIC_ATTR_TARGET 0x001 +#define BASIC_ATTR_EMATCH 0x002 +#define BASIC_ATTR_ACTION 0x004 +/** @endcond */ + +static struct nla_policy basic_policy[TCA_BASIC_MAX+1] = { + [TCA_BASIC_CLASSID] = { .type = NLA_U32 }, + [TCA_BASIC_EMATCHES] = { .type = NLA_NESTED }, +}; + +static int basic_clone(void *_dst, void *_src) +{ + return -NLE_OPNOTSUPP; +} + +static void basic_free_data(struct rtnl_tc *tc, void *data) +{ + struct rtnl_basic *b = data; + + if (!b) + return; + + if (b->b_act) + rtnl_act_put_all(&b->b_act); + rtnl_ematch_tree_free(b->b_ematch); +} + +static int basic_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct nlattr *tb[TCA_BASIC_MAX + 1]; + struct rtnl_basic *b = data; + int err; + + err = tca_parse(tb, TCA_BASIC_MAX, tc, basic_policy); + if (err < 0) + return err; + + if (tb[TCA_BASIC_CLASSID]) { + b->b_target = nla_get_u32(tb[TCA_BASIC_CLASSID]); + b->b_mask |= BASIC_ATTR_TARGET; + } + + if (tb[TCA_BASIC_EMATCHES]) { + if ((err = rtnl_ematch_parse_attr(tb[TCA_BASIC_EMATCHES], + &b->b_ematch)) < 0) + return err; + + if (b->b_ematch) + b->b_mask |= BASIC_ATTR_EMATCH; + } + if (tb[TCA_BASIC_ACT]) { + b->b_mask |= BASIC_ATTR_ACTION; + err = rtnl_act_parse(&b->b_act, tb[TCA_BASIC_ACT]); + if (err < 0) + return err; + } + + return 0; +} + +static void basic_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_basic *b = data; + char buf[32]; + + if (!b) + return; + + if (b->b_mask & BASIC_ATTR_EMATCH) + nl_dump(p, " ematch"); + else + nl_dump(p, " match-all"); + + if (b->b_mask & BASIC_ATTR_TARGET) + nl_dump(p, " target %s", + rtnl_tc_handle2str(b->b_target, buf, sizeof(buf))); +} + +static void basic_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_basic *b = data; + + if (!b) + return; + + if (b->b_mask & BASIC_ATTR_EMATCH) { + nl_dump_line(p, " ematch "); + rtnl_ematch_tree_dump(b->b_ematch, p); + } else + nl_dump(p, "no options.\n"); +} + +static int basic_msg_fill(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) +{ + struct rtnl_basic *b = data; + + if (!b) + return 0; + + if (b->b_mask & BASIC_ATTR_TARGET) + NLA_PUT_U32(msg, TCA_BASIC_CLASSID, b->b_target); + + if (b->b_mask & BASIC_ATTR_EMATCH && + rtnl_ematch_fill_attr(msg, TCA_BASIC_EMATCHES, b->b_ematch) < 0) + goto nla_put_failure; + + if (b->b_mask & BASIC_ATTR_ACTION) { + int err; + + err = rtnl_act_fill(msg, TCA_BASIC_ACT, b->b_act); + if (err) + return err; + } + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +/** + * @name Attribute Modifications + * @{ + */ + +void rtnl_basic_set_target(struct rtnl_cls *cls, uint32_t target) +{ + struct rtnl_basic *b; + + if (!(b = rtnl_tc_data(TC_CAST(cls)))) + return; + + b->b_target = target; + b->b_mask |= BASIC_ATTR_TARGET; +} + +uint32_t rtnl_basic_get_target(struct rtnl_cls *cls) +{ + struct rtnl_basic *b; + + if (!(b = rtnl_tc_data(TC_CAST(cls)))) + return 0; + + return b->b_target; +} + +void rtnl_basic_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree) +{ + struct rtnl_basic *b; + + if (!(b = rtnl_tc_data(TC_CAST(cls)))) + return; + + if (b->b_ematch) { + rtnl_ematch_tree_free(b->b_ematch); + b->b_mask &= ~BASIC_ATTR_EMATCH; + } + + b->b_ematch = tree; + + if (tree) + b->b_mask |= BASIC_ATTR_EMATCH; +} + +struct rtnl_ematch_tree *rtnl_basic_get_ematch(struct rtnl_cls *cls) +{ + struct rtnl_basic *b; + + if (!(b = rtnl_tc_data(TC_CAST(cls)))) + return NULL; + + return b->b_ematch; +} + +int rtnl_basic_add_action(struct rtnl_cls *cls, struct rtnl_act *act) +{ + struct rtnl_basic *b; + int err; + + if (!act) + return 0; + + if (!(b = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if ((err = _rtnl_act_append_get(&b->b_act, act)) < 0) + return err; + + b->b_mask |= BASIC_ATTR_ACTION; + return 0; +} + +struct rtnl_act* rtnl_basic_get_action(struct rtnl_cls *cls) +{ + struct rtnl_basic *b; + + if (!(b = rtnl_tc_data_peek(TC_CAST(cls)))) + return NULL; + + if (!(b->b_mask & BASIC_ATTR_ACTION)) + return NULL; + + return b->b_act; +} + +int rtnl_basic_del_action(struct rtnl_cls *cls, struct rtnl_act *act) +{ + struct rtnl_basic *b; + int ret; + + if (!act) + return 0; + + if (!(b = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(b->b_mask & BASIC_ATTR_ACTION)) + return -NLE_INVAL; + ret = rtnl_act_remove(&b->b_act, act); + if (ret) + return ret; + + if (!b->b_act) + b->b_mask &= ~BASIC_ATTR_ACTION; + rtnl_act_put(act); + return 0; +} +/** @} */ + +static struct rtnl_tc_ops basic_ops = { + .to_kind = "basic", + .to_type = RTNL_TC_TYPE_CLS, + .to_size = sizeof(struct rtnl_basic), + .to_msg_parser = basic_msg_parser, + .to_clone = basic_clone, + .to_free_data = basic_free_data, + .to_msg_fill = basic_msg_fill, + .to_dump = { + [NL_DUMP_LINE] = basic_dump_line, + [NL_DUMP_DETAILS] = basic_dump_details, + }, +}; + +static void _nl_init basic_init(void) +{ + rtnl_tc_register(&basic_ops); +} + +static void _nl_exit basic_exit(void) +{ + rtnl_tc_unregister(&basic_ops); +} + +/** @} */ diff --git a/libnl/lib/route/cls/cgroup.c b/libnl/lib/route/cls/cgroup.c new file mode 100644 index 0000000..3534281 --- /dev/null +++ b/libnl/lib/route/cls/cgroup.c @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2009-2013 Thomas Graf + */ + +/** + * @ingroup cls + * @defgroup cls_cgroup Control Groups Classifier + * + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include +#include + +#include "tc-api.h" + +/** @cond SKIP */ +struct rtnl_cgroup { + struct rtnl_ematch_tree *cg_ematch; + int cg_mask; +}; + +#define CGROUP_ATTR_EMATCH 0x001 +/** @endcond */ + +static struct nla_policy cgroup_policy[TCA_CGROUP_MAX+1] = { + [TCA_CGROUP_EMATCHES] = { .type = NLA_NESTED }, +}; + +static int cgroup_clone(void *_dst, void *_src) +{ + struct rtnl_cgroup *dst = _dst, *src = _src; + + dst->cg_ematch = NULL; + + if (src->cg_ematch) { + dst->cg_ematch = rtnl_ematch_tree_clone(src->cg_ematch); + if (!dst->cg_ematch) + return -NLE_NOMEM; + } + + return 0; +} + +static void cgroup_free_data(struct rtnl_tc *tc, void *data) +{ + struct rtnl_cgroup *c = data; + + if (!c) + return; + + rtnl_ematch_tree_free(c->cg_ematch); +} + +static int cgroup_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct nlattr *tb[TCA_CGROUP_MAX + 1]; + struct rtnl_cgroup *c = data; + int err; + + err = tca_parse(tb, TCA_CGROUP_MAX, tc, cgroup_policy); + if (err < 0) + return err; + + if (tb[TCA_CGROUP_EMATCHES]) { + if ((err = rtnl_ematch_parse_attr(tb[TCA_CGROUP_EMATCHES], + &c->cg_ematch)) < 0) + return err; + c->cg_mask |= CGROUP_ATTR_EMATCH; + } + +#if 0 + TODO: + TCA_CGROUP_ACT, + TCA_CGROUP_POLICE, +#endif + + return 0; +} + +static void cgroup_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_cgroup *c = data; + + if (!c) + return; + + if (c->cg_mask & CGROUP_ATTR_EMATCH) + nl_dump(p, " ematch"); + else + nl_dump(p, " match-all"); +} + +static void cgroup_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_cgroup *c = data; + + if (!c) + return; + + if (c->cg_mask & CGROUP_ATTR_EMATCH) { + nl_dump_line(p, " ematch "); + + if (c->cg_ematch) + rtnl_ematch_tree_dump(c->cg_ematch, p); + else + nl_dump(p, ""); + } else + nl_dump(p, "no options"); +} + +static int cgroup_fill_msg(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) +{ + struct rtnl_cgroup *c = data; + + if (!c) + BUG(); + + if (!(tc->ce_mask & TCA_ATTR_HANDLE)) + return -NLE_MISSING_ATTR; + + if (c->cg_mask & CGROUP_ATTR_EMATCH) + return rtnl_ematch_fill_attr(msg, TCA_CGROUP_EMATCHES, + c->cg_ematch); + + return 0; +} + + +/** + * @name Attribute Modifications + * @{ + */ + +void rtnl_cgroup_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree) +{ + struct rtnl_cgroup *c; + + if (!(c = rtnl_tc_data(TC_CAST(cls)))) + BUG(); + + if (c->cg_ematch) { + rtnl_ematch_tree_free(c->cg_ematch); + c->cg_mask &= ~CGROUP_ATTR_EMATCH; + } + + c->cg_ematch = tree; + + if (tree) + c->cg_mask |= CGROUP_ATTR_EMATCH; +} + +struct rtnl_ematch_tree *rtnl_cgroup_get_ematch(struct rtnl_cls *cls) +{ + struct rtnl_cgroup *c; + + if (!(c = rtnl_tc_data(TC_CAST(cls)))) + BUG(); + + return c->cg_ematch; +} + +/** @} */ + +static struct rtnl_tc_ops cgroup_ops = { + .to_kind = "cgroup", + .to_type = RTNL_TC_TYPE_CLS, + .to_size = sizeof(struct rtnl_cgroup), + .to_clone = cgroup_clone, + .to_msg_parser = cgroup_msg_parser, + .to_free_data = cgroup_free_data, + .to_msg_fill = cgroup_fill_msg, + .to_dump = { + [NL_DUMP_LINE] = cgroup_dump_line, + [NL_DUMP_DETAILS] = cgroup_dump_details, + }, +}; + +static void _nl_init cgroup_init(void) +{ + rtnl_tc_register(&cgroup_ops); +} + +static void _nl_exit cgroup_exit(void) +{ + rtnl_tc_unregister(&cgroup_ops); +} + +/** @} */ diff --git a/libnl/lib/route/cls/ematch.c b/libnl/lib/route/cls/ematch.c new file mode 100644 index 0000000..6b4dfde --- /dev/null +++ b/libnl/lib/route/cls/ematch.c @@ -0,0 +1,756 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2013 Thomas Graf + */ + +/** + * @ingroup cls + * @defgroup ematch Extended Match + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include + +#include "nl-route.h" +#include "nl-aux-core/nl-core.h" + +#include "ematch_syntax.h" +#include "ematch_grammar.h" + +/** + * @name Module API + * @{ + */ + +static NL_LIST_HEAD(ematch_ops_list); + +/** + * Register ematch module + * @arg ops Module operations. + * + * This function must be called by each ematch module at initialization + * time. It registers the calling module as available module. + * + * @return 0 on success or a negative error code. + */ +int rtnl_ematch_register(struct rtnl_ematch_ops *ops) +{ + if (rtnl_ematch_lookup_ops(ops->eo_kind)) + return -NLE_EXIST; + + NL_DBG(1, "ematch module \"%s\" registered\n", ops->eo_name); + + nl_list_add_tail(&ops->eo_list, &ematch_ops_list); + + return 0; +} + +/** + * Lookup ematch module by identification number. + * @arg kind Module kind. + * + * Searches the list of registered ematch modules for match and returns it. + * + * @return Module operations or NULL if not found. + */ +struct rtnl_ematch_ops *rtnl_ematch_lookup_ops(int kind) +{ + struct rtnl_ematch_ops *ops; + + nl_list_for_each_entry(ops, &ematch_ops_list, eo_list) + if (ops->eo_kind == kind) + return ops; + + return NULL; +} + +/** + * Lookup ematch module by name + * @arg name Name of ematch module. + * + * Searches the list of registered ematch modules for a match and returns it. + * + * @return Module operations or NULL if not fuond. + */ +struct rtnl_ematch_ops *rtnl_ematch_lookup_ops_by_name(const char *name) +{ + struct rtnl_ematch_ops *ops; + + nl_list_for_each_entry(ops, &ematch_ops_list, eo_list) + if (!strcasecmp(ops->eo_name, name)) + return ops; + + return NULL; +} + +/** @} */ + +/** + * @name Match + */ + +/** + * Allocate ematch object. + * + * Allocates and initializes an ematch object. + * + * @return New ematch object or NULL. + */ +struct rtnl_ematch *rtnl_ematch_alloc(void) +{ + struct rtnl_ematch *e; + + if (!(e = calloc(1, sizeof(*e)))) + return NULL; + + NL_DBG(2, "allocated ematch %p\n", e); + + NL_INIT_LIST_HEAD(&e->e_list); + NL_INIT_LIST_HEAD(&e->e_childs); + + return e; +} + +/** + * Add ematch to the end of the parent's list of children. + * @arg parent parent ematch object + * @arg child ematch object to be added to parent + * + * The parent must be a container ematch. + */ +int rtnl_ematch_add_child(struct rtnl_ematch *parent, + struct rtnl_ematch *child) +{ + if (parent->e_kind != TCF_EM_CONTAINER) + return -NLE_OPNOTSUPP; + + NL_DBG(2, "added ematch %p \"%s\" to container %p\n", + child, child->e_ops->eo_name, parent); + + nl_list_add_tail(&child->e_list, &parent->e_childs); + + return 0; +} + +/** + * Remove ematch from the list of ematches it is linked to. + * @arg ematch ematch object + */ +void rtnl_ematch_unlink(struct rtnl_ematch *ematch) +{ + NL_DBG(2, "unlinked ematch %p from any lists\n", ematch); + + if (!nl_list_empty(&ematch->e_childs)) + NL_DBG(1, "warning: ematch %p with childs was unlinked\n", + ematch); + + nl_list_del(&ematch->e_list); + nl_init_list_head(&ematch->e_list); +} + +void rtnl_ematch_free(struct rtnl_ematch *ematch) +{ + NL_DBG(2, "freed ematch %p\n", ematch); + rtnl_ematch_unlink(ematch); + free(ematch->e_data); + free(ematch); +} + +int rtnl_ematch_set_ops(struct rtnl_ematch *ematch, struct rtnl_ematch_ops *ops) +{ + if (ematch->e_ops) + return -NLE_EXIST; + + ematch->e_ops = ops; + ematch->e_kind = ops->eo_kind; + + if (ops->eo_datalen) { + ematch->e_data = calloc(1, ops->eo_datalen); + if (!ematch->e_data) + return -NLE_NOMEM; + + ematch->e_datalen = ops->eo_datalen; + } + + return 0; +} + +int rtnl_ematch_set_kind(struct rtnl_ematch *ematch, uint16_t kind) +{ + struct rtnl_ematch_ops *ops; + + if (ematch->e_kind) + return -NLE_EXIST; + + ematch->e_kind = kind; + + if ((ops = rtnl_ematch_lookup_ops(kind))) + rtnl_ematch_set_ops(ematch, ops); + + return 0; +} + +int rtnl_ematch_set_name(struct rtnl_ematch *ematch, const char *name) +{ + struct rtnl_ematch_ops *ops; + + if (ematch->e_kind) + return -NLE_EXIST; + + if (!(ops = rtnl_ematch_lookup_ops_by_name(name))) + return -NLE_OPNOTSUPP; + + rtnl_ematch_set_ops(ematch, ops); + + return 0; +} + +void rtnl_ematch_set_flags(struct rtnl_ematch *ematch, uint16_t flags) +{ + ematch->e_flags |= flags; +} + +void rtnl_ematch_unset_flags(struct rtnl_ematch *ematch, uint16_t flags) +{ + ematch->e_flags &= ~flags; +} + +uint16_t rtnl_ematch_get_flags(struct rtnl_ematch *ematch) +{ + return ematch->e_flags; +} + +void *rtnl_ematch_data(struct rtnl_ematch *ematch) +{ + return ematch->e_data; +} + +/** @} */ + +/** + * @name Tree + */ + +/** + * Allocate ematch tree object + * @arg progid program id + */ +struct rtnl_ematch_tree *rtnl_ematch_tree_alloc(uint16_t progid) +{ + struct rtnl_ematch_tree *tree; + + if (!(tree = calloc(1, sizeof(*tree)))) + return NULL; + + NL_INIT_LIST_HEAD(&tree->et_list); + tree->et_progid = progid; + + NL_DBG(2, "allocated new ematch tree %p, progid=%u\n", tree, progid); + + return tree; +} + +static void free_ematch_list(struct nl_list_head *head) +{ + struct rtnl_ematch *pos, *next; + + nl_list_for_each_entry_safe(pos, next, head, e_list) { + if (!nl_list_empty(&pos->e_childs)) + free_ematch_list(&pos->e_childs); + rtnl_ematch_free(pos); + } +} + +/** + * Free ematch tree object + * @arg tree ematch tree object + * + * This function frees the ematch tree and all ematches attached to it. + */ +void rtnl_ematch_tree_free(struct rtnl_ematch_tree *tree) +{ + if (!tree) + return; + + free_ematch_list(&tree->et_list); + + NL_DBG(2, "Freed ematch tree %p\n", tree); + + free(tree); +} + +static int clone_ematch_list(struct nl_list_head *dst, struct nl_list_head *src) +{ + struct rtnl_ematch *new = NULL, *pos = NULL; + + nl_list_for_each_entry(pos, src, e_list) { + new = rtnl_ematch_alloc(); + if (!new) + goto nomem; + + new->e_id = pos->e_id; + new->e_kind = pos->e_kind; + new->e_flags = pos->e_flags; + new->e_index = pos->e_index; + new->e_datalen = pos->e_datalen; + + if (pos->e_ops) { + if (rtnl_ematch_set_ops(new, pos->e_ops)) + goto nomem; + } + + if (!nl_list_empty(&pos->e_childs)) { + if (clone_ematch_list(&new->e_childs, &pos->e_childs) < 0) + goto nomem; + } + nl_list_add_tail(&new->e_list, dst); + } + + return 0; + +nomem: + if (new) + free(new); + free_ematch_list(dst); + return -NLE_NOMEM; +} + +/** + * Clone ematch tree object + * @arg src ematch tree object + * + * This function clones the ematch tree and all ematches attached to it. + */ +struct rtnl_ematch_tree *rtnl_ematch_tree_clone(struct rtnl_ematch_tree *src) +{ + struct rtnl_ematch_tree *dst = NULL; + + if (!src) + return NULL; + + if (!(dst = rtnl_ematch_tree_alloc(src->et_progid))) + return NULL; + + clone_ematch_list(&dst->et_list, &src->et_list); + + return dst; +} + +/** + * Add ematch object to the end of the ematch tree + * @arg tree ematch tree object + * @arg ematch ematch object to add + */ +void rtnl_ematch_tree_add(struct rtnl_ematch_tree *tree, + struct rtnl_ematch *ematch) +{ + nl_list_add_tail(&ematch->e_list, &tree->et_list); +} + +static inline uint32_t container_ref(struct rtnl_ematch *ematch) +{ + return *((uint32_t *) rtnl_ematch_data(ematch)); +} + +static int link_tree(struct rtnl_ematch *index[], int nmatches, int pos, + struct nl_list_head *root) +{ + struct rtnl_ematch *ematch; + int i; + + for (i = pos; i < nmatches; i++) { + ematch = index[i]; + + nl_list_add_tail(&ematch->e_list, root); + + if (ematch->e_kind == TCF_EM_CONTAINER) + link_tree(index, nmatches, container_ref(ematch), + &ematch->e_childs); + + if (!(ematch->e_flags & TCF_EM_REL_MASK)) + return 0; + } + + /* Last entry in chain can't possibly have no relation */ + return -NLE_INVAL; +} + +static struct nla_policy tree_policy[TCA_EMATCH_TREE_MAX+1] = { + [TCA_EMATCH_TREE_HDR] = { .minlen=sizeof(struct tcf_ematch_tree_hdr) }, + [TCA_EMATCH_TREE_LIST] = { .type = NLA_NESTED }, +}; + +/** + * Parse ematch netlink attributes + * + * @return 0 on success or a negative error code. + */ +int rtnl_ematch_parse_attr(struct nlattr *attr, struct rtnl_ematch_tree **result) +{ + struct nlattr *a, *tb[TCA_EMATCH_TREE_MAX+1]; + struct tcf_ematch_tree_hdr *thdr; + struct rtnl_ematch_tree *tree; + struct rtnl_ematch **index; + int nmatches = 0, err, remaining; + + NL_DBG(2, "Parsing attribute %p as ematch tree\n", attr); + + err = nla_parse_nested(tb, TCA_EMATCH_TREE_MAX, attr, tree_policy); + if (err < 0) + return err; + + if (!tb[TCA_EMATCH_TREE_HDR]) + return -NLE_MISSING_ATTR; + + thdr = nla_data(tb[TCA_EMATCH_TREE_HDR]); + + /* Ignore empty trees */ + if (thdr->nmatches == 0) { + NL_DBG(2, "Ignoring empty ematch configuration\n"); + return 0; + } + + if (!tb[TCA_EMATCH_TREE_LIST]) + return -NLE_MISSING_ATTR; + + NL_DBG(2, "ematch tree found with nmatches=%u, progid=%u\n", + thdr->nmatches, thdr->progid); + + /* + * Do some basic sanity checking since we will allocate + * index[thdr->nmatches]. Calculate how many ematch headers fit into + * the provided data and make sure nmatches does not exceed it. + */ + if (thdr->nmatches > (nla_len(tb[TCA_EMATCH_TREE_LIST]) / + nla_total_size(sizeof(struct tcf_ematch_hdr)))) + return -NLE_INVAL; + + if (!(index = calloc(thdr->nmatches, sizeof(struct rtnl_ematch *)))) + return -NLE_NOMEM; + + if (!(tree = rtnl_ematch_tree_alloc(thdr->progid))) { + err = -NLE_NOMEM; + goto errout; + } + + nla_for_each_nested(a, tb[TCA_EMATCH_TREE_LIST], remaining) { + struct rtnl_ematch_ops *ops; + struct tcf_ematch_hdr *hdr; + struct rtnl_ematch *ematch; + void *data; + size_t len; + + NL_DBG(3, "parsing ematch attribute %d, len=%u\n", + nmatches+1, nla_len(a)); + + if (_nla_len(a) < sizeof(*hdr)) { + err = -NLE_INVAL; + goto errout; + } + + /* Quit as soon as we've parsed more matches than expected */ + if (nmatches >= thdr->nmatches) { + err = -NLE_RANGE; + goto errout; + } + + hdr = nla_data(a); + data = (char *) nla_data(a) + NLA_ALIGN(sizeof(*hdr)); + len = nla_len(a) - NLA_ALIGN(sizeof(*hdr)); + + NL_DBG(3, "ematch attribute matchid=%u, kind=%u, flags=%u\n", + hdr->matchid, hdr->kind, hdr->flags); + + /* + * Container matches contain a reference to another sequence + * of matches. Ensure that the reference is within boundries. + */ + if (hdr->kind == TCF_EM_CONTAINER && + *((uint32_t *) data) >= thdr->nmatches) { + err = -NLE_INVAL; + goto errout; + } + + if (!(ematch = rtnl_ematch_alloc())) { + err = -NLE_NOMEM; + goto errout; + } + + ematch->e_id = hdr->matchid; + ematch->e_kind = hdr->kind; + ematch->e_flags = hdr->flags; + + if ((ops = rtnl_ematch_lookup_ops(hdr->kind))) { + if (ops->eo_minlen && len < ops->eo_minlen) { + rtnl_ematch_free(ematch); + err = -NLE_INVAL; + goto errout; + } + + rtnl_ematch_set_ops(ematch, ops); + + if (ops->eo_parse && + (err = ops->eo_parse(ematch, data, len)) < 0) { + rtnl_ematch_free(ematch); + goto errout; + } + } + + NL_DBG(3, "index[%d] = %p\n", nmatches, ematch); + index[nmatches++] = ematch; + } + + if (nmatches != thdr->nmatches) { + err = -NLE_INVAL; + goto errout; + } + + err = link_tree(index, nmatches, 0, &tree->et_list); + if (err < 0) + goto errout; + + free(index); + *result = tree; + + return 0; + +errout: + rtnl_ematch_tree_free(tree); + free(index); + return err; +} + +static void dump_ematch_sequence(struct nl_list_head *head, + struct nl_dump_params *p) +{ + struct rtnl_ematch *match; + + nl_list_for_each_entry(match, head, e_list) { + if (match->e_flags & TCF_EM_INVERT) + nl_dump(p, "!"); + + if (match->e_kind == TCF_EM_CONTAINER) { + nl_dump(p, "("); + dump_ematch_sequence(&match->e_childs, p); + nl_dump(p, ")"); + } else if (!match->e_ops) { + nl_dump(p, "[unknown ematch %d]", match->e_kind); + } else { + if (match->e_ops->eo_dump) + match->e_ops->eo_dump(match, p); + else + nl_dump(p, "[data]"); + } + + switch (match->e_flags & TCF_EM_REL_MASK) { + case TCF_EM_REL_AND: + nl_dump(p, " AND "); + break; + case TCF_EM_REL_OR: + nl_dump(p, " OR "); + break; + default: + /* end of first level ematch sequence */ + return; + } + } +} + +void rtnl_ematch_tree_dump(struct rtnl_ematch_tree *tree, + struct nl_dump_params *p) +{ + if (!tree) + BUG(); + + dump_ematch_sequence(&tree->et_list, p); + nl_dump(p, "\n"); +} + +static int update_container_index(struct nl_list_head *list, int *index) +{ + struct rtnl_ematch *e; + + nl_list_for_each_entry(e, list, e_list) + e->e_index = (*index)++; + + nl_list_for_each_entry(e, list, e_list) { + if (e->e_kind == TCF_EM_CONTAINER) { + int err; + + if (nl_list_empty(&e->e_childs)) + return -NLE_OBJ_NOTFOUND; + + *((uint32_t *) e->e_data) = *index; + + err = update_container_index(&e->e_childs, index); + if (err < 0) + return err; + } + } + + return 0; +} + +static int fill_ematch_sequence(struct nl_msg *msg, struct nl_list_head *list) +{ + struct rtnl_ematch *e; + + nl_list_for_each_entry(e, list, e_list) { + struct tcf_ematch_hdr match = { + .matchid = e->e_id, + .kind = e->e_kind, + .flags = e->e_flags, + }; + struct nlattr *attr; + int err = 0; + + if (!(attr = nla_nest_start(msg, e->e_index + 1))) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &match, sizeof(match), 0) < 0) + return -NLE_NOMEM; + + if (e->e_ops->eo_fill) + err = e->e_ops->eo_fill(e, msg); + else if (e->e_flags & TCF_EM_SIMPLE) + err = nlmsg_append(msg, e->e_data, 4, 0); + else if (e->e_datalen > 0) + err = nlmsg_append(msg, e->e_data, e->e_datalen, 0); + + NL_DBG(3, "msg %p: added ematch [%d] id=%d kind=%d flags=%d\n", + msg, e->e_index, match.matchid, match.kind, match.flags); + + if (err < 0) + return -NLE_NOMEM; + + nla_nest_end(msg, attr); + } + + nl_list_for_each_entry(e, list, e_list) { + if (e->e_kind == TCF_EM_CONTAINER && + fill_ematch_sequence(msg, &e->e_childs) < 0) + return -NLE_NOMEM; + } + + return 0; +} + +int rtnl_ematch_fill_attr(struct nl_msg *msg, int attrid, + struct rtnl_ematch_tree *tree) +{ + struct tcf_ematch_tree_hdr thdr = { + .progid = tree->et_progid, + }; + struct nlattr *list, *topattr; + int err, index = 0; + + /* Assign index number to each ematch to allow for references + * to be made while constructing the sequence of matches. */ + err = update_container_index(&tree->et_list, &index); + if (err < 0) + return err; + + if (!(topattr = nla_nest_start(msg, attrid))) + goto nla_put_failure; + + thdr.nmatches = index; + NLA_PUT(msg, TCA_EMATCH_TREE_HDR, sizeof(thdr), &thdr); + + if (!(list = nla_nest_start(msg, TCA_EMATCH_TREE_LIST))) + goto nla_put_failure; + + if (fill_ematch_sequence(msg, &tree->et_list) < 0) + goto nla_put_failure; + + nla_nest_end(msg, list); + + nla_nest_end(msg, topattr); + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +/** @} */ + +extern int ematch_parse(void *, char **, struct nl_list_head *); + +int rtnl_ematch_parse_expr(const char *expr, char **errp, + struct rtnl_ematch_tree **result) +{ + struct rtnl_ematch_tree *tree; + YY_BUFFER_STATE buf = NULL; + yyscan_t scanner = NULL; + int err; + + NL_DBG(2, "Parsing ematch expression \"%s\"\n", expr); + + if (!(tree = rtnl_ematch_tree_alloc(RTNL_EMATCH_PROGID))) + return -NLE_FAILURE; + + if (ematch_lex_init(&scanner) < 0) { + err = -NLE_FAILURE; + goto errout; + } + + buf = ematch__scan_string(expr, scanner); + + if (ematch_parse(scanner, errp, &tree->et_list) != 0) { + ematch__delete_buffer(buf, scanner); + err = -NLE_PARSE_ERR; + goto errout; + } + + ematch_lex_destroy(scanner); + *result = tree; + + return 0; + +errout: + if (scanner) + ematch_lex_destroy(scanner); + + rtnl_ematch_tree_free(tree); + + return err; +} + +static const char *layer_txt[] = { + [TCF_LAYER_LINK] = "eth", + [TCF_LAYER_NETWORK] = "ip", + [TCF_LAYER_TRANSPORT] = "tcp", +}; + +char *rtnl_ematch_offset2txt(uint8_t layer, uint16_t offset, char *buf, size_t len) +{ + snprintf(buf, len, "%s+%u", + (layer <= TCF_LAYER_MAX) ? layer_txt[layer] : "?", + offset); + + return buf; +} + +static const char *operand_txt[] = { + [TCF_EM_OPND_EQ] = "=", + [TCF_EM_OPND_LT] = "<", + [TCF_EM_OPND_GT] = ">", +}; + +char *rtnl_ematch_opnd2txt(uint8_t opnd, char *buf, size_t len) +{ + snprintf(buf, len, "%s", + opnd < ARRAY_SIZE(operand_txt) ? operand_txt[opnd] : "?"); + + return buf; +} + +/** @} */ diff --git a/libnl/lib/route/cls/ematch/cmp.c b/libnl/lib/route/cls/ematch/cmp.c new file mode 100644 index 0000000..9feb2e5 --- /dev/null +++ b/libnl/lib/route/cls/ematch/cmp.c @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2013 Thomas Graf + */ + +/** + * @ingroup ematch + * @defgroup em_cmp Simple packet data comparison + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +void rtnl_ematch_cmp_set(struct rtnl_ematch *e, struct tcf_em_cmp *cfg) +{ + memcpy(rtnl_ematch_data(e), cfg, sizeof(*cfg)); +} + +struct tcf_em_cmp *rtnl_ematch_cmp_get(struct rtnl_ematch *e) +{ + return rtnl_ematch_data(e); +} + +static int cmp_parse(struct rtnl_ematch *e, void *data, size_t len) +{ + memcpy(rtnl_ematch_data(e), data, len); + + return 0; +} + +static const char *align_txt[] = { + [TCF_EM_ALIGN_U8] = "u8", + [TCF_EM_ALIGN_U16] = "u16", + [TCF_EM_ALIGN_U32] = "u32" +}; + +static const char *layer_txt[] = { + [TCF_LAYER_LINK] = "eth", + [TCF_LAYER_NETWORK] = "ip", + [TCF_LAYER_TRANSPORT] = "tcp" +}; + +static const char *operand_txt[] = { + [TCF_EM_OPND_EQ] = "=", + [TCF_EM_OPND_LT] = "<", + [TCF_EM_OPND_GT] = ">", +}; + +static void cmp_dump(struct rtnl_ematch *e, struct nl_dump_params *p) +{ + struct tcf_em_cmp *cmp = rtnl_ematch_data(e); + + if (cmp->flags & TCF_EM_CMP_TRANS) + nl_dump(p, "ntoh%c(", (cmp->align == TCF_EM_ALIGN_U32) ? 'l' : 's'); + + nl_dump(p, "%s at %s+%u", + align_txt[cmp->align], layer_txt[cmp->layer], cmp->off); + + if (cmp->mask) + nl_dump(p, " & 0x%x", cmp->mask); + + if (cmp->flags & TCF_EM_CMP_TRANS) + nl_dump(p, ")"); + + nl_dump(p, " %s %u", operand_txt[cmp->opnd], cmp->val); +} + +static struct rtnl_ematch_ops cmp_ops = { + .eo_kind = TCF_EM_CMP, + .eo_name = "cmp", + .eo_minlen = sizeof(struct tcf_em_cmp), + .eo_datalen = sizeof(struct tcf_em_cmp), + .eo_parse = cmp_parse, + .eo_dump = cmp_dump, +}; + +static void _nl_init cmp_init(void) +{ + rtnl_ematch_register(&cmp_ops); +} + +/** @} */ diff --git a/libnl/lib/route/cls/ematch/container.c b/libnl/lib/route/cls/ematch/container.c new file mode 100644 index 0000000..d7e5a3a --- /dev/null +++ b/libnl/lib/route/cls/ematch/container.c @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2013 Thomas Graf + */ + +#include "nl-default.h" + +#include +#include + +#include "nl-route.h" + +static int container_parse(struct rtnl_ematch *e, void *data, size_t len) +{ + /* + The kernel may provide more than 4 bytes of data in the future and we want + older libnl versions to be ok with that. We want interfaces to be growable + so we only ever enforce a minimum data length and copy as much as we are + aware of. Thomas Graf. + */ + memcpy(e->e_data, data, sizeof(uint32_t)); + + return 0; +} + +static int container_fill(struct rtnl_ematch *e, struct nl_msg *msg) +{ + return nlmsg_append(msg, e->e_data, sizeof(uint32_t), 0); +} + +static struct rtnl_ematch_ops container_ops = { + .eo_kind = TCF_EM_CONTAINER, + .eo_name = "container", + .eo_minlen = sizeof(uint32_t), + .eo_datalen = sizeof(uint32_t), + .eo_parse = container_parse, + .eo_fill = container_fill, +}; + +static void _nl_init container_init(void) +{ + rtnl_ematch_register(&container_ops); +} diff --git a/libnl/lib/route/cls/ematch/meta.c b/libnl/lib/route/cls/ematch/meta.c new file mode 100644 index 0000000..2c884dc --- /dev/null +++ b/libnl/lib/route/cls/ematch/meta.c @@ -0,0 +1,333 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2013 Thomas Graf + */ + +/** + * @ingroup ematch + * @defgroup em_meta Metadata Match + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +#include "nl-priv-dynamic-core/nl-core.h" + +struct rtnl_meta_value +{ + uint8_t mv_type; + uint8_t mv_shift; + uint16_t mv_id; + size_t mv_len; +}; + +struct meta_data +{ + struct rtnl_meta_value * left; + struct rtnl_meta_value * right; + uint8_t opnd; +}; + +static struct rtnl_meta_value *meta_alloc(uint8_t type, uint16_t id, + uint8_t shift, void *data, + size_t len) +{ + struct rtnl_meta_value *value; + + if (!(value = calloc(1, sizeof(*value) + len))) + return NULL; + + value->mv_type = type; + value->mv_id = id; + value->mv_shift = shift; + value->mv_len = len; + + if (len) + memcpy(value + 1, data, len); + + return value; +} + +struct rtnl_meta_value *rtnl_meta_value_alloc_int(uint64_t value) +{ + return meta_alloc(TCF_META_TYPE_INT, TCF_META_ID_VALUE, 0, &value, 8); +} + +struct rtnl_meta_value *rtnl_meta_value_alloc_var(void *data, size_t len) +{ + return meta_alloc(TCF_META_TYPE_VAR, TCF_META_ID_VALUE, 0, data, len); +} + +struct rtnl_meta_value *rtnl_meta_value_alloc_id(uint8_t type, uint16_t id, + uint8_t shift, uint64_t mask) +{ + size_t masklen = 0; + + if (id > TCF_META_ID_MAX) + return NULL; + + if (mask) { + if (type == TCF_META_TYPE_VAR) + return NULL; + + masklen = 8; + } + + return meta_alloc(type, id, shift, &mask, masklen); +} + +void rtnl_meta_value_put(struct rtnl_meta_value *mv) +{ + free(mv); +} + +void rtnl_ematch_meta_set_lvalue(struct rtnl_ematch *e, struct rtnl_meta_value *v) +{ + struct meta_data *m = rtnl_ematch_data(e); + m->left = v; +} + +void rtnl_ematch_meta_set_rvalue(struct rtnl_ematch *e, struct rtnl_meta_value *v) +{ + struct meta_data *m = rtnl_ematch_data(e); + m->right = v; +} + +void rtnl_ematch_meta_set_operand(struct rtnl_ematch *e, uint8_t opnd) +{ + struct meta_data *m = rtnl_ematch_data(e); + m->opnd = opnd; +} + +static struct nla_policy meta_policy[TCA_EM_META_MAX+1] = { + [TCA_EM_META_HDR] = { .minlen = sizeof(struct tcf_meta_hdr) }, + [TCA_EM_META_LVALUE] = { .minlen = 1, }, + [TCA_EM_META_RVALUE] = { .minlen = 1, }, +}; + +static int meta_parse(struct rtnl_ematch *e, void *data, size_t len) +{ + struct meta_data *m = rtnl_ematch_data(e); + struct nlattr *tb[TCA_EM_META_MAX+1]; + struct rtnl_meta_value *v; + struct tcf_meta_hdr *hdr; + void *vdata = NULL; + size_t vlen = 0; + int err; + + if ((err = nla_parse(tb, TCA_EM_META_MAX, data, len, meta_policy)) < 0) + return err; + + if (!tb[TCA_EM_META_HDR]) + return -NLE_MISSING_ATTR; + + hdr = nla_data(tb[TCA_EM_META_HDR]); + + if (tb[TCA_EM_META_LVALUE]) { + vdata = nla_data(tb[TCA_EM_META_LVALUE]); + vlen = nla_len(tb[TCA_EM_META_LVALUE]); + } + + v = meta_alloc(TCF_META_TYPE(hdr->left.kind), + TCF_META_ID(hdr->left.kind), + hdr->left.shift, vdata, vlen); + if (!v) + return -NLE_NOMEM; + + m->left = v; + + vlen = 0; + if (tb[TCA_EM_META_RVALUE]) { + vdata = nla_data(tb[TCA_EM_META_RVALUE]); + vlen = nla_len(tb[TCA_EM_META_RVALUE]); + } + + v = meta_alloc(TCF_META_TYPE(hdr->right.kind), + TCF_META_ID(hdr->right.kind), + hdr->right.shift, vdata, vlen); + if (!v) { + rtnl_meta_value_put(m->left); + return -NLE_NOMEM; + } + + m->right = v; + m->opnd = hdr->left.op; + + return 0; +} + +static const struct trans_tbl meta_int[] = { + __ADD(TCF_META_ID_RANDOM, random), + __ADD(TCF_META_ID_LOADAVG_0, loadavg_0), + __ADD(TCF_META_ID_LOADAVG_1, loadavg_1), + __ADD(TCF_META_ID_LOADAVG_2, loadavg_2), + __ADD(TCF_META_ID_DEV, dev), + __ADD(TCF_META_ID_PRIORITY, prio), + __ADD(TCF_META_ID_PROTOCOL, proto), + __ADD(TCF_META_ID_PKTTYPE, pkttype), + __ADD(TCF_META_ID_PKTLEN, pktlen), + __ADD(TCF_META_ID_DATALEN, datalen), + __ADD(TCF_META_ID_MACLEN, maclen), + __ADD(TCF_META_ID_NFMARK, mark), + __ADD(TCF_META_ID_TCINDEX, tcindex), + __ADD(TCF_META_ID_RTCLASSID, rtclassid), + __ADD(TCF_META_ID_RTIIF, rtiif), + __ADD(TCF_META_ID_SK_FAMILY, sk_family), + __ADD(TCF_META_ID_SK_STATE, sk_state), + __ADD(TCF_META_ID_SK_REUSE, sk_reuse), + __ADD(TCF_META_ID_SK_REFCNT, sk_refcnt), + __ADD(TCF_META_ID_SK_RCVBUF, sk_rcvbuf), + __ADD(TCF_META_ID_SK_SNDBUF, sk_sndbuf), + __ADD(TCF_META_ID_SK_SHUTDOWN, sk_sutdown), + __ADD(TCF_META_ID_SK_PROTO, sk_proto), + __ADD(TCF_META_ID_SK_TYPE, sk_type), + __ADD(TCF_META_ID_SK_RMEM_ALLOC, sk_rmem_alloc), + __ADD(TCF_META_ID_SK_WMEM_ALLOC, sk_wmem_alloc), + __ADD(TCF_META_ID_SK_WMEM_QUEUED, sk_wmem_queued), + __ADD(TCF_META_ID_SK_RCV_QLEN, sk_rcv_qlen), + __ADD(TCF_META_ID_SK_SND_QLEN, sk_snd_qlen), + __ADD(TCF_META_ID_SK_ERR_QLEN, sk_err_qlen), + __ADD(TCF_META_ID_SK_FORWARD_ALLOCS, sk_forward_allocs), + __ADD(TCF_META_ID_SK_ALLOCS, sk_allocs), + __ADD(__TCF_META_ID_SK_ROUTE_CAPS, sk_route_caps), + __ADD(TCF_META_ID_SK_HASH, sk_hash), + __ADD(TCF_META_ID_SK_LINGERTIME, sk_lingertime), + __ADD(TCF_META_ID_SK_ACK_BACKLOG, sk_ack_backlog), + __ADD(TCF_META_ID_SK_MAX_ACK_BACKLOG, sk_max_ack_backlog), + __ADD(TCF_META_ID_SK_PRIO, sk_prio), + __ADD(TCF_META_ID_SK_RCVLOWAT, sk_rcvlowat), + __ADD(TCF_META_ID_SK_RCVTIMEO, sk_rcvtimeo), + __ADD(TCF_META_ID_SK_SNDTIMEO, sk_sndtimeo), + __ADD(TCF_META_ID_SK_SENDMSG_OFF, sk_sendmsg_off), + __ADD(TCF_META_ID_SK_WRITE_PENDING, sk_write_pending), + __ADD(TCF_META_ID_VLAN_TAG, vlan), + __ADD(TCF_META_ID_RXHASH, rxhash), +}; + +static char *int_id2str(int id, char *buf, size_t size) +{ + return __type2str(id, buf, size, meta_int, ARRAY_SIZE(meta_int)); +} + +static const struct trans_tbl meta_var[] = { + __ADD(TCF_META_ID_DEV,devname), + __ADD(TCF_META_ID_SK_BOUND_IF,sk_bound_if), +}; + +static char *var_id2str(int id, char *buf, size_t size) +{ + return __type2str(id, buf, size, meta_var, ARRAY_SIZE(meta_var)); +} + +static void dump_value(struct rtnl_meta_value *v, struct nl_dump_params *p) +{ + char buf[32]; + + switch (v->mv_type) { + case TCF_META_TYPE_INT: + if (v->mv_id == TCF_META_ID_VALUE) { + nl_dump(p, "%u", + *(uint32_t *) (v + 1)); + } else { + nl_dump(p, "%s", + int_id2str(v->mv_id, buf, sizeof(buf))); + + if (v->mv_shift) + nl_dump(p, " >> %u", v->mv_shift); + + if (v->mv_len == 4) + nl_dump(p, " & %#lx", (long unsigned) *(uint32_t *) (v + 1)); + else if (v->mv_len == 8) + nl_dump(p, " & %#llx", (long long unsigned) (*(uint64_t *) (v + 1))); + } + break; + + case TCF_META_TYPE_VAR: + if (v->mv_id == TCF_META_ID_VALUE) { + nl_dump(p, "%s", (char *) (v + 1)); + } else { + nl_dump(p, "%s", + var_id2str(v->mv_id, buf, sizeof(buf))); + + if (v->mv_shift) + nl_dump(p, " >> %u", v->mv_shift); + } + break; + } +} + +static void meta_dump(struct rtnl_ematch *e, struct nl_dump_params *p) +{ + struct meta_data *m = rtnl_ematch_data(e); + char buf[32]; + + nl_dump(p, "meta("); + dump_value(m->left, p); + + nl_dump(p, " %s ", rtnl_ematch_opnd2txt(m->opnd, buf, sizeof(buf))); + + dump_value(m->right, p); + nl_dump(p, ")"); +} + +static int meta_fill(struct rtnl_ematch *e, struct nl_msg *msg) +{ + struct meta_data *m = rtnl_ematch_data(e); + struct tcf_meta_hdr hdr; + + if (!(m->left && m->right)) + return -NLE_MISSING_ATTR; + + memset(&hdr, 0, sizeof(hdr)); + hdr.left.kind = (m->left->mv_type << 12) & TCF_META_TYPE_MASK; + hdr.left.kind |= m->left->mv_id & TCF_META_ID_MASK; + hdr.left.shift = m->left->mv_shift; + hdr.left.op = m->opnd; + hdr.right.kind = (m->right->mv_type << 12) & TCF_META_TYPE_MASK; + hdr.right.kind |= m->right->mv_id & TCF_META_ID_MASK; + + NLA_PUT(msg, TCA_EM_META_HDR, sizeof(hdr), &hdr); + + if (m->left->mv_len) + NLA_PUT(msg, TCA_EM_META_LVALUE, m->left->mv_len, (m->left + 1)); + + if (m->right->mv_len) + NLA_PUT(msg, TCA_EM_META_RVALUE, m->right->mv_len, (m->right + 1)); + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +static void meta_free(struct rtnl_ematch *e) +{ + struct meta_data *m = rtnl_ematch_data(e); + free(m->left); + free(m->right); +} + +static struct rtnl_ematch_ops meta_ops = { + .eo_kind = TCF_EM_META, + .eo_name = "meta", + .eo_minlen = sizeof(struct tcf_meta_hdr), + .eo_datalen = sizeof(struct meta_data), + .eo_parse = meta_parse, + .eo_dump = meta_dump, + .eo_fill = meta_fill, + .eo_free = meta_free, +}; + +static void _nl_init meta_init(void) +{ + rtnl_ematch_register(&meta_ops); +} + +/** @} */ diff --git a/libnl/lib/route/cls/ematch/nbyte.c b/libnl/lib/route/cls/ematch/nbyte.c new file mode 100644 index 0000000..d0ab84b --- /dev/null +++ b/libnl/lib/route/cls/ematch/nbyte.c @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2013 Thomas Graf + */ + +/** + * @ingroup ematch + * @defgroup em_nbyte N-Byte Comparison + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +struct nbyte_data +{ + struct tcf_em_nbyte cfg; + uint8_t * pattern; +}; + +void rtnl_ematch_nbyte_set_offset(struct rtnl_ematch *e, uint8_t layer, + uint16_t offset) +{ + struct nbyte_data *n = rtnl_ematch_data(e); + n->cfg.off = offset; + n->cfg.layer = layer; +} + +uint16_t rtnl_ematch_nbyte_get_offset(struct rtnl_ematch *e) +{ + return ((struct nbyte_data *) rtnl_ematch_data(e))->cfg.off; +} + +uint8_t rtnl_ematch_nbyte_get_layer(struct rtnl_ematch *e) +{ + return ((struct nbyte_data *) rtnl_ematch_data(e))->cfg.layer; +} + +void rtnl_ematch_nbyte_set_pattern(struct rtnl_ematch *e, + uint8_t *pattern, size_t len) +{ + struct nbyte_data *n = rtnl_ematch_data(e); + + if (n->pattern) + free(n->pattern); + + n->pattern = pattern; + n->cfg.len = len; +} + +uint8_t *rtnl_ematch_nbyte_get_pattern(struct rtnl_ematch *e) +{ + return ((struct nbyte_data *) rtnl_ematch_data(e))->pattern; +} + +size_t rtnl_ematch_nbyte_get_len(struct rtnl_ematch *e) +{ + return ((struct nbyte_data *) rtnl_ematch_data(e))->cfg.len; +} + +static const char *layer_txt(struct tcf_em_nbyte *nbyte) +{ + switch (nbyte->layer) { + case TCF_LAYER_LINK: + return "link"; + case TCF_LAYER_NETWORK: + return "net"; + case TCF_LAYER_TRANSPORT: + return "trans"; + default: + return "?"; + } +} + +static int nbyte_parse(struct rtnl_ematch *e, void *data, size_t len) +{ + struct nbyte_data *n = rtnl_ematch_data(e); + size_t hdrlen = sizeof(struct tcf_em_nbyte); + size_t plen = len - hdrlen; + + memcpy(&n->cfg, data, hdrlen); + if (plen > 0) { + if (!(n->pattern = calloc(1, plen))) + return -NLE_NOMEM; + + memcpy(n->pattern, (char *) data + hdrlen, plen); + } + + return 0; +} + +static void nbyte_dump(struct rtnl_ematch *e, struct nl_dump_params *p) +{ + struct nbyte_data *n = rtnl_ematch_data(e); + int i; + + nl_dump(p, "pattern(%u:[", n->cfg.len); + + for (i = 0; i < n->cfg.len; i++) { + nl_dump(p, "%02x", n->pattern[i]); + if (i+1 < n->cfg.len) + nl_dump(p, " "); + } + + nl_dump(p, "] at %s+%u)", layer_txt(&n->cfg), n->cfg.off); +} + +static void nbyte_free(struct rtnl_ematch *e) +{ + struct nbyte_data *n = rtnl_ematch_data(e); + free(n->pattern); +} + +static struct rtnl_ematch_ops nbyte_ops = { + .eo_kind = TCF_EM_NBYTE, + .eo_name = "nbyte", + .eo_minlen = sizeof(struct tcf_em_nbyte), + .eo_datalen = sizeof(struct nbyte_data), + .eo_parse = nbyte_parse, + .eo_dump = nbyte_dump, + .eo_free = nbyte_free, +}; + +static void _nl_init nbyte_init(void) +{ + rtnl_ematch_register(&nbyte_ops); +} + +/** @} */ diff --git a/libnl/lib/route/cls/ematch/text.c b/libnl/lib/route/cls/ematch/text.c new file mode 100644 index 0000000..d7a233f --- /dev/null +++ b/libnl/lib/route/cls/ematch/text.c @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2013 Thomas Graf + */ + +/** + * @ingroup ematch + * @defgroup em_text Text Search + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +struct text_data +{ + struct tcf_em_text cfg; + char * pattern; +}; + +void rtnl_ematch_text_set_from(struct rtnl_ematch *e, uint8_t layer, + uint16_t offset) +{ + struct text_data *t = rtnl_ematch_data(e); + t->cfg.from_offset = offset; + t->cfg.from_layer = layer; +} + +uint16_t rtnl_ematch_text_get_from_offset(struct rtnl_ematch *e) +{ + return ((struct text_data *) rtnl_ematch_data(e))->cfg.from_offset; +} + +uint8_t rtnl_ematch_text_get_from_layer(struct rtnl_ematch *e) +{ + return ((struct text_data *) rtnl_ematch_data(e))->cfg.from_layer; +} + +void rtnl_ematch_text_set_to(struct rtnl_ematch *e, uint8_t layer, + uint16_t offset) +{ + struct text_data *t = rtnl_ematch_data(e); + t->cfg.to_offset = offset; + t->cfg.to_layer = layer; +} + +uint16_t rtnl_ematch_text_get_to_offset(struct rtnl_ematch *e) +{ + return ((struct text_data *) rtnl_ematch_data(e))->cfg.to_offset; +} + +uint8_t rtnl_ematch_text_get_to_layer(struct rtnl_ematch *e) +{ + return ((struct text_data *) rtnl_ematch_data(e))->cfg.to_layer; +} + +void rtnl_ematch_text_set_pattern(struct rtnl_ematch *e, + char *pattern, size_t len) +{ + struct text_data *t = rtnl_ematch_data(e); + + if (t->pattern) + free(t->pattern); + + t->pattern = pattern; + t->cfg.pattern_len = len; +} + +char *rtnl_ematch_text_get_pattern(struct rtnl_ematch *e) +{ + return ((struct text_data *) rtnl_ematch_data(e))->pattern; +} + +size_t rtnl_ematch_text_get_len(struct rtnl_ematch *e) +{ + return ((struct text_data *) rtnl_ematch_data(e))->cfg.pattern_len; +} + +void rtnl_ematch_text_set_algo(struct rtnl_ematch *e, const char *algo) +{ + struct text_data *t = rtnl_ematch_data(e); + + _nl_strncpy_trunc(t->cfg.algo, algo, sizeof(t->cfg.algo)); +} + +char *rtnl_ematch_text_get_algo(struct rtnl_ematch *e) +{ + struct text_data *t = rtnl_ematch_data(e); + + return t->cfg.algo[0] ? t->cfg.algo : NULL; +} + +static int text_parse(struct rtnl_ematch *e, void *data, size_t len) +{ + struct text_data *t = rtnl_ematch_data(e); + size_t hdrlen = sizeof(struct tcf_em_text); + size_t plen = len - hdrlen; + + memcpy(&t->cfg, data, hdrlen); + + if (t->cfg.pattern_len > plen) + return -NLE_INVAL; + + if (t->cfg.pattern_len > 0) { + if (!(t->pattern = calloc(1, t->cfg.pattern_len))) + return -NLE_NOMEM; + + memcpy(t->pattern, (char *) data + hdrlen, t->cfg.pattern_len); + } + + return 0; +} + +static void text_dump(struct rtnl_ematch *e, struct nl_dump_params *p) +{ + struct text_data *t = rtnl_ematch_data(e); + char buf[64]; + + nl_dump(p, "text(%s \"%s\"", + t->cfg.algo[0] ? t->cfg.algo : "no-algo", + t->pattern ? t->pattern : "no-pattern"); + + if (t->cfg.from_layer || t->cfg.from_offset) { + nl_dump(p, " from %s", + rtnl_ematch_offset2txt(t->cfg.from_layer, + t->cfg.from_offset, + buf, sizeof(buf))); + } + + if (t->cfg.to_layer || t->cfg.to_offset) { + nl_dump(p, " to %s", + rtnl_ematch_offset2txt(t->cfg.to_layer, + t->cfg.to_offset, + buf, sizeof(buf))); + } + + nl_dump(p, ")"); +} + +static int text_fill(struct rtnl_ematch *e, struct nl_msg *msg) +{ + struct text_data *t = rtnl_ematch_data(e); + int err; + + if ((err = nlmsg_append(msg, &t->cfg, sizeof(t->cfg), 0)) < 0) + return err; + + return nlmsg_append(msg, t->pattern, t->cfg.pattern_len, 0); +} + +static void text_free(struct rtnl_ematch *e) +{ + struct text_data *t = rtnl_ematch_data(e); + free(t->pattern); +} + +static struct rtnl_ematch_ops text_ops = { + .eo_kind = TCF_EM_TEXT, + .eo_name = "text", + .eo_minlen = sizeof(struct tcf_em_text), + .eo_datalen = sizeof(struct text_data), + .eo_parse = text_parse, + .eo_dump = text_dump, + .eo_fill = text_fill, + .eo_free = text_free, +}; + +static void _nl_init text_init(void) +{ + rtnl_ematch_register(&text_ops); +} + +/** @} */ diff --git a/libnl/lib/route/cls/ematch_grammar.l b/libnl/lib/route/cls/ematch_grammar.l new file mode 100644 index 0000000..c78c41c --- /dev/null +++ b/libnl/lib/route/cls/ematch_grammar.l @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2013 Thomas Graf + */ + +%top{ + #include "nl-default.h" +} +%{ + #include + + #include + #include + #include + + #include "nl-route.h" + + #include "ematch_syntax.h" + + int ematch_get_column(yyscan_t); + void ematch_set_column(int, yyscan_t); +%} + +%option 8bit +%option reentrant +%option warn +%option noyywrap +%option noinput +%option nounput +%option bison-bridge +%option prefix="ematch_" + +%x QUOTE + +%% + +[ \t\r\n]+ + +\" { + NL_DBG(4, "Beginning of quote\n"); + yylval->q.len = 32; + if (!(yylval->q.data = calloc(1, yylval->q.len))) + return ERROR; + + yylval->q.index = 0; + BEGIN(QUOTE); + } + +[^\\\n\"]+ { + memcpy(yylval->q.data + yylval->q.index, yytext, + strlen(yytext)); + yylval->q.index += strlen(yytext); + } + +\" { + BEGIN(0); + return QUOTED; + } + + +[[:digit:]]+ | +0[xX][[:xdigit:]]+ { + yylval->i = strtoul(yytext, NULL, 0); + return NUMBER; + } + +eq | +"=" return KW_EQ; +gt | +">" return KW_GT; +lt | +"<" return KW_LT; + +[aA][nN][dD] | +"&&" { yylval->i = TCF_EM_REL_AND; return LOGIC; } +[oO][rR] | +"||" { yylval->i = TCF_EM_REL_OR; return LOGIC; } +[nN][oO][tT] | +"!" return NOT; + +[cC][mM][pP] { yylval->i = TCF_EM_CMP; return EMATCH_CMP; } +[pP][aA][tT][tT][eE][rR][nN] { yylval->i = TCF_EM_NBYTE; return EMATCH_NBYTE; } +[tT][eE][xX][tT] { yylval->i = TCF_EM_TEXT; return EMATCH_TEXT; } +[mM][eE][tT][aA] { yylval->i = TCF_EM_META; return EMATCH_META; } + +"(" return KW_OPEN; +")" return KW_CLOSE; +[mM][aA][sS][kK] | +"&" return KW_MASK; +[sS][hH][iI][fF][tT] | +">>" return KW_SHIFT; +[aA][tT] return KW_AT; +"+" return KW_PLUS; +[fF][rR][oO][mM] return KW_FROM; +[tT][oO] return KW_TO; + +[uU]8 { yylval->i = TCF_EM_ALIGN_U8; return ALIGN; } +[uU]16 { yylval->i = TCF_EM_ALIGN_U16; return ALIGN; } +[uU]32 { yylval->i = TCF_EM_ALIGN_U32; return ALIGN; } + +[lL][iI][nN][kK] | +[eE][tT][hH] { yylval->i = TCF_LAYER_LINK; return LAYER; } +[nN][eE][tT] | +[iI][pP]6 | +[iI][pP] { yylval->i = TCF_LAYER_NETWORK; return LAYER; } +[tT][rR][aA][nN][sS][pP][oO][rR][tT] | +[tT][cC][pP] { yylval->i = TCF_LAYER_TRANSPORT; return LAYER; } + +random return META_RANDOM; +loadavg_0 return META_LOADAVG_0; +loadavg_1 return META_LOADAVG_1; +loadavg_2 return META_LOADAVG_2; +dev return META_DEV; +prio return META_PRIO; +proto return META_PROTO; +pkttype return META_PKTTYPE; +pktlen return META_PKTLEN; +datalen return META_DATALEN; +maclen return META_MACLEN; +mark return META_MARK; +tcindex return META_TCINDEX; +rtclassid return META_RTCLASSID; +rtiif return META_RTIIF; +sk_family return META_SK_FAMILY; +sk_state return META_SK_STATE; +sk_reuse return META_SK_REUSE; +sk_refcnt return META_SK_REFCNT; +sk_rcvbuf return META_SK_RCVBUF; +sk_sndbuf return META_SK_SNDBUF; +sk_shutdown return META_SK_SHUTDOWN; +sk_proto return META_SK_PROTO; +sk_type return META_SK_TYPE; +sk_rmem_alloc return META_SK_RMEM_ALLOC; +sk_wmem_alloc return META_SK_WMEM_ALLOC; +sk_wmem_queued return META_SK_WMEM_QUEUED; +sk_rcv_qlen return META_SK_RCV_QLEN; +sk_snd_qlen return META_SK_SND_QLEN; +sk_err_qlen return META_SK_ERR_QLEN; +sk_forward_allocs return META_SK_FORWARD_ALLOCS; +sk_allocs return META_SK_ALLOCS; +sk_route_caps return META_SK_ROUTE_CAPS; +sk_hash return META_SK_HASH; +sk_lingertime return META_SK_LINGERTIME; +sk_ack_backlog return META_SK_ACK_BACKLOG; +sk_max_ack_backlog return META_SK_MAX_ACK_BACKLOG; +sk_prio return META_SK_PRIO; +sk_rcvlowat return META_SK_RCVLOWAT; +sk_rcvtimeo return META_SK_RCVTIMEO; +sk_sndtimeo return META_SK_SNDTIMEO; +sk_sendmsg_off return META_SK_SENDMSG_OFF; +sk_write_pending return META_SK_WRITE_PENDING; +vlan return META_VLAN; +rxhash return META_RXHASH; + +devname return META_DEVNAME; +sk_bound_if return META_SK_BOUND_IF; + + +[^ \t\r\n+()=<>&|\"]+ { + yylval->s = strdup(yytext); + if (yylval->s == NULL) + return ERROR; + NL_DBG(4, "lex STR=%s\n", yylval->s); + return STR; + } diff --git a/libnl/lib/route/cls/ematch_syntax.y b/libnl/lib/route/cls/ematch_syntax.y new file mode 100644 index 0000000..bc0cc9c --- /dev/null +++ b/libnl/lib/route/cls/ematch_syntax.y @@ -0,0 +1,511 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2013 Thomas Graf + */ + +%{ +#include "nl-default.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" + +#define META_ALLOC rtnl_meta_value_alloc_id +#define META_ID(name) TCF_META_ID_##name +#define META_INT TCF_META_TYPE_INT +#define META_VAR TCF_META_TYPE_VAR +%} + +%code requires { + +struct ematch_quoted { + char * data; + size_t len; + int index; +}; + +} + +%error-verbose +%define api.pure +%name-prefix "ematch_" + +%parse-param {void *scanner} +%parse-param {char **errp} +%parse-param {struct nl_list_head *root} +%lex-param {void *scanner} + +%union { + struct tcf_em_cmp cmp; + struct ematch_quoted q; + struct rtnl_ematch * e; + struct rtnl_pktloc * loc; + struct rtnl_meta_value *mv; + uint32_t i; + uint64_t i64; + char * s; +} + +%{ +extern int ematch_lex(YYSTYPE *, void *); + +#define ematch_error yyerror +static void yyerror(void *scanner, char **errp, struct nl_list_head *root, const char *msg) +{ + if (msg) + *errp = strdup(msg); + else + *errp = NULL; +} +%} + +%token ERROR LOGIC NOT OPERAND NUMBER ALIGN LAYER +%token KW_OPEN "(" +%token KW_CLOSE ")" +%token KW_PLUS "+" +%token KW_MASK "mask" +%token KW_SHIFT ">>" +%token KW_AT "at" +%token EMATCH_CMP "cmp" +%token EMATCH_NBYTE "pattern" +%token EMATCH_TEXT "text" +%token EMATCH_META "meta" +%token KW_EQ "=" +%token KW_GT ">" +%token KW_LT "<" +%token KW_FROM "from" +%token KW_TO "to" + +%token META_RANDOM "random" +%token META_LOADAVG_0 "loadavg_0" +%token META_LOADAVG_1 "loadavg_1" +%token META_LOADAVG_2 "loadavg_2" +%token META_DEV "dev" +%token META_PRIO "prio" +%token META_PROTO "proto" +%token META_PKTTYPE "pkttype" +%token META_PKTLEN "pktlen" +%token META_DATALEN "datalen" +%token META_MACLEN "maclen" +%token META_MARK "mark" +%token META_TCINDEX "tcindex" +%token META_RTCLASSID "rtclassid" +%token META_RTIIF "rtiif" +%token META_SK_FAMILY "sk_family" +%token META_SK_STATE "sk_state" +%token META_SK_REUSE "sk_reuse" +%token META_SK_REFCNT "sk_refcnt" +%token META_SK_RCVBUF "sk_rcvbuf" +%token META_SK_SNDBUF "sk_sndbuf" +%token META_SK_SHUTDOWN "sk_shutdown" +%token META_SK_PROTO "sk_proto" +%token META_SK_TYPE "sk_type" +%token META_SK_RMEM_ALLOC "sk_rmem_alloc" +%token META_SK_WMEM_ALLOC "sk_wmem_alloc" +%token META_SK_WMEM_QUEUED "sk_wmem_queued" +%token META_SK_RCV_QLEN "sk_rcv_qlen" +%token META_SK_SND_QLEN "sk_snd_qlen" +%token META_SK_ERR_QLEN "sk_err_qlen" +%token META_SK_FORWARD_ALLOCS "sk_forward_allocs" +%token META_SK_ALLOCS "sk_allocs" +%token META_SK_ROUTE_CAPS "sk_route_caps" +%token META_SK_HASH "sk_hash" +%token META_SK_LINGERTIME "sk_lingertime" +%token META_SK_ACK_BACKLOG "sk_ack_backlog" +%token META_SK_MAX_ACK_BACKLOG "sk_max_ack_backlog" +%token META_SK_PRIO "sk_prio" +%token META_SK_RCVLOWAT "sk_rcvlowat" +%token META_SK_RCVTIMEO "sk_rcvtimeo" +%token META_SK_SNDTIMEO "sk_sndtimeo" +%token META_SK_SENDMSG_OFF "sk_sendmsg_off" +%token META_SK_WRITE_PENDING "sk_write_pending" +%token META_VLAN "vlan" +%token META_RXHASH "rxhash" +%token META_DEVNAME "devname" +%token META_SK_BOUND_IF "sk_bound_if" + +%token STR + +%token QUOTED + +%type align operand shift meta_int_id meta_var_id +%type mask +%type expr match ematch +%type cmp_expr cmp_match +%type pktloc text_from text_to +%type pattern +%type meta_value + +%destructor { free($$); NL_DBG(2, "string destructor\n"); } +%destructor { rtnl_pktloc_put($$); NL_DBG(2, "pktloc destructor\n"); } +%destructor { free($$.data); NL_DBG(2, "quoted destructor\n"); } +%destructor { rtnl_meta_value_put($$); NL_DBG(2, "meta value destructor\n"); } + +%start input + +%% + +input: + /* empty */ + | expr + { + nl_list_add_tail(root, &$1->e_list); + } + ; + +expr: + match + { + $$ = $1; + } + | match LOGIC expr + { + rtnl_ematch_set_flags($1, $2); + + /* make ematch new head */ + nl_list_add_tail(&$1->e_list, &$3->e_list); + + $$ = $1; + } + ; + +match: + NOT ematch + { + rtnl_ematch_set_flags($2, TCF_EM_INVERT); + $$ = $2; + } + | ematch + { + $$ = $1; + } + ; + +ematch: + /* CMP */ + cmp_match + { + struct rtnl_ematch *e; + + if (!(e = rtnl_ematch_alloc())) { + *errp = strdup("Unable to allocate ematch object"); + YYABORT; + } + + if (rtnl_ematch_set_kind(e, TCF_EM_CMP) < 0) + BUG(); + + rtnl_ematch_cmp_set(e, &$1); + $$ = e; + } + | EMATCH_NBYTE "(" pktloc KW_EQ pattern ")" + { + struct rtnl_ematch *e; + + if (!(e = rtnl_ematch_alloc())) { + *errp = strdup("Unable to allocate ematch object"); + YYABORT; + } + + if (rtnl_ematch_set_kind(e, TCF_EM_NBYTE) < 0) + BUG(); + + rtnl_ematch_nbyte_set_offset(e, $3->layer, $3->offset); + rtnl_pktloc_put($3); + rtnl_ematch_nbyte_set_pattern(e, (uint8_t *) $5.data, $5.index); + + $$ = e; + } + | EMATCH_TEXT "(" STR QUOTED text_from text_to ")" + { + struct rtnl_ematch *e; + + if (!(e = rtnl_ematch_alloc())) { + *errp = strdup("Unable to allocate ematch object"); + YYABORT; + } + + if (rtnl_ematch_set_kind(e, TCF_EM_TEXT) < 0) + BUG(); + + rtnl_ematch_text_set_algo(e, $3); + rtnl_ematch_text_set_pattern(e, $4.data, $4.index); + + if ($5) { + rtnl_ematch_text_set_from(e, $5->layer, $5->offset); + rtnl_pktloc_put($5); + } + + if ($6) { + rtnl_ematch_text_set_to(e, $6->layer, $6->offset); + rtnl_pktloc_put($6); + } + + $$ = e; + } + | EMATCH_META "(" meta_value operand meta_value ")" + { + struct rtnl_ematch *e; + + if (!(e = rtnl_ematch_alloc())) { + *errp = strdup("Unable to allocate ematch object"); + YYABORT; + } + + if (rtnl_ematch_set_kind(e, TCF_EM_META) < 0) + BUG(); + + rtnl_ematch_meta_set_lvalue(e, $3); + rtnl_ematch_meta_set_rvalue(e, $5); + rtnl_ematch_meta_set_operand(e, $4); + + $$ = e; + } + /* CONTAINER */ + | "(" expr ")" + { + struct rtnl_ematch *e; + + if (!(e = rtnl_ematch_alloc())) { + *errp = strdup("Unable to allocate ematch object"); + YYABORT; + } + + if (rtnl_ematch_set_kind(e, TCF_EM_CONTAINER) < 0) + BUG(); + + /* Make e->childs the list head of a the ematch sequence */ + nl_list_add_tail(&e->e_childs, &$2->e_list); + + $$ = e; + } + ; + +/* + * CMP match + * + * match := cmp(expr) | expr + * expr := pktloc (=|>|<) NUMBER + * pktloc := alias | definition + * + */ +cmp_match: + EMATCH_CMP "(" cmp_expr ")" + { $$ = $3; } + | cmp_expr + { $$ = $1; } + ; + +cmp_expr: + pktloc operand NUMBER + { + if ($1->align == TCF_EM_ALIGN_U16 || + $1->align == TCF_EM_ALIGN_U32) + $$.flags = TCF_EM_CMP_TRANS; + + memset(&$$, 0, sizeof($$)); + + $$.mask = $1->mask; + $$.off = $1->offset; + $$.align = $1->align; + $$.layer = $1->layer; + $$.opnd = $2; + $$.val = $3; + + rtnl_pktloc_put($1); + } + ; + +text_from: + /* empty */ + { $$ = NULL; } + | "from" pktloc + { $$ = $2; } + ; + +text_to: + /* empty */ + { $$ = NULL; } + | "to" pktloc + { $$ = $2; } + ; + +meta_value: + QUOTED + { $$ = rtnl_meta_value_alloc_var($1.data, $1.len); } + | NUMBER + { $$ = rtnl_meta_value_alloc_int($1); } + | meta_int_id shift mask + { $$ = META_ALLOC(META_INT, $1, $2, $3); } + | meta_var_id shift + { $$ = META_ALLOC(META_VAR, $1, $2, 0); } + ; + +meta_int_id: + META_RANDOM { $$ = META_ID(RANDOM); } + |META_LOADAVG_0 { $$ = META_ID(LOADAVG_0); } + |META_LOADAVG_1 { $$ = META_ID(LOADAVG_1); } + |META_LOADAVG_2 { $$ = META_ID(LOADAVG_2); } + | META_DEV { $$ = META_ID(DEV); } + | META_PRIO { $$ = META_ID(PRIORITY); } + | META_PROTO { $$ = META_ID(PROTOCOL); } + | META_PKTTYPE { $$ = META_ID(PKTTYPE); } + | META_PKTLEN { $$ = META_ID(PKTLEN); } + | META_DATALEN { $$ = META_ID(DATALEN); } + | META_MACLEN { $$ = META_ID(MACLEN); } + | META_MARK { $$ = META_ID(NFMARK); } + | META_TCINDEX { $$ = META_ID(TCINDEX); } + | META_RTCLASSID { $$ = META_ID(RTCLASSID); } + | META_RTIIF { $$ = META_ID(RTIIF); } + | META_SK_FAMILY { $$ = META_ID(SK_FAMILY); } + | META_SK_STATE { $$ = META_ID(SK_STATE); } + | META_SK_REUSE { $$ = META_ID(SK_REUSE); } + | META_SK_REFCNT { $$ = META_ID(SK_REFCNT); } + | META_SK_RCVBUF { $$ = META_ID(SK_RCVBUF); } + | META_SK_SNDBUF { $$ = META_ID(SK_SNDBUF); } + | META_SK_SHUTDOWN { $$ = META_ID(SK_SHUTDOWN); } + | META_SK_PROTO { $$ = META_ID(SK_PROTO); } + | META_SK_TYPE { $$ = META_ID(SK_TYPE); } + | META_SK_RMEM_ALLOC { $$ = META_ID(SK_RMEM_ALLOC); } + | META_SK_WMEM_ALLOC { $$ = META_ID(SK_WMEM_ALLOC); } + | META_SK_WMEM_QUEUED { $$ = META_ID(SK_WMEM_QUEUED); } + | META_SK_RCV_QLEN { $$ = META_ID(SK_RCV_QLEN); } + | META_SK_SND_QLEN { $$ = META_ID(SK_SND_QLEN); } + | META_SK_ERR_QLEN { $$ = META_ID(SK_ERR_QLEN); } + | META_SK_FORWARD_ALLOCS { $$ = META_ID(SK_FORWARD_ALLOCS); } + | META_SK_ALLOCS { $$ = META_ID(SK_ALLOCS); } + | META_SK_ROUTE_CAPS { $$ = __TCF_META_ID_SK_ROUTE_CAPS; } + | META_SK_HASH { $$ = META_ID(SK_HASH); } + | META_SK_LINGERTIME { $$ = META_ID(SK_LINGERTIME); } + | META_SK_ACK_BACKLOG { $$ = META_ID(SK_ACK_BACKLOG); } + | META_SK_MAX_ACK_BACKLOG { $$ = META_ID(SK_MAX_ACK_BACKLOG); } + | META_SK_PRIO { $$ = META_ID(SK_PRIO); } + | META_SK_RCVLOWAT { $$ = META_ID(SK_RCVLOWAT); } + | META_SK_RCVTIMEO { $$ = META_ID(SK_RCVTIMEO); } + | META_SK_SNDTIMEO { $$ = META_ID(SK_SNDTIMEO); } + | META_SK_SENDMSG_OFF { $$ = META_ID(SK_SENDMSG_OFF); } + | META_SK_WRITE_PENDING { $$ = META_ID(SK_WRITE_PENDING); } + | META_VLAN { $$ = META_ID(VLAN_TAG); } + | META_RXHASH { $$ = META_ID(RXHASH); } + ; + +meta_var_id: + META_DEVNAME { $$ = META_ID(DEV); } + | META_SK_BOUND_IF { $$ = META_ID(SK_BOUND_IF); } + ; + +/* + * pattern + */ +pattern: + QUOTED + { + $$ = $1; + } + | STR + { + struct nl_addr *addr; + + if (nl_addr_parse($1, AF_UNSPEC, &addr) == 0) { + $$.len = nl_addr_get_len(addr); + + $$.index = _NL_MIN($$.len, nl_addr_get_prefixlen(addr)/8); + + if (!($$.data = calloc(1, $$.len))) { + nl_addr_put(addr); + YYABORT; + } + + memcpy($$.data, nl_addr_get_binary_addr(addr), $$.len); + nl_addr_put(addr); + } else { + if (asprintf(errp, "invalid pattern \"%s\"", $1) == -1) + *errp = NULL; + YYABORT; + } + } + ; + +/* + * packet location + */ + +pktloc: + STR + { + struct rtnl_pktloc *loc; + + if (rtnl_pktloc_lookup($1, &loc) < 0) { + if (asprintf(errp, "Packet location \"%s\" not found", $1) == -1) + *errp = NULL; + YYABORT; + } + + $$ = loc; + } + /* [u8|u16|u32|NUM at] LAYER + OFFSET [mask MASK] */ + | align LAYER "+" NUMBER mask + { + struct rtnl_pktloc *loc; + + if ($5 && (!$1 || $1 > TCF_EM_ALIGN_U32)) { + *errp = strdup("mask only allowed for alignments u8|u16|u32"); + YYABORT; + } + + if (!(loc = rtnl_pktloc_alloc())) { + *errp = strdup("Unable to allocate packet location object"); + YYABORT; + } + + loc->name = strdup(""); + loc->align = $1; + loc->layer = $2; + loc->offset = $4; + loc->mask = $5; + + $$ = loc; + } + ; + +align: + /* empty */ + { $$ = 0; } + | ALIGN "at" + { $$ = $1; } + | NUMBER "at" + { $$ = $1; } + ; + +mask: + /* empty */ + { $$ = 0; } + | KW_MASK NUMBER + { $$ = $2; } + ; + +shift: + /* empty */ + { $$ = 0; } + | KW_SHIFT NUMBER + { $$ = $2; } + ; + +operand: + KW_EQ + { $$ = TCF_EM_OPND_EQ; } + | KW_GT + { $$ = TCF_EM_OPND_GT; } + | KW_LT + { $$ = TCF_EM_OPND_LT; } + ; diff --git a/libnl/lib/route/cls/flower.c b/libnl/lib/route/cls/flower.c new file mode 100644 index 0000000..56f24c3 --- /dev/null +++ b/libnl/lib/route/cls/flower.c @@ -0,0 +1,921 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2018 Volodymyr Bendiuga + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "tc-api.h" +#include "nl-aux-route/nl-route.h" + +/** @cond SKIP */ +struct rtnl_flower { + struct rtnl_act *cf_act; + int cf_mask; + uint32_t cf_flags; + uint16_t cf_proto; + uint16_t cf_vlan_id; + uint16_t cf_vlan_ethtype; + uint8_t cf_vlan_prio; + uint8_t cf_src_mac[ETH_ALEN]; + uint8_t cf_src_mac_mask[ETH_ALEN]; + uint8_t cf_dst_mac[ETH_ALEN]; + uint8_t cf_dst_mac_mask[ETH_ALEN]; + in_addr_t cf_ipv4_src; + in_addr_t cf_ipv4_src_mask; + in_addr_t cf_ipv4_dst; + in_addr_t cf_ipv4_dst_mask; + uint8_t cf_ip_dscp; + uint8_t cf_ip_dscp_mask; +}; + +#define FLOWER_ATTR_FLAGS (1 << 0) +#define FLOWER_ATTR_ACTION (1 << 1) +#define FLOWER_ATTR_VLAN_ID (1 << 2) +#define FLOWER_ATTR_VLAN_PRIO (1 << 3) +#define FLOWER_ATTR_VLAN_ETH_TYPE (1 << 4) +#define FLOWER_ATTR_DST_MAC (1 << 5) +#define FLOWER_ATTR_DST_MAC_MASK (1 << 6) +#define FLOWER_ATTR_SRC_MAC (1 << 7) +#define FLOWER_ATTR_SRC_MAC_MASK (1 << 8) +#define FLOWER_ATTR_IP_DSCP (1 << 9) +#define FLOWER_ATTR_IP_DSCP_MASK (1 << 10) +#define FLOWER_ATTR_PROTO (1 << 11) +#define FLOWER_ATTR_IPV4_SRC (1 << 12) +#define FLOWER_ATTR_IPV4_SRC_MASK (1 << 13) +#define FLOWER_ATTR_IPV4_DST (1 << 14) +#define FLOWER_ATTR_IPV4_DST_MASK (1 << 15) +/** @endcond */ + +#define FLOWER_DSCP_MAX 0xe0 +#define FLOWER_DSCP_MASK_MAX 0xe0 +#define FLOWER_VID_MAX 4095 +#define FLOWER_VLAN_PRIO_MAX 7 + +static struct nla_policy flower_policy[TCA_FLOWER_MAX + 1] = { + [TCA_FLOWER_FLAGS] = { .type = NLA_U32 }, + [TCA_FLOWER_KEY_ETH_TYPE] = { .type = NLA_U16 }, + [TCA_FLOWER_KEY_ETH_DST] = { .maxlen = ETH_ALEN }, + [TCA_FLOWER_KEY_ETH_DST_MASK] = { .maxlen = ETH_ALEN }, + [TCA_FLOWER_KEY_ETH_SRC] = { .maxlen = ETH_ALEN }, + [TCA_FLOWER_KEY_ETH_SRC_MASK] = { .maxlen = ETH_ALEN }, + [TCA_FLOWER_KEY_VLAN_ID] = { .type = NLA_U16 }, + [TCA_FLOWER_KEY_VLAN_PRIO] = { .type = NLA_U8 }, + [TCA_FLOWER_KEY_IP_TOS] = { .type = NLA_U8 }, + [TCA_FLOWER_KEY_IP_TOS_MASK] = { .type = NLA_U8 }, + [TCA_FLOWER_KEY_VLAN_ETH_TYPE] = { .type = NLA_U16 }, + [TCA_FLOWER_KEY_IPV4_SRC] = { .type = NLA_U32 }, + [TCA_FLOWER_KEY_IPV4_SRC_MASK] = { .type = NLA_U32 }, + [TCA_FLOWER_KEY_IPV4_DST] = { .type = NLA_U32 }, + [TCA_FLOWER_KEY_IPV4_DST_MASK] = { .type = NLA_U32 }, +}; + +static int flower_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_flower *f = data; + struct nlattr *tb[TCA_FLOWER_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_FLOWER_MAX, tc, flower_policy); + if (err < 0) + return err; + + if (tb[TCA_FLOWER_FLAGS]) { + f->cf_flags = nla_get_u32(tb[TCA_FLOWER_FLAGS]); + f->cf_mask |= FLOWER_ATTR_FLAGS; + } + + if (tb[TCA_FLOWER_ACT]) { + err = rtnl_act_parse(&f->cf_act, tb[TCA_FLOWER_ACT]); + if (err) + return err; + + f->cf_mask |= FLOWER_ATTR_ACTION; + } + + if (tb[TCA_FLOWER_KEY_ETH_TYPE]) { + f->cf_proto = nla_get_u16(tb[TCA_FLOWER_KEY_ETH_TYPE]); + f->cf_mask |= FLOWER_ATTR_PROTO; + } + + if (tb[TCA_FLOWER_KEY_VLAN_ID]) { + f->cf_vlan_id = nla_get_u16(tb[TCA_FLOWER_KEY_VLAN_ID]); + f->cf_mask |= FLOWER_ATTR_VLAN_ID; + } + + if (tb[TCA_FLOWER_KEY_VLAN_PRIO]) { + f->cf_vlan_prio = nla_get_u8(tb[TCA_FLOWER_KEY_VLAN_PRIO]); + f->cf_mask |= FLOWER_ATTR_VLAN_PRIO; + } + + if (tb[TCA_FLOWER_KEY_VLAN_ETH_TYPE]) { + f->cf_vlan_ethtype = nla_get_u16(tb[TCA_FLOWER_KEY_VLAN_ETH_TYPE]); + f->cf_mask |= FLOWER_ATTR_VLAN_ETH_TYPE; + } + + if (tb[TCA_FLOWER_KEY_ETH_DST]) { + nla_memcpy(f->cf_dst_mac, tb[TCA_FLOWER_KEY_ETH_DST], ETH_ALEN); + f->cf_mask |= FLOWER_ATTR_DST_MAC; + } + + if (tb[TCA_FLOWER_KEY_ETH_DST_MASK]) { + nla_memcpy(f->cf_dst_mac_mask, tb[TCA_FLOWER_KEY_ETH_DST_MASK], ETH_ALEN); + f->cf_mask |= FLOWER_ATTR_DST_MAC_MASK; + } + + if (tb[TCA_FLOWER_KEY_ETH_SRC]) { + nla_memcpy(f->cf_src_mac, tb[TCA_FLOWER_KEY_ETH_SRC], ETH_ALEN); + f->cf_mask |= FLOWER_ATTR_SRC_MAC; + } + + if (tb[TCA_FLOWER_KEY_ETH_SRC_MASK]) { + nla_memcpy(f->cf_src_mac_mask, tb[TCA_FLOWER_KEY_ETH_SRC_MASK], ETH_ALEN); + f->cf_mask |= FLOWER_ATTR_SRC_MAC_MASK; + } + + if (tb[TCA_FLOWER_KEY_IP_TOS]) { + f->cf_ip_dscp = nla_get_u8(tb[TCA_FLOWER_KEY_IP_TOS]); + f->cf_mask |= FLOWER_ATTR_IP_DSCP; + } + + if (tb[TCA_FLOWER_KEY_IP_TOS_MASK]) { + f->cf_ip_dscp_mask = nla_get_u8(tb[TCA_FLOWER_KEY_IP_TOS_MASK]); + f->cf_mask |= FLOWER_ATTR_IP_DSCP_MASK; + } + + if (tb[TCA_FLOWER_KEY_IPV4_SRC]) { + f->cf_ipv4_src = nla_get_u32(tb[TCA_FLOWER_KEY_IPV4_SRC]); + f->cf_mask |= FLOWER_ATTR_IPV4_SRC; + } + + if (tb[TCA_FLOWER_KEY_IPV4_SRC_MASK]) { + f->cf_ipv4_src_mask = + nla_get_u32(tb[TCA_FLOWER_KEY_IPV4_SRC_MASK]); + f->cf_mask |= FLOWER_ATTR_IPV4_SRC_MASK; + } + + if (tb[TCA_FLOWER_KEY_IPV4_DST]) { + f->cf_ipv4_dst = nla_get_u32(tb[TCA_FLOWER_KEY_IPV4_DST]); + f->cf_mask |= FLOWER_ATTR_IPV4_DST; + } + + if (tb[TCA_FLOWER_KEY_IPV4_DST_MASK]) { + f->cf_ipv4_dst_mask = + nla_get_u32(tb[TCA_FLOWER_KEY_IPV4_DST_MASK]); + f->cf_mask |= FLOWER_ATTR_IPV4_DST_MASK; + } + + return 0; +} + +static int flower_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_flower *f = data; + int err; + + if (!f) + return 0; + + if (f->cf_mask & FLOWER_ATTR_FLAGS) + NLA_PUT_U32(msg, TCA_FLOWER_FLAGS, f->cf_flags); + + if (f->cf_mask & FLOWER_ATTR_ACTION) { + err = rtnl_act_fill(msg, TCA_FLOWER_ACT, f->cf_act); + if (err) + return err; + } + + if (f->cf_mask & FLOWER_ATTR_PROTO) + NLA_PUT_U16(msg, TCA_FLOWER_KEY_ETH_TYPE, f->cf_proto); + + if (f->cf_mask & FLOWER_ATTR_VLAN_ID) + NLA_PUT_U16(msg, TCA_FLOWER_KEY_VLAN_ID, f->cf_vlan_id); + + if (f->cf_mask & FLOWER_ATTR_VLAN_PRIO) + NLA_PUT_U8(msg, TCA_FLOWER_KEY_VLAN_PRIO, f->cf_vlan_prio); + + if (f->cf_mask & FLOWER_ATTR_VLAN_ETH_TYPE) + NLA_PUT_U16(msg, TCA_FLOWER_KEY_VLAN_ETH_TYPE, f->cf_vlan_ethtype); + + if (f->cf_mask & FLOWER_ATTR_DST_MAC) + NLA_PUT(msg, TCA_FLOWER_KEY_ETH_DST, ETH_ALEN, f->cf_dst_mac); + + if (f->cf_mask & FLOWER_ATTR_DST_MAC_MASK) + NLA_PUT(msg, TCA_FLOWER_KEY_ETH_DST_MASK, ETH_ALEN, f->cf_dst_mac_mask); + + if (f->cf_mask & FLOWER_ATTR_SRC_MAC) + NLA_PUT(msg, TCA_FLOWER_KEY_ETH_SRC, ETH_ALEN, f->cf_src_mac); + + if (f->cf_mask & FLOWER_ATTR_SRC_MAC_MASK) + NLA_PUT(msg, TCA_FLOWER_KEY_ETH_SRC_MASK, ETH_ALEN, f->cf_src_mac_mask); + + if (f->cf_mask & FLOWER_ATTR_IP_DSCP) + NLA_PUT_U8(msg, TCA_FLOWER_KEY_IP_TOS, f->cf_ip_dscp); + + if (f->cf_mask & FLOWER_ATTR_IP_DSCP_MASK) + NLA_PUT_U8(msg, TCA_FLOWER_KEY_IP_TOS_MASK, f->cf_ip_dscp_mask); + + if (f->cf_mask & FLOWER_ATTR_IPV4_SRC) + NLA_PUT_U32(msg, TCA_FLOWER_KEY_IPV4_SRC, f->cf_ipv4_src); + + if (f->cf_mask & FLOWER_ATTR_IPV4_SRC_MASK) + NLA_PUT_U32(msg, TCA_FLOWER_KEY_IPV4_SRC_MASK, + f->cf_ipv4_src_mask); + + if (f->cf_mask & FLOWER_ATTR_IPV4_DST) + NLA_PUT_U32(msg, TCA_FLOWER_KEY_IPV4_DST, f->cf_ipv4_dst); + + if (f->cf_mask & FLOWER_ATTR_IPV4_DST_MASK) + NLA_PUT_U32(msg, TCA_FLOWER_KEY_IPV4_DST_MASK, + f->cf_ipv4_dst_mask); + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +static void flower_free_data(struct rtnl_tc *tc, void *data) +{ + struct rtnl_flower *f = data; + + if (f->cf_act) + rtnl_act_put_all(&f->cf_act); +} + +static int flower_clone(void *_dst, void *_src) +{ + struct rtnl_flower *dst = _dst, *src = _src; + + if (src->cf_act) { + if (!(dst->cf_act = rtnl_act_alloc())) + return -NLE_NOMEM; + + memcpy(dst->cf_act, src->cf_act, sizeof(struct rtnl_act)); + + /* action nl list next and prev pointers must be updated */ + nl_init_list_head(&dst->cf_act->ce_list); + + if ( src->cf_act->c_opts + && !(dst->cf_act->c_opts = nl_data_clone(src->cf_act->c_opts))) + return -NLE_NOMEM; + + if ( src->cf_act->c_xstats + && !(dst->cf_act->c_xstats = nl_data_clone(src->cf_act->c_xstats))) + return -NLE_NOMEM; + + if ( src->cf_act->c_subdata + && !(dst->cf_act->c_subdata = nl_data_clone(src->cf_act->c_subdata))) + return -NLE_NOMEM; + + if (dst->cf_act->c_link) { + nl_object_get(OBJ_CAST(dst->cf_act->c_link)); + } + + dst->cf_act->a_next = NULL; /* Only clone first in chain */ + } + + return 0; +} + +static void flower_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_flower *f = data; + char addr_str[INET_ADDRSTRLEN]; + char mask_str[INET_ADDRSTRLEN]; + + if (!f) + return; + + if (f->cf_mask & FLOWER_ATTR_FLAGS) + nl_dump(p, " flags %u", f->cf_flags); + + if (f->cf_mask & FLOWER_ATTR_PROTO) + nl_dump(p, " protocol %u", f->cf_proto); + + if (f->cf_mask & FLOWER_ATTR_VLAN_ID) + nl_dump(p, " vlan_id %u", f->cf_vlan_id); + + if (f->cf_mask & FLOWER_ATTR_VLAN_PRIO) + nl_dump(p, " vlan_prio %u", f->cf_vlan_prio); + + if (f->cf_mask & FLOWER_ATTR_VLAN_ETH_TYPE) + nl_dump(p, " vlan_ethtype %u", f->cf_vlan_ethtype); + + if (f->cf_mask & FLOWER_ATTR_DST_MAC) + nl_dump(p, " dst_mac %02x:%02x:%02x:%02x:%02x:%02x", + f->cf_dst_mac[0], f->cf_dst_mac[1], + f->cf_dst_mac[2], f->cf_dst_mac[3], + f->cf_dst_mac[4], f->cf_dst_mac[5]); + + if (f->cf_mask & FLOWER_ATTR_DST_MAC_MASK) + nl_dump(p, " dst_mac_mask %02x:%02x:%02x:%02x:%02x:%02x", + f->cf_dst_mac_mask[0], f->cf_dst_mac_mask[1], + f->cf_dst_mac_mask[2], f->cf_dst_mac_mask[3], + f->cf_dst_mac_mask[4], f->cf_dst_mac_mask[5]); + + if (f->cf_mask & FLOWER_ATTR_SRC_MAC) + nl_dump(p, " src_mac %02x:%02x:%02x:%02x:%02x:%02x", + f->cf_src_mac[0], f->cf_src_mac[1], + f->cf_src_mac[2], f->cf_src_mac[3], + f->cf_src_mac[4], f->cf_src_mac[5]); + + if (f->cf_mask & FLOWER_ATTR_SRC_MAC_MASK) + nl_dump(p, " src_mac_mask %02x:%02x:%02x:%02x:%02x:%02x", + f->cf_src_mac_mask[0], f->cf_src_mac_mask[1], + f->cf_src_mac_mask[2], f->cf_src_mac_mask[3], + f->cf_src_mac_mask[4], f->cf_src_mac_mask[5]); + + if (f->cf_mask & FLOWER_ATTR_IP_DSCP) + nl_dump(p, " dscp %u", f->cf_ip_dscp); + + if (f->cf_mask & FLOWER_ATTR_IP_DSCP_MASK) + nl_dump(p, " dscp_mask %u", f->cf_ip_dscp_mask); + + if (f->cf_mask & FLOWER_ATTR_IPV4_SRC) { + inet_ntop(AF_INET, &f->cf_ipv4_src, addr_str, sizeof(addr_str)); + inet_ntop(AF_INET, &f->cf_ipv4_src_mask, mask_str, sizeof(mask_str)); + nl_dump(p, "IPv4 src %s mask %s\n", addr_str, mask_str); + } + + if (f->cf_mask & FLOWER_ATTR_IPV4_DST) { + inet_ntop(AF_INET, &f->cf_ipv4_dst, addr_str, sizeof(addr_str)); + inet_ntop(AF_INET, &f->cf_ipv4_dst_mask, mask_str, sizeof(mask_str)); + nl_dump(p, "IPv4 dst %s mask %s\n", addr_str, mask_str); + } +} + +/** + * @name Attribute Modification + * @{ + */ + +/** + * Set protocol for flower classifier + * @arg cls Flower classifier. + * @arg proto protocol (ETH_P_*) + * @return 0 on success or a negative error code. + */ +int rtnl_flower_set_proto(struct rtnl_cls *cls, uint16_t proto) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + f->cf_proto = htons(proto); + f->cf_mask |= FLOWER_ATTR_PROTO; + + return 0; +} + +/** + * Get protocol for flower classifier + * @arg cls Flower classifier. + * @arg proto protocol + * @return 0 on success or a negative error code. +*/ +int rtnl_flower_get_proto(struct rtnl_cls *cls, uint16_t *proto) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_INVAL; + + if (!(f->cf_mask & FLOWER_ATTR_PROTO)) + return -NLE_MISSING_ATTR; + + *proto = ntohs(f->cf_proto); + + return 0; +} + +/** + * Set vlan id for flower classifier + * @arg cls Flower classifier. + * @arg vid vlan id + * @return 0 on success or a negative error code. + */ +int rtnl_flower_set_vlan_id(struct rtnl_cls *cls, uint16_t vid) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (vid > FLOWER_VID_MAX) + return -NLE_RANGE; + + f->cf_vlan_id = vid; + f->cf_mask |= FLOWER_ATTR_VLAN_ID; + + return 0; +} + +/** + * Get vlan id for flower classifier + * @arg cls Flower classifier. + * @arg vid vlan id + * @return 0 on success or a negative error code. +*/ +int rtnl_flower_get_vlan_id(struct rtnl_cls *cls, uint16_t *vid) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_INVAL; + + if (!(f->cf_mask & FLOWER_ATTR_VLAN_ID)) + return -NLE_MISSING_ATTR; + + *vid = f->cf_vlan_id; + + return 0; +} + +/** + * Set vlan priority for flower classifier + * @arg cls Flower classifier. + * @arg prio vlan priority + * @return 0 on success or a negative error code. + */ +int rtnl_flower_set_vlan_prio(struct rtnl_cls *cls, uint8_t prio) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (prio > FLOWER_VLAN_PRIO_MAX) + return -NLE_RANGE; + + f->cf_vlan_prio = prio; + f->cf_mask |= FLOWER_ATTR_VLAN_PRIO; + + return 0; +} + +/** + * Get vlan prio for flower classifier + * @arg cls Flower classifier. + * @arg prio vlan priority + * @return 0 on success or a negative error code. +*/ +int rtnl_flower_get_vlan_prio(struct rtnl_cls *cls, uint8_t *prio) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_INVAL; + + if (!(f->cf_mask & FLOWER_ATTR_VLAN_PRIO)) + return -NLE_MISSING_ATTR; + + *prio = f->cf_vlan_prio; + + return 0; +} + +/** + * Set vlan ethertype for flower classifier + * @arg cls Flower classifier. + * @arg ethtype vlan ethertype + * @return 0 on success or a negative error code. + */ +int rtnl_flower_set_vlan_ethtype(struct rtnl_cls *cls, uint16_t ethtype) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(f->cf_mask & FLOWER_ATTR_PROTO)) + return -NLE_MISSING_ATTR; + + if (f->cf_proto != htons(ETH_P_8021Q)) + return -NLE_INVAL; + + f->cf_vlan_ethtype = htons(ethtype); + f->cf_mask |= FLOWER_ATTR_VLAN_ETH_TYPE; + + return 0; +} + +/** + * Set destination mac address for flower classifier + * @arg cls Flower classifier. + * @arg mac destination mac address + * @arg mask mask for mac address + * @return 0 on success or a negative error code. + */ +int rtnl_flower_set_dst_mac(struct rtnl_cls *cls, unsigned char *mac, + unsigned char *mask) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (mac) { + memcpy(f->cf_dst_mac, mac, ETH_ALEN); + f->cf_mask |= FLOWER_ATTR_DST_MAC; + + if (mask) { + memcpy(f->cf_dst_mac_mask, mask, ETH_ALEN); + f->cf_mask |= FLOWER_ATTR_DST_MAC_MASK; + } + + return 0; + } + + return -NLE_FAILURE; +} + +/** + * Get destination mac address for flower classifier + * @arg cls Flower classifier. + * @arg mac destination mac address + * @arg mask mask for mac address + * @return 0 on success or a negative error code. +*/ +int rtnl_flower_get_dst_mac(struct rtnl_cls *cls, unsigned char *mac, + unsigned char *mask) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_INVAL; + + if (!(f->cf_mask & FLOWER_ATTR_DST_MAC)) + return -NLE_MISSING_ATTR; + + if (mac) + memcpy(mac, f->cf_dst_mac, ETH_ALEN); + + if (mask) + memcpy(mask, f->cf_dst_mac_mask, ETH_ALEN); + + return 0; +} + +/** + * Set source mac address for flower classifier + * @arg cls Flower classifier. + * @arg mac source mac address + * @arg mask mask for mac address + * @return 0 on success or a negative error code. + */ +int rtnl_flower_set_src_mac(struct rtnl_cls *cls, unsigned char *mac, + unsigned char *mask) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (mac) { + memcpy(f->cf_src_mac, mac, ETH_ALEN); + f->cf_mask |= FLOWER_ATTR_SRC_MAC; + + if (mask) { + memcpy(f->cf_src_mac_mask, mask, ETH_ALEN); + f->cf_mask |= FLOWER_ATTR_SRC_MAC_MASK; + } + + return 0; + } + + return -NLE_FAILURE; +} + +/** + * Get source mac address for flower classifier + * @arg cls Flower classifier. + * @arg mac source mac address + * @arg mask mask for mac address + * @return 0 on success or a negative error code. +*/ +int rtnl_flower_get_src_mac(struct rtnl_cls *cls, unsigned char *mac, + unsigned char *mask) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_INVAL; + + if (!(f->cf_mask & FLOWER_ATTR_SRC_MAC)) + return -NLE_MISSING_ATTR; + + if (mac) + memcpy(mac, f->cf_src_mac, ETH_ALEN); + + if (mask) + memcpy(mask, f->cf_src_mac_mask, ETH_ALEN); + + return 0; +} + +/** + * Set dscp value for flower classifier + * @arg cls Flower classifier. + * @arg dscp dscp value + * @arg mask mask for dscp value + * @return 0 on success or a negative error code. + */ +int rtnl_flower_set_ip_dscp(struct rtnl_cls *cls, uint8_t dscp, uint8_t mask) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (dscp > FLOWER_DSCP_MAX) + return -NLE_RANGE; + + if (mask > FLOWER_DSCP_MASK_MAX) + return -NLE_RANGE; + + f->cf_ip_dscp = dscp; + f->cf_mask |= FLOWER_ATTR_IP_DSCP; + + if (mask) { + f->cf_ip_dscp_mask = mask; + f->cf_mask |= FLOWER_ATTR_IP_DSCP_MASK; + } + + return 0; +} + +/** + * Get dscp value for flower classifier + * @arg cls Flower classifier. + * @arg dscp dscp value + * @arg mask mask for dscp value + * @return 0 on success or a negative error code. +*/ +int rtnl_flower_get_ip_dscp(struct rtnl_cls *cls, uint8_t *dscp, uint8_t *mask) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_INVAL; + + if (!(f->cf_mask & FLOWER_ATTR_IP_DSCP)) + return -NLE_MISSING_ATTR; + + *dscp = f->cf_ip_dscp; + *mask = f->cf_ip_dscp_mask; + + return 0; +} + +/** + * Set IPv4 source address for flower classifier + * @arg cls Flower classifier. + * @arg addr IPv4 source address + * @arg mask mask for IPv4 source address + * @return 0 on success or a negative error code. + */ +int rtnl_flower_set_ipv4_src(struct rtnl_cls *cls, in_addr_t addr, + in_addr_t mask) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (addr) { + f->cf_ipv4_src = addr; + f->cf_mask |= FLOWER_ATTR_IPV4_SRC; + + if (mask) { + f->cf_ipv4_src_mask = mask; + f->cf_mask |= FLOWER_ATTR_IPV4_SRC_MASK; + } + + return 0; + } + + return -NLE_FAILURE; +} + +/** + * Get IPv4 source address for flower classifier + * @arg cls Flower classifier. + * @arg addr IPv4 source address + * @arg mask mask for IPv4 source address + * @return 0 on success or a negative error code. + */ +int rtnl_flower_get_ipv4_src(struct rtnl_cls *cls, in_addr_t *out_addr, + in_addr_t *out_mask) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_INVAL; + + if (!(f->cf_mask & FLOWER_ATTR_IPV4_SRC)) + return -NLE_MISSING_ATTR; + + if (out_addr) + *out_addr = f->cf_ipv4_src; + + if (out_mask) { + if (f->cf_mask & FLOWER_ATTR_IPV4_SRC_MASK) + *out_mask = f->cf_ipv4_src_mask; + else + *out_mask = 0xffffffff; + } + + return 0; +} + +/** + * Set IPv4 destination address for flower classifier + * @arg cls Flower classifier. + * @arg addr IPv4 destination address + * @arg mask mask for IPv4 destination address + * @return 0 on success or a negative error code. + */ +int rtnl_flower_set_ipv4_dst(struct rtnl_cls *cls, in_addr_t addr, + in_addr_t mask) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (addr) { + f->cf_ipv4_dst = addr; + f->cf_mask |= FLOWER_ATTR_IPV4_DST; + + if (mask) { + f->cf_ipv4_dst_mask = mask; + f->cf_mask |= FLOWER_ATTR_IPV4_DST_MASK; + } + + return 0; + } + + return -NLE_FAILURE; +} + +/** + * Get IPv4 destination address for flower classifier + * @arg cls Flower classifier. + * @arg addr IPv4 destination address + * @arg mask mask for IPv4 destination address + * @return 0 on success or a negative error code. + */ +int rtnl_flower_get_ipv4_dst(struct rtnl_cls *cls, in_addr_t *out_addr, + in_addr_t *out_mask) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_INVAL; + + if (!(f->cf_mask & FLOWER_ATTR_IPV4_DST)) + return -NLE_MISSING_ATTR; + + if (out_addr) + *out_addr = f->cf_ipv4_dst; + + if (out_mask) { + if (f->cf_mask & FLOWER_ATTR_IPV4_DST_MASK) + *out_mask = f->cf_ipv4_dst_mask; + else + *out_mask = 0xffffffff; + } + + return 0; +} + +/** + * Append action for flower classifier + * @arg cls Flower classifier. + * @arg act action to append + * @return 0 on success or a negative error code. + */ +int rtnl_flower_append_action(struct rtnl_cls *cls, struct rtnl_act *act) +{ + struct rtnl_flower *f; + int err; + + if (!act) + return 0; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if ((err = _rtnl_act_append_get(&f->cf_act, act)) < 0) + return err; + + f->cf_mask |= FLOWER_ATTR_ACTION; + return 0; +} + +/** + * Delete action from flower classifier + * @arg cls Flower classifier. + * @arg act action to delete + * @return 0 on success or a negative error code. + */ +int rtnl_flower_del_action(struct rtnl_cls *cls, struct rtnl_act *act) +{ + struct rtnl_flower *f; + int ret; + + if (!act) + return 0; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(f->cf_mask & FLOWER_ATTR_ACTION)) + return -NLE_INVAL; + + ret = rtnl_act_remove(&f->cf_act, act); + if (ret) + return ret; + + if (!f->cf_act) + f->cf_mask &= ~FLOWER_ATTR_ACTION; + rtnl_act_put(act); + + return 0; +} + +/** + * Get action from flower classifier + * @arg cls Flower classifier. + * @return action on success or NULL on error. + */ +struct rtnl_act* rtnl_flower_get_action(struct rtnl_cls *cls) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data_peek(TC_CAST(cls)))) + return NULL; + + if (!(f->cf_mask & FLOWER_ATTR_ACTION)) + return NULL; + + rtnl_act_get(f->cf_act); + + return f->cf_act; +} + +/** + * Set flags for flower classifier + * @arg cls Flower classifier. + * @arg flags (TCA_CLS_FLAGS_SKIP_HW | TCA_CLS_FLAGS_SKIP_SW) + * @return 0 on success or a negative error code. + */ +int rtnl_flower_set_flags(struct rtnl_cls *cls, int flags) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + f->cf_flags = flags; + f->cf_mask |= FLOWER_ATTR_FLAGS; + + return 0; +} + +/** @} */ + +static struct rtnl_tc_ops flower_ops = { + .to_kind = "flower", + .to_type = RTNL_TC_TYPE_CLS, + .to_size = sizeof(struct rtnl_flower), + .to_msg_parser = flower_msg_parser, + .to_free_data = flower_free_data, + .to_clone = flower_clone, + .to_msg_fill = flower_msg_fill, + .to_dump = { + [NL_DUMP_DETAILS] = flower_dump_details, + }, +}; + +static void _nl_init flower_init(void) +{ + rtnl_tc_register(&flower_ops); +} + +static void _nl_exit flower_exit(void) +{ + rtnl_tc_unregister(&flower_ops); +} diff --git a/libnl/lib/route/cls/fw.c b/libnl/lib/route/cls/fw.c new file mode 100644 index 0000000..01be33f --- /dev/null +++ b/libnl/lib/route/cls/fw.c @@ -0,0 +1,226 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + * Copyright (c) 2006 Petr Gotthard + * Copyright (c) 2006 Siemens AG Oesterreich + */ + +/** + * @ingroup cls + * @defgroup cls_fw Firewall Classifier + * + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include + +#include "tc-api.h" + +/** @cond SKIP */ +struct rtnl_fw { + uint32_t cf_classid; + struct nl_data *cf_act; + struct nl_data *cf_police; + char cf_indev[IFNAMSIZ]; + uint32_t cf_fwmask; + int cf_mask; +}; + +#define FW_ATTR_CLASSID 0x001 +#define FW_ATTR_ACTION 0x002 +#define FW_ATTR_POLICE 0x004 +#define FW_ATTR_INDEV 0x008 +#define FW_ATTR_MASK 0x010 +/** @endcond */ + +static struct nla_policy fw_policy[TCA_FW_MAX+1] = { + [TCA_FW_CLASSID] = { .type = NLA_U32 }, + [TCA_FW_INDEV] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, + [TCA_FW_MASK] = { .type = NLA_U32 }, +}; + +static int fw_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct nlattr *tb[TCA_FW_MAX + 1]; + struct rtnl_fw *f = data; + int err; + + err = tca_parse(tb, TCA_FW_MAX, tc, fw_policy); + if (err < 0) + return err; + + if (tb[TCA_FW_CLASSID]) { + f->cf_classid = nla_get_u32(tb[TCA_FW_CLASSID]); + f->cf_mask |= FW_ATTR_CLASSID; + } + + if (tb[TCA_FW_ACT]) { + f->cf_act = nl_data_alloc_attr(tb[TCA_FW_ACT]); + if (!f->cf_act) + return -NLE_NOMEM; + f->cf_mask |= FW_ATTR_ACTION; + } + + if (tb[TCA_FW_POLICE]) { + f->cf_police = nl_data_alloc_attr(tb[TCA_FW_POLICE]); + if (!f->cf_police) + return -NLE_NOMEM; + f->cf_mask |= FW_ATTR_POLICE; + } + + if (tb[TCA_FW_INDEV]) { + nla_strlcpy(f->cf_indev, tb[TCA_FW_INDEV], IFNAMSIZ); + f->cf_mask |= FW_ATTR_INDEV; + } + + if (tb[TCA_FW_MASK]) { + f->cf_fwmask = nla_get_u32(tb[TCA_FW_MASK]); + f->cf_mask |= FW_ATTR_MASK; + } + + return 0; +} + +static void fw_free_data(struct rtnl_tc *tc, void *data) +{ + struct rtnl_fw *f = data; + + nl_data_free(f->cf_act); + nl_data_free(f->cf_police); +} + +static int fw_clone(void *_dst, void *_src) +{ + struct rtnl_fw *dst = _dst, *src = _src; + + dst->cf_act = NULL; + dst->cf_police = NULL; + + if (src->cf_act && !(dst->cf_act = nl_data_clone(src->cf_act))) + return -NLE_NOMEM; + + if (src->cf_police && !(dst->cf_police = nl_data_clone(src->cf_police))) + return -NLE_NOMEM; + + return 0; +} + +static void fw_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_fw *f = data; + + if (!f) + return; + + if (f->cf_mask & FW_ATTR_CLASSID) { + char buf[32]; + + nl_dump(p, " target %s", + rtnl_tc_handle2str(f->cf_classid, buf, sizeof(buf))); + } + + if (f->cf_mask & FW_ATTR_MASK) + nl_dump(p, " mask 0x%x", f->cf_fwmask); +} + +static void fw_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_fw *f = data; + + if (f && f->cf_mask & FW_ATTR_INDEV) + nl_dump(p, "indev %s ", f->cf_indev); +} + +static int fw_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_fw *f = data; + + if (!f) + return 0; + + if (f->cf_mask & FW_ATTR_CLASSID) + NLA_PUT_U32(msg, TCA_FW_CLASSID, f->cf_classid); + + if (f->cf_mask & FW_ATTR_ACTION) + NLA_PUT_DATA(msg, TCA_FW_ACT, f->cf_act); + + if (f->cf_mask & FW_ATTR_POLICE) + NLA_PUT_DATA(msg, TCA_FW_POLICE, f->cf_police); + + if (f->cf_mask & FW_ATTR_INDEV) + NLA_PUT_STRING(msg, TCA_FW_INDEV, f->cf_indev); + + if (f->cf_mask & FW_ATTR_MASK) + NLA_PUT_U32(msg, TCA_FW_MASK, f->cf_fwmask); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +/** + * @name Attribute Modifications + * @{ + */ + +int rtnl_fw_set_classid(struct rtnl_cls *cls, uint32_t classid) +{ + struct rtnl_fw *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + f->cf_classid = classid; + f->cf_mask |= FW_ATTR_CLASSID; + + return 0; +} + +int rtnl_fw_set_mask(struct rtnl_cls *cls, uint32_t mask) +{ + struct rtnl_fw *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + f->cf_fwmask = mask; + f->cf_mask |= FW_ATTR_MASK; + + return 0; +} + +/** @} */ + +static struct rtnl_tc_ops fw_ops = { + .to_kind = "fw", + .to_type = RTNL_TC_TYPE_CLS, + .to_size = sizeof(struct rtnl_fw), + .to_msg_parser = fw_msg_parser, + .to_msg_fill = fw_msg_fill, + .to_free_data = fw_free_data, + .to_clone = fw_clone, + .to_dump = { + [NL_DUMP_LINE] = fw_dump_line, + [NL_DUMP_DETAILS] = fw_dump_details, + }, +}; + +static void _nl_init fw_init(void) +{ + rtnl_tc_register(&fw_ops); +} + +static void _nl_exit fw_exit(void) +{ + rtnl_tc_unregister(&fw_ops); +} + +/** @} */ diff --git a/libnl/lib/route/cls/mall.c b/libnl/lib/route/cls/mall.c new file mode 100644 index 0000000..030eaa8 --- /dev/null +++ b/libnl/lib/route/cls/mall.c @@ -0,0 +1,307 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2017 Volodymyr Bendiuga + */ + +/** + * @ingroup cls + * @defgroup cls_mall Match-all Classifier + * + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include +#include + +#include "tc-api.h" +#include "nl-aux-route/nl-route.h" + +struct rtnl_mall { + uint32_t m_classid; + uint32_t m_flags; + struct rtnl_act *m_act; + int m_mask; +}; + +#define MALL_ATTR_CLASSID 0x01 +#define MALL_ATTR_FLAGS 0x02 +#define MALL_ATTR_ACTION 0x03 + + +static struct nla_policy mall_policy[TCA_MATCHALL_MAX + 1] = { + [TCA_MATCHALL_CLASSID] = { .type = NLA_U32 }, + [TCA_MATCHALL_FLAGS] = { .type = NLA_U32 }, +}; + +/** + * @name Attribute Modifications + * @{ + */ + +int rtnl_mall_set_classid(struct rtnl_cls *cls, uint32_t classid) +{ + struct rtnl_mall *mall; + if (!(mall = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + mall->m_classid = classid; + mall->m_mask |= MALL_ATTR_CLASSID; + + return 0; +} + +int rtnl_mall_get_classid(struct rtnl_cls *cls, uint32_t *classid) +{ + struct rtnl_mall *mall; + + if (!(mall = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_INVAL; + + if (!(mall->m_mask & MALL_ATTR_CLASSID)) + return -NLE_INVAL; + + *classid = mall->m_classid; + return 0; +} + +int rtnl_mall_set_flags(struct rtnl_cls *cls, uint32_t flags) +{ + struct rtnl_mall *mall; + + if (!(mall = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + mall->m_flags = flags; + mall->m_mask |= MALL_ATTR_FLAGS; + + return 0; +} + +int rtnl_mall_get_flags(struct rtnl_cls *cls, uint32_t *flags) +{ + struct rtnl_mall *mall; + + if (!(mall = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_INVAL; + + if (!(mall->m_mask & MALL_ATTR_FLAGS)) + return -NLE_INVAL; + + *flags = mall->m_flags; + return 0; +} + +int rtnl_mall_append_action(struct rtnl_cls *cls, struct rtnl_act *act) +{ + struct rtnl_mall *mall; + int err; + + if (!act) + return 0; + + if (!(mall = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if ((err = _rtnl_act_append_get(&mall->m_act, act)) < 0) + return err; + + mall->m_mask |= MALL_ATTR_ACTION; + return 0; +} + +struct rtnl_act *rtnl_mall_get_first_action(struct rtnl_cls *cls) +{ + struct rtnl_mall *mall; + struct rtnl_act *act; + + if (!(mall = rtnl_tc_data(TC_CAST(cls)))) + return NULL; + + if (!(mall->m_mask & MALL_ATTR_ACTION)) + return NULL; + + act = mall->m_act; + rtnl_act_get(act); + + return act; +} + +int rtnl_mall_del_action(struct rtnl_cls *cls, struct rtnl_act *act) +{ + struct rtnl_mall *mall; + int ret; + + if (!act) + return 0; + + if (!(mall = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(mall->m_mask & MALL_ATTR_ACTION)) + return -NLE_INVAL; + + ret = rtnl_act_remove(&mall->m_act, act); + if (ret < 0) + return ret; + + rtnl_act_put(act); + + return 0; +} + +/** @} */ + +static void mall_free_data(struct rtnl_tc *tc, void *data) +{ + struct rtnl_mall *mall = data; + + if (mall->m_act) + rtnl_act_put_all(&mall->m_act); +} + +static int mall_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_mall *mall = data; + struct nlattr *tb[TCA_MATCHALL_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_MATCHALL_MAX, tc, mall_policy); + if (err < 0) + return err; + + if (tb[TCA_MATCHALL_CLASSID]) { + mall->m_classid = nla_get_u32(tb[TCA_MATCHALL_CLASSID]); + mall->m_mask |= MALL_ATTR_CLASSID; + } + + if (tb[TCA_MATCHALL_FLAGS]) { + mall->m_flags = nla_get_u32(tb[TCA_MATCHALL_FLAGS]); + mall->m_mask |= MALL_ATTR_FLAGS; + } + + if (tb[TCA_MATCHALL_ACT]) { + mall->m_mask |= MALL_ATTR_ACTION; + err = rtnl_act_parse(&mall->m_act, tb[TCA_MATCHALL_ACT]); + if (err < 0) + return err; + } + + return 0; +} + +static int mall_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_mall *mall = data; + + if (!mall) + return 0; + + if (mall->m_mask & MALL_ATTR_CLASSID) + NLA_PUT_U32(msg, TCA_MATCHALL_CLASSID, mall->m_classid); + + if (mall->m_mask & MALL_ATTR_FLAGS) + NLA_PUT_U32(msg, TCA_MATCHALL_FLAGS, mall->m_flags); + + if (mall->m_mask & MALL_ATTR_ACTION) { + int err; + + err = rtnl_act_fill(msg, TCA_MATCHALL_ACT, mall->m_act); + if (err < 0) + return err; + } + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +static int mall_clone(void *_dst, void *_src) +{ + struct rtnl_mall *dst = _dst, *src = _src; + struct rtnl_act *next, *new; + int err; + + dst->m_act = NULL; + + if (src->m_act) { + if (!(dst->m_act = rtnl_act_alloc())) + return -NLE_NOMEM; + + /* action nl list next and prev pointers must be updated */ + nl_init_list_head(&dst->m_act->ce_list); + + memcpy(dst->m_act, src->m_act, sizeof(struct rtnl_act)); + next = rtnl_act_next(src->m_act); + while (next) { + new = (struct rtnl_act *) nl_object_clone((struct nl_object *) next); + if (!new) + return -NLE_NOMEM; + + err = _rtnl_act_append_take(&dst->m_act, new); + if (err < 0) + return err; + + next = rtnl_act_next(next); + } + } + + return 0; +} + +static void mall_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_mall *mall = data; + char buf[32]; + + if (!mall) + return; + + if (mall->m_mask & MALL_ATTR_CLASSID) + nl_dump(p, " target %s", + rtnl_tc_handle2str(mall->m_classid, buf, sizeof(buf))); +} + +static void mall_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_mall *mall = data; + + if (!mall) + return; + + nl_dump(p, "no details for match-all"); +} + +static struct rtnl_tc_ops mall_ops = { + .to_kind = "matchall", + .to_type = RTNL_TC_TYPE_CLS, + .to_size = sizeof(struct rtnl_mall), + .to_msg_parser = mall_msg_parser, + .to_free_data = mall_free_data, + .to_clone = mall_clone, + .to_msg_fill = mall_msg_fill, + .to_dump = { + [NL_DUMP_LINE] = mall_dump_line, + [NL_DUMP_DETAILS] = mall_dump_details, + }, +}; + +static void _nl_init mall_init(void) +{ + rtnl_tc_register(&mall_ops); +} + +static void _nl_exit mall_exit(void) +{ + rtnl_tc_unregister(&mall_ops); +} + +/** @} */ diff --git a/libnl/lib/route/cls/police.c b/libnl/lib/route/cls/police.c new file mode 100644 index 0000000..e7e3926 --- /dev/null +++ b/libnl/lib/route/cls/police.c @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#include "nl-default.h" + +#include +#include +#include +#include + +#include "nl-priv-dynamic-core/nl-core.h" + +/** + * @name Policer Type + * @{ + */ + +static const struct trans_tbl police_types[] = { + __ADD(TC_POLICE_UNSPEC,unspec), + __ADD(TC_POLICE_OK,ok), + __ADD(TC_POLICE_RECLASSIFY,reclassify), + __ADD(TC_POLICE_SHOT,shot), +#ifdef TC_POLICE_PIPE + __ADD(TC_POLICE_PIPE,pipe), +#endif +}; + +/** + * Transform a policer type number into a character string (Reentrant). + * @arg type policer type + * @arg buf destination buffer + * @arg len buffer length + * + * Transforms a policer type number into a character string and stores + * it in the provided buffer. + * + * @return The destination buffer or the type encoded in hex if no match was found. + */ +char * nl_police2str(int type, char *buf, size_t len) +{ + return __type2str(type, buf, len, police_types, + ARRAY_SIZE(police_types)); +} + +/** + * Transform a character string into a policer type number + * @arg name policer type name + * + * Transform the provided character string specifying a policer + * type into the corresponding numeric value + * + * @return Policer type number or a negative value. + */ +int nl_str2police(const char *name) +{ + return __str2type(name, police_types, ARRAY_SIZE(police_types)); +} + +/** @} */ diff --git a/libnl/lib/route/cls/u32.c b/libnl/lib/route/cls/u32.c new file mode 100644 index 0000000..9821c77 --- /dev/null +++ b/libnl/lib/route/cls/u32.c @@ -0,0 +1,910 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + * Copyright (c) 2005-2006 Petr Gotthard + * Copyright (c) 2005-2006 Siemens AG Oesterreich + */ + +/** + * @ingroup cls + * @defgroup cls_u32 Universal 32-bit Classifier + * + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include +#include + +#include "tc-api.h" +#include "nl-aux-route/nl-route.h" + +/** @cond SKIP */ +struct rtnl_u32 { + uint32_t cu_divisor; + uint32_t cu_hash; + uint32_t cu_classid; + uint32_t cu_link; + struct nl_data *cu_pcnt; + struct nl_data *cu_selector; + struct nl_data *cu_mark; + struct rtnl_act *cu_act; + struct nl_data *cu_police; + char cu_indev[IFNAMSIZ]; + int cu_mask; +}; + +#define U32_ATTR_DIVISOR 0x001 +#define U32_ATTR_HASH 0x002 +#define U32_ATTR_CLASSID 0x004 +#define U32_ATTR_LINK 0x008 +#define U32_ATTR_PCNT 0x010 +#define U32_ATTR_SELECTOR 0x020 +#define U32_ATTR_ACTION 0x040 +#define U32_ATTR_POLICE 0x080 +#define U32_ATTR_INDEV 0x100 +#define U32_ATTR_MARK 0x200 +/** @endcond */ + +static inline struct tc_u32_sel *u32_selector(struct rtnl_u32 *u) +{ + return (struct tc_u32_sel *) u->cu_selector->d_data; +} + +static inline struct tc_u32_sel *u32_selector_alloc(struct rtnl_u32 *u) +{ + if (!u->cu_selector) + u->cu_selector = nl_data_alloc(NULL, sizeof(struct tc_u32_sel)); + + return u32_selector(u); +} + +static inline struct tc_u32_mark *u32_mark_alloc(struct rtnl_u32 *u) +{ + if (!u->cu_mark) + u->cu_mark = nl_data_alloc(NULL, sizeof(struct tc_u32_mark)); + + return (struct tc_u32_mark *) u->cu_mark->d_data; +} + +static struct nla_policy u32_policy[TCA_U32_MAX+1] = { + [TCA_U32_DIVISOR] = { .type = NLA_U32 }, + [TCA_U32_HASH] = { .type = NLA_U32 }, + [TCA_U32_CLASSID] = { .type = NLA_U32 }, + [TCA_U32_LINK] = { .type = NLA_U32 }, + [TCA_U32_INDEV] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, + [TCA_U32_SEL] = { .minlen = sizeof(struct tc_u32_sel) }, + [TCA_U32_PCNT] = { .minlen = sizeof(struct tc_u32_pcnt) }, + [TCA_U32_MARK] = { .minlen = sizeof(struct tc_u32_mark) } +}; + +static int u32_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_u32 *u = data; + struct nlattr *tb[TCA_U32_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_U32_MAX, tc, u32_policy); + if (err < 0) + return err; + + if (tb[TCA_U32_DIVISOR]) { + u->cu_divisor = nla_get_u32(tb[TCA_U32_DIVISOR]); + u->cu_mask |= U32_ATTR_DIVISOR; + } + + if (tb[TCA_U32_SEL]) { + u->cu_selector = nl_data_alloc_attr(tb[TCA_U32_SEL]); + if (!u->cu_selector) + goto errout_nomem; + u->cu_mask |= U32_ATTR_SELECTOR; + } + + if (tb[TCA_U32_MARK]) { + u->cu_mark = nl_data_alloc_attr(tb[TCA_U32_MARK]); + if (!u->cu_mark) + goto errout_nomem; + u->cu_mask |= U32_ATTR_MARK; + } + + if (tb[TCA_U32_HASH]) { + u->cu_hash = nla_get_u32(tb[TCA_U32_HASH]); + u->cu_mask |= U32_ATTR_HASH; + } + + if (tb[TCA_U32_CLASSID]) { + u->cu_classid = nla_get_u32(tb[TCA_U32_CLASSID]); + u->cu_mask |= U32_ATTR_CLASSID; + } + + if (tb[TCA_U32_LINK]) { + u->cu_link = nla_get_u32(tb[TCA_U32_LINK]); + u->cu_mask |= U32_ATTR_LINK; + } + + if (tb[TCA_U32_ACT]) { + u->cu_mask |= U32_ATTR_ACTION; + err = rtnl_act_parse(&u->cu_act, tb[TCA_U32_ACT]); + if (err < 0) + return err; + } + + if (tb[TCA_U32_POLICE]) { + u->cu_police = nl_data_alloc_attr(tb[TCA_U32_POLICE]); + if (!u->cu_police) + goto errout_nomem; + u->cu_mask |= U32_ATTR_POLICE; + } + + if (tb[TCA_U32_PCNT]) { + struct tc_u32_sel *sel; + size_t pcnt_size; + + if (!tb[TCA_U32_SEL]) { + err = -NLE_MISSING_ATTR; + goto errout; + } + + sel = u->cu_selector->d_data; + pcnt_size = sizeof(struct tc_u32_pcnt) + + (sel->nkeys * sizeof(uint64_t)); + if (_nla_len(tb[TCA_U32_PCNT]) < pcnt_size) { + err = -NLE_INVAL; + goto errout; + } + + u->cu_pcnt = nl_data_alloc_attr(tb[TCA_U32_PCNT]); + if (!u->cu_pcnt) + goto errout_nomem; + u->cu_mask |= U32_ATTR_PCNT; + } + + if (tb[TCA_U32_INDEV]) { + nla_strlcpy(u->cu_indev, tb[TCA_U32_INDEV], IFNAMSIZ); + u->cu_mask |= U32_ATTR_INDEV; + } + + return 0; + +errout_nomem: + err = -NLE_NOMEM; +errout: + return err; +} + +static void u32_free_data(struct rtnl_tc *tc, void *data) +{ + struct rtnl_u32 *u = data; + + if (u->cu_act) + rtnl_act_put_all(&u->cu_act); + nl_data_free(u->cu_mark); + nl_data_free(u->cu_selector); + nl_data_free(u->cu_police); + nl_data_free(u->cu_pcnt); +} + +static int u32_clone(void *_dst, void *_src) +{ + struct rtnl_u32 *dst = _dst, *src = _src; + _nl_auto_nl_data struct nl_data *selector = NULL; + _nl_auto_nl_data struct nl_data *mark = NULL; + _nl_auto_nl_data struct nl_data *police = NULL; + _nl_auto_nl_data struct nl_data *pcnt = NULL; + _nl_auto_nl_data struct nl_data *opts = NULL; + _nl_auto_nl_data struct nl_data *xstats = NULL; + _nl_auto_nl_data struct nl_data *subdata = NULL; + _nl_auto_rtnl_act struct rtnl_act *act = NULL; + + dst->cu_pcnt = NULL; + dst->cu_selector = NULL; + dst->cu_mark = NULL; + dst->cu_act = NULL; + dst->cu_police = NULL; + + if (src->cu_selector) { + if (!(selector = nl_data_clone(src->cu_selector))) + return -NLE_NOMEM; + } + + if (src->cu_mark) { + if (!(mark = nl_data_clone(src->cu_mark))) + return -NLE_NOMEM; + } + + if (src->cu_act) { + if (!(act = rtnl_act_alloc())) + return -NLE_NOMEM; + + if (src->cu_act->c_opts) { + if (!(opts = nl_data_clone(src->cu_act->c_opts))) + return -NLE_NOMEM; + } + + if (src->cu_act->c_xstats) { + if (!(xstats = nl_data_clone(src->cu_act->c_xstats))) + return -NLE_NOMEM; + } + + if (src->cu_act->c_subdata) { + if (!(subdata = nl_data_clone(src->cu_act->c_subdata))) + return -NLE_NOMEM; + } + } + + if (src->cu_police) { + if (!(police = nl_data_clone(src->cu_police))) + return -NLE_NOMEM; + } + + if (src->cu_pcnt) { + if (!(pcnt = nl_data_clone(src->cu_pcnt))) + return -NLE_NOMEM; + } + + /* we've passed the critical point and its safe to proceed */ + + if (selector) + dst->cu_selector = _nl_steal_pointer(&selector); + + if (mark) + dst->cu_mark = _nl_steal_pointer(&mark); + + if (police) + dst->cu_police = _nl_steal_pointer(&police); + + if (pcnt) + dst->cu_pcnt = _nl_steal_pointer(&pcnt); + + if (act) { + dst->cu_act = _nl_steal_pointer(&act); + + /* action nl list next and prev pointers must be updated */ + nl_init_list_head(&dst->cu_act->ce_list); + + if (opts) + dst->cu_act->c_opts = _nl_steal_pointer(&opts); + + if (xstats) + dst->cu_act->c_xstats = _nl_steal_pointer(&xstats); + + if (subdata) + dst->cu_act->c_subdata = _nl_steal_pointer(&subdata); + + if (dst->cu_act->c_link) { + nl_object_get(OBJ_CAST(dst->cu_act->c_link)); + } + + dst->cu_act->a_next = NULL; /* Only clone first in chain */ + } + + return 0; +} + +static void u32_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_u32 *u = data; + char buf[32]; + + if (!u) + return; + + if (u->cu_mask & U32_ATTR_DIVISOR) + nl_dump(p, " divisor %u", u->cu_divisor); + else if (u->cu_mask & U32_ATTR_CLASSID) + nl_dump(p, " target %s", + rtnl_tc_handle2str(u->cu_classid, buf, sizeof(buf))); +} + +static void print_selector(struct nl_dump_params *p, struct tc_u32_sel *sel, + struct rtnl_u32 *u) +{ + int i; + struct tc_u32_key *key; + + if (sel->hmask || sel->hoff) { + /* I guess this will never be used since the kernel only + * exports the selector if no divisor is set but hash offset + * and hash mask make only sense in hash filters with divisor + * set */ + nl_dump(p, " hash at %u & 0x%x", sel->hoff, sel->hmask); + } + + if (sel->flags & (TC_U32_OFFSET | TC_U32_VAROFFSET)) { + nl_dump(p, " offset at %u", sel->off); + + if (sel->flags & TC_U32_VAROFFSET) + nl_dump(p, " variable (at %u & 0x%x) >> %u", + sel->offoff, ntohs(sel->offmask), sel->offshift); + } + + if (sel->flags) { + int flags = sel->flags; + nl_dump(p, " <"); + +#define PRINT_FLAG(f) if (flags & TC_U32_##f) { \ + flags &= ~TC_U32_##f; nl_dump(p, #f "%s", flags ? "," : ""); } + + PRINT_FLAG(TERMINAL); + PRINT_FLAG(OFFSET); + PRINT_FLAG(VAROFFSET); + PRINT_FLAG(EAT); +#undef PRINT_FLAG + + nl_dump(p, ">"); + } + + + for (i = 0; i < sel->nkeys; i++) { + key = &sel->keys[i]; + + nl_dump(p, "\n"); + nl_dump_line(p, " match key at %s%u ", + key->offmask ? "nexthdr+" : "", key->off); + + if (key->offmask) + nl_dump(p, "[0x%u] ", key->offmask); + + nl_dump(p, "& 0x%08x == 0x%08x", ntohl(key->mask), ntohl(key->val)); + + if (p->dp_type == NL_DUMP_STATS && + (u->cu_mask & U32_ATTR_PCNT)) { + struct tc_u32_pcnt *pcnt = u->cu_pcnt->d_data; + + nl_dump(p, " successful %llu", + (long long unsigned)pcnt->kcnts[i]); + } + } +} + +static void u32_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_u32 *u = data; + struct tc_u32_sel *s = NULL; + struct tc_u32_mark *m; + + if (!u) + return; + + if (!(u->cu_mask & U32_ATTR_SELECTOR)) { + nl_dump(p, "no-selector"); + } else { + s = u->cu_selector->d_data; + nl_dump(p, "nkeys %u", s->nkeys); + } + + if (!(u->cu_mask & U32_ATTR_MARK)) { + nl_dump(p, " no-mark"); + } else { + m = u->cu_mark->d_data; + nl_dump(p, " mark 0x%u 0x%u", m->val, m->mask); + } + + if (u->cu_mask & U32_ATTR_HASH) + nl_dump(p, " ht key 0x%x hash 0x%u", + TC_U32_USERHTID(u->cu_hash), TC_U32_HASH(u->cu_hash)); + + if (u->cu_mask & U32_ATTR_LINK) + nl_dump(p, " link %u", u->cu_link); + + if (u->cu_mask & U32_ATTR_INDEV) + nl_dump(p, " indev %s", u->cu_indev); + + if (u->cu_mask & U32_ATTR_SELECTOR) + print_selector(p, s, u); + + nl_dump(p, "\n"); +} + +static void u32_dump_stats(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_u32 *u = data; + + if (!u) + return; + + if (u->cu_mask & U32_ATTR_PCNT) { + struct tc_u32_pcnt *pc = u->cu_pcnt->d_data; + + nl_dump(p, "\n"); + nl_dump_line(p, " hit %8llu count %8llu\n", + (long long unsigned)pc->rhit, + (long long unsigned)pc->rcnt); + } +} + +static int u32_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_u32 *u = data; + + if (!u) + return 0; + + if (u->cu_mask & U32_ATTR_DIVISOR) + NLA_PUT_U32(msg, TCA_U32_DIVISOR, u->cu_divisor); + + if (u->cu_mask & U32_ATTR_HASH) + NLA_PUT_U32(msg, TCA_U32_HASH, u->cu_hash); + + if (u->cu_mask & U32_ATTR_CLASSID) + NLA_PUT_U32(msg, TCA_U32_CLASSID, u->cu_classid); + + if (u->cu_mask & U32_ATTR_LINK) + NLA_PUT_U32(msg, TCA_U32_LINK, u->cu_link); + + if (u->cu_mask & U32_ATTR_SELECTOR) + NLA_PUT_DATA(msg, TCA_U32_SEL, u->cu_selector); + + if (u->cu_mask & U32_ATTR_MARK) + NLA_PUT_DATA(msg, TCA_U32_MARK, u->cu_mark); + + if (u->cu_mask & U32_ATTR_ACTION) { + int err; + + err = rtnl_act_fill(msg, TCA_U32_ACT, u->cu_act); + if (err < 0) + return err; + } + + if (u->cu_mask & U32_ATTR_POLICE) + NLA_PUT_DATA(msg, TCA_U32_POLICE, u->cu_police); + + if (u->cu_mask & U32_ATTR_INDEV) + NLA_PUT_STRING(msg, TCA_U32_INDEV, u->cu_indev); + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +/** + * @name Attribute Modifications + * @{ + */ + +void rtnl_u32_set_handle(struct rtnl_cls *cls, int htid, int hash, + int nodeid) +{ + uint32_t handle = (htid << 20) | (hash << 12) | nodeid; + + rtnl_tc_set_handle((struct rtnl_tc *) cls, handle ); +} + +int rtnl_u32_set_classid(struct rtnl_cls *cls, uint32_t classid) +{ + struct rtnl_u32 *u; + + if (!(u = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + u->cu_classid = classid; + u->cu_mask |= U32_ATTR_CLASSID; + + return 0; +} + +int rtnl_u32_get_classid(struct rtnl_cls *cls, uint32_t *classid) +{ + struct rtnl_u32 *u; + + if (!(u = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_INVAL; + + if (!(u->cu_mask & U32_ATTR_CLASSID)) + return -NLE_INVAL; + + *classid = u->cu_classid; + return 0; +} + +int rtnl_u32_set_divisor(struct rtnl_cls *cls, uint32_t divisor) +{ + struct rtnl_u32 *u; + + if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + u->cu_divisor = divisor; + u->cu_mask |= U32_ATTR_DIVISOR; + return 0; +} + +int rtnl_u32_set_link(struct rtnl_cls *cls, uint32_t link) +{ + struct rtnl_u32 *u; + + if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + u->cu_link = link; + u->cu_mask |= U32_ATTR_LINK; + return 0; +} + +int rtnl_u32_set_hashtable(struct rtnl_cls *cls, uint32_t ht) +{ + struct rtnl_u32 *u; + + if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + u->cu_hash = ht; + u->cu_mask |= U32_ATTR_HASH; + return 0; +} + +int rtnl_u32_set_hashmask(struct rtnl_cls *cls, uint32_t hashmask, uint32_t offset) +{ + struct rtnl_u32 *u; + struct tc_u32_sel *sel; + + hashmask = htonl(hashmask); + + if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + sel = u32_selector_alloc(u); + if (!sel) + return -NLE_NOMEM; + + sel->hmask = hashmask; + sel->hoff = offset; + return 0; +} + +int rtnl_u32_set_selector(struct rtnl_cls *cls, int offoff, uint32_t offmask, char offshift, uint16_t off, char flags) +{ + struct rtnl_u32 *u; + struct tc_u32_sel *sel; + + offmask = ntohs(offmask); + + if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + sel = u32_selector_alloc(u); + if (!sel) + return -NLE_NOMEM; + + sel->offoff = offoff; + sel->offmask = offmask; + sel->offshift = offshift; + sel->flags |= TC_U32_VAROFFSET; + sel->off = off; + sel->flags |= flags; + return 0; +} + +int rtnl_u32_set_cls_terminal(struct rtnl_cls *cls) +{ + struct rtnl_u32 *u; + struct tc_u32_sel *sel; + + if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + sel = u32_selector_alloc(u); + if (!sel) + return -NLE_NOMEM; + + sel->flags |= TC_U32_TERMINAL; + return 0; +} + +int rtnl_u32_add_action(struct rtnl_cls *cls, struct rtnl_act *act) +{ + struct rtnl_u32 *u; + int err; + + if (!act) + return 0; + + if (!(u = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if ((err = _rtnl_act_append_get(&u->cu_act, act)) < 0) + return err; + + u->cu_mask |= U32_ATTR_ACTION; + return 0; +} + +struct rtnl_act* rtnl_u32_get_action(struct rtnl_cls *cls) +{ + struct rtnl_u32 *u; + + if (!(u = rtnl_tc_data_peek(TC_CAST(cls)))) + return NULL; + + if (!(u->cu_mask & U32_ATTR_ACTION)) + return NULL; + + return u->cu_act; +} + +int rtnl_u32_del_action(struct rtnl_cls *cls, struct rtnl_act *act) +{ + struct rtnl_u32 *u; + int ret; + + if (!act) + return 0; + + if (!(u = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(u->cu_mask & U32_ATTR_ACTION)) + return -NLE_INVAL; + + ret = rtnl_act_remove(&u->cu_act, act); + if (ret) + return ret; + + if (!u->cu_act) + u->cu_mask &= ~U32_ATTR_ACTION; + rtnl_act_put(act); + return 0; +} +/** @} */ + +/** + * @name Selector Modifications + * @{ + */ + +int rtnl_u32_set_flags(struct rtnl_cls *cls, int flags) +{ + struct tc_u32_sel *sel; + struct rtnl_u32 *u; + + if (!(u = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + sel = u32_selector_alloc(u); + if (!sel) + return -NLE_NOMEM; + + sel->flags |= flags; + u->cu_mask |= U32_ATTR_SELECTOR; + + return 0; +} + +/** + * Append new 32-bit key to the selector + * + * @arg cls classifier to be modifier + * @arg val value to be matched (network byte-order) + * @arg mask mask to be applied before matching (network byte-order) + * @arg off offset, in bytes, to start matching + * @arg offmask offset mask + * + * General selectors define the pattern, mask and offset the pattern will be + * matched to the packet contents. Using the general selectors you can match + * virtually any single bit in the IP (or upper layer) header. + * +*/ +int rtnl_u32_add_key(struct rtnl_cls *cls, uint32_t val, uint32_t mask, + int off, int offmask) +{ + struct tc_u32_sel *sel; + struct rtnl_u32 *u; + int err; + + if (!(u = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + sel = u32_selector_alloc(u); + if (!sel) + return -NLE_NOMEM; + + if (sel->nkeys == UCHAR_MAX) + return -NLE_NOMEM; + + err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key)); + if (err < 0) + return err; + + /* the selector might have been moved by realloc */ + sel = u32_selector(u); + + sel->keys[sel->nkeys].mask = mask; + sel->keys[sel->nkeys].val = val & mask; + sel->keys[sel->nkeys].off = off; + sel->keys[sel->nkeys].offmask = offmask; + sel->nkeys++; + u->cu_mask |= U32_ATTR_SELECTOR; + + return 0; +} + +int rtnl_u32_add_mark(struct rtnl_cls *cls, uint32_t val, uint32_t mask) +{ + struct tc_u32_mark *mark; + struct rtnl_u32 *u; + + if (!(u = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + mark = u32_mark_alloc(u); + if (!mark) + return -NLE_NOMEM; + + mark->mask = mask; + mark->val = val; + + u->cu_mask |= U32_ATTR_MARK; + + return 0; +} + +int rtnl_u32_del_mark(struct rtnl_cls *cls) +{ + struct rtnl_u32 *u; + + if (!(u = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(u->cu_mask)) + return -NLE_INVAL; + + if (!(u->cu_mask & U32_ATTR_MARK)) + return -NLE_INVAL; + + nl_data_free(u->cu_mark); + u->cu_mark = NULL; + u->cu_mask &= ~U32_ATTR_MARK; + + return 0; +} + +/** + * Get the 32-bit key from the selector + * + * @arg cls classifier to be retrieve + * @arg index the index of the array of keys, start with 0 + * @arg val pointer to store value after masked (network byte-order) + * @arg mask pointer to store the mask (network byte-order) + * @arg off pointer to store the offset + * @arg offmask pointer to store offset mask + * +*/ +int rtnl_u32_get_key(struct rtnl_cls *cls, uint8_t index, + uint32_t *val, uint32_t *mask, int *off, int *offmask) +{ + struct tc_u32_sel *sel; + struct rtnl_u32 *u; + + if (!(u = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(u->cu_mask & U32_ATTR_SELECTOR)) + return -NLE_INVAL; + + sel = u32_selector(u); + if (index >= sel->nkeys) + return -NLE_RANGE; + + *mask = sel->keys[index].mask; + *val = sel->keys[index].val; + *off = sel->keys[index].off; + *offmask = sel->keys[index].offmask; + return 0; +} + + +int rtnl_u32_add_key_uint8(struct rtnl_cls *cls, uint8_t val, uint8_t mask, + int off, int offmask) +{ + int shift = 24 - 8 * (off & 3); + + return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift), + htonl((uint32_t)mask << shift), + off & ~3, offmask); +} + +/** + * Append new selector key to match a 16-bit number + * + * @arg cls classifier to be modified + * @arg val value to be matched (host byte-order) + * @arg mask mask to be applied before matching (host byte-order) + * @arg off offset, in bytes, to start matching + * @arg offmask offset mask +*/ +int rtnl_u32_add_key_uint16(struct rtnl_cls *cls, uint16_t val, uint16_t mask, + int off, int offmask) +{ + int shift = ((off & 3) == 0 ? 16 : 0); + if (off % 2) + return -NLE_INVAL; + + return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift), + htonl((uint32_t)mask << shift), + off & ~3, offmask); +} + +/** + * Append new selector key to match a 32-bit number + * + * @arg cls classifier to be modified + * @arg val value to be matched (host byte-order) + * @arg mask mask to be applied before matching (host byte-order) + * @arg off offset, in bytes, to start matching + * @arg offmask offset mask +*/ +int rtnl_u32_add_key_uint32(struct rtnl_cls *cls, uint32_t val, uint32_t mask, + int off, int offmask) +{ + return rtnl_u32_add_key(cls, htonl(val), htonl(mask), + off & ~3, offmask); +} + +int rtnl_u32_add_key_in_addr(struct rtnl_cls *cls, const struct in_addr *addr, + uint8_t bitmask, int off, int offmask) +{ + uint32_t mask = 0xFFFFFFFF << (32 - bitmask); + return rtnl_u32_add_key(cls, addr->s_addr, htonl(mask), off, offmask); +} + +int rtnl_u32_add_key_in6_addr(struct rtnl_cls *cls, const struct in6_addr *addr, + uint8_t bitmask, int off, int offmask) +{ + int i, err; + + for (i = 1; i <= 4; i++) { + if (32 * i - bitmask <= 0) { + if ((err = rtnl_u32_add_key(cls, addr->s6_addr32[i-1], + 0xFFFFFFFF, off+4*(i-1), offmask)) < 0) + return err; + } + else if (32 * i - bitmask < 32) { + uint32_t mask = 0xFFFFFFFF << (32 * i - bitmask); + if ((err = rtnl_u32_add_key(cls, addr->s6_addr32[i-1], + htonl(mask), off+4*(i-1), offmask)) < 0) + return err; + } + /* otherwise, if (32*i - bitmask >= 32) no key is generated */ + } + + return 0; +} + +/** @} */ + +static struct rtnl_tc_ops u32_ops = { + .to_kind = "u32", + .to_type = RTNL_TC_TYPE_CLS, + .to_size = sizeof(struct rtnl_u32), + .to_msg_parser = u32_msg_parser, + .to_free_data = u32_free_data, + .to_clone = u32_clone, + .to_msg_fill = u32_msg_fill, + .to_dump = { + [NL_DUMP_LINE] = u32_dump_line, + [NL_DUMP_DETAILS] = u32_dump_details, + [NL_DUMP_STATS] = u32_dump_stats, + }, +}; + +static void _nl_init u32_init(void) +{ + rtnl_tc_register(&u32_ops); +} + +static void _nl_exit u32_exit(void) +{ + rtnl_tc_unregister(&u32_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link-sriov.h b/libnl/lib/route/link-sriov.h new file mode 100644 index 0000000..6493a57 --- /dev/null +++ b/libnl/lib/route/link-sriov.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Intel Corp. All rights reserved. + * Copyright (c) 2016 Jef Oliver + */ + +#ifndef NETLINK_PRIV_LINK_SRIOV_H_ +#define NETLINK_PRIV_LINK_SRIOV_H_ + +#include +#include + +extern int rtnl_link_sriov_clone(struct rtnl_link *, struct rtnl_link *); +extern void rtnl_link_sriov_dump_details(struct rtnl_link *, struct nl_dump_params *); +extern void rtnl_link_sriov_dump_stats(struct rtnl_link *, struct nl_dump_params *); +extern int rtnl_link_sriov_fill_vflist(struct nl_msg *, struct rtnl_link *); +extern void rtnl_link_sriov_free_data(struct rtnl_link *); +extern int rtnl_link_sriov_parse_vflist(struct rtnl_link *, struct nlattr **); + +#endif diff --git a/libnl/lib/route/link.c b/libnl/lib/route/link.c new file mode 100644 index 0000000..9b72574 --- /dev/null +++ b/libnl/lib/route/link.c @@ -0,0 +1,3256 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup link Links (Interfaces) + * + * @details + * @route_doc{route_link, Link Documentation} + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nl-aux-route/nl-route.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" +#include "nl-route.h" +#include "link-sriov.h" +#include "link/link-api.h" + +/** @cond SKIP */ +#define LINK_ATTR_MTU (1 << 0) +#define LINK_ATTR_LINK (1 << 1) +#define LINK_ATTR_TXQLEN (1 << 2) +#define LINK_ATTR_WEIGHT (1 << 3) +#define LINK_ATTR_MASTER (1 << 4) +#define LINK_ATTR_QDISC (1 << 5) +#define LINK_ATTR_MAP (1 << 6) +#define LINK_ATTR_ADDR (1 << 7) +#define LINK_ATTR_BRD (1 << 8) +#define LINK_ATTR_FLAGS (1 << 9) +#define LINK_ATTR_IFNAME (1 << 10) +#define LINK_ATTR_IFINDEX (1 << 11) +#define LINK_ATTR_FAMILY (1 << 12) +#define LINK_ATTR_ARPTYPE (1 << 13) +#define LINK_ATTR_STATS (1 << 14) +#define LINK_ATTR_CHANGE (1 << 15) +#define LINK_ATTR_OPERSTATE (1 << 16) +#define LINK_ATTR_LINKMODE (1 << 17) +#define LINK_ATTR_LINKINFO (1 << 18) +#define LINK_ATTR_IFALIAS (1 << 19) +#define LINK_ATTR_NUM_VF (1 << 20) +#define LINK_ATTR_PROMISCUITY (1 << 21) +#define LINK_ATTR_NUM_TX_QUEUES (1 << 22) +#define LINK_ATTR_NUM_RX_QUEUES (1 << 23) +#define LINK_ATTR_GROUP (1 << 24) +#define LINK_ATTR_CARRIER (1 << 25) +#define LINK_ATTR_PROTINFO (1 << 26) +#define LINK_ATTR_AF_SPEC (1 << 27) +#define LINK_ATTR_PHYS_PORT_ID (1 << 28) +#define LINK_ATTR_NS_FD (1 << 29) +#define LINK_ATTR_NS_PID (1 << 30) +/* 31 used by 32-bit api */ +#define LINK_ATTR_LINK_NETNSID ((uint64_t) 1 << 32) +#define LINK_ATTR_VF_LIST ((uint64_t) 1 << 33) +#define LINK_ATTR_CARRIER_CHANGES ((uint64_t) 1 << 34) +#define LINK_ATTR_PHYS_PORT_NAME ((uint64_t) 1 << 35) +#define LINK_ATTR_PHYS_SWITCH_ID ((uint64_t) 1 << 36) +#define LINK_ATTR_GSO_MAX_SEGS ((uint64_t) 1 << 37) +#define LINK_ATTR_GSO_MAX_SIZE ((uint64_t) 1 << 38) +#define LINK_ATTR_LINKINFO_SLAVE_KIND ((uint64_t) 1 << 39) +#define LINK_ATTR_PERMANENT_ADDR ((uint64_t) 1 << 40) + +static struct nl_cache_ops rtnl_link_ops; +static struct nl_object_ops link_obj_ops; +/** @endcond */ + +struct rtnl_link *link_lookup(struct nl_cache *cache, int ifindex) +{ + if (!cache) { + cache = __nl_cache_mngt_require("route/link"); + if (!cache) + return NULL; + } + + return rtnl_link_get(cache, ifindex); +} + +static struct rtnl_link_af_ops *af_lookup_and_alloc(struct rtnl_link *link, + int family) +{ + struct rtnl_link_af_ops *af_ops; + + af_ops = rtnl_link_af_ops_lookup(family); + if (!af_ops) + return NULL; + + if (!rtnl_link_af_alloc(link, af_ops)) { + rtnl_link_af_ops_put(af_ops); + return NULL; + } + + return af_ops; +} + +static int af_free(struct rtnl_link *link, struct rtnl_link_af_ops *ops, + void *data, void *arg) +{ + if (ops->ao_free) + ops->ao_free(link, data); + + rtnl_link_af_ops_put(ops); + + return 0; +} + +static int af_request_type(int af_type, struct rtnl_link *changes) +{ + struct rtnl_link_af_ops *ops; + + ops = rtnl_link_af_ops_lookup(af_type); + if (ops && ops->ao_override_rtm && ops->ao_override_rtm(changes)) + return RTM_SETLINK; + + return RTM_NEWLINK; +} + +static int af_clone(struct rtnl_link *link, struct rtnl_link_af_ops *ops, + void *data, void *arg) +{ + struct rtnl_link *dst = arg; + + if (ops->ao_clone && + !(dst->l_af_data[ops->ao_family] = ops->ao_clone(dst, data))) + return -NLE_NOMEM; + + return 0; +} + +static int af_fill(struct rtnl_link *link, struct rtnl_link_af_ops *ops, + void *data, void *arg) +{ + struct nl_msg *msg = arg; + struct nlattr *af_attr = NULL; + int err; + + if (!ops->ao_fill_af) + return 0; + + if (!ops->ao_fill_af_no_nest) + if (!(af_attr = nla_nest_start(msg, ops->ao_family))) + return -NLE_MSGSIZE; + + if ((err = ops->ao_fill_af(link, arg, data)) < 0) + return err; + + if (!ops->ao_fill_af_no_nest) + nla_nest_end(msg, af_attr); + + return 0; +} + +static int af_fill_pi(struct rtnl_link *link, struct rtnl_link_af_ops *ops, + void *data, void *arg) +{ + struct nl_msg *msg = arg; + struct nlattr *pi_attr; + int err, pi_type = IFLA_PROTINFO; + + if (!ops->ao_fill_pi) + return 0; + + if (ops->ao_fill_pi_flags > 0) + pi_type |= ops->ao_fill_pi_flags; + + if (!(pi_attr = nla_nest_start(msg, pi_type))) + return -NLE_MSGSIZE; + + if ((err = ops->ao_fill_pi(link, arg, data)) < 0) + return err; + + nla_nest_end(msg, pi_attr); + + return 0; +} + +static int af_dump_line(struct rtnl_link *link, struct rtnl_link_af_ops *ops, + void *data, void *arg) +{ + struct nl_dump_params *p = arg; + + if (ops->ao_dump[NL_DUMP_LINE]) + ops->ao_dump[NL_DUMP_LINE](link, p, data); + + return 0; +} + +static int af_dump_details(struct rtnl_link *link, struct rtnl_link_af_ops *ops, + void *data, void *arg) +{ + struct nl_dump_params *p = arg; + + if (ops->ao_dump[NL_DUMP_DETAILS]) + ops->ao_dump[NL_DUMP_DETAILS](link, p, data); + + return 0; +} + +static int af_dump_stats(struct rtnl_link *link, struct rtnl_link_af_ops *ops, + void *data, void *arg) +{ + struct nl_dump_params *p = arg; + + if (ops->ao_dump[NL_DUMP_STATS]) + ops->ao_dump[NL_DUMP_STATS](link, p, data); + + return 0; +} + +static int do_foreach_af(struct rtnl_link *link, + int (*cb)(struct rtnl_link *, + struct rtnl_link_af_ops *, void *, void *), + void *arg) +{ + int i, err; + + for (i = 0; i < AF_MAX; i++) { + if (link->l_af_data[i]) { + _nl_auto_rtnl_link_af_ops struct rtnl_link_af_ops *ops = NULL; + + if (!(ops = rtnl_link_af_ops_lookup(i))) + BUG(); + + err = cb(link, ops, link->l_af_data[i], arg); + if (err < 0) + return err; + } + } + + return 0; +} + +static void release_link_info(struct rtnl_link *link) +{ + struct rtnl_link_info_ops *io = link->l_info_ops; + + if (io != NULL) { + if (io->io_free) + io->io_free(link); + else { + /* Catch missing io_free() implementations */ + BUG_ON(link->l_info); + } + rtnl_link_info_ops_put(io); + link->l_info_ops = NULL; + } +} + +static void link_free_data(struct nl_object *c) +{ + struct rtnl_link *link = nl_object_priv(c); + + if (link) { + release_link_info(link); + + /* proto info af reference */ + rtnl_link_af_ops_put(link->l_af_ops); + + nl_addr_put(link->l_addr); + nl_addr_put(link->l_bcast); + nl_addr_put(link->l_paddr); + + free(link->l_ifalias); + free(link->l_info_kind); + free(link->l_info_slave_kind); + + do_foreach_af(link, af_free, NULL); + + nl_data_free(link->l_phys_port_id); + nl_data_free(link->l_phys_switch_id); + + if (link->ce_mask & LINK_ATTR_VF_LIST) + rtnl_link_sriov_free_data(link); + } +} + +static int link_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_link *dst = nl_object_priv(_dst); + struct rtnl_link *src = nl_object_priv(_src); + int err; + + dst->l_addr = NULL; + dst->l_bcast = NULL; + dst->l_paddr = NULL; + dst->l_info_kind = NULL; + dst->l_info_slave_kind = NULL; + dst->l_info_ops = NULL; + memset(dst->l_af_data, 0, sizeof (dst->l_af_data)); + dst->l_info = NULL; + dst->l_ifalias = NULL; + dst->l_af_ops = NULL; + dst->l_phys_port_id = NULL; + dst->l_phys_switch_id = NULL; + dst->l_vf_list = NULL; + + if (src->l_addr) + if (!(dst->l_addr = nl_addr_clone(src->l_addr))) + return -NLE_NOMEM; + + if (src->l_bcast) + if (!(dst->l_bcast = nl_addr_clone(src->l_bcast))) + return -NLE_NOMEM; + + if (src->l_paddr) + if (!(dst->l_paddr = nl_addr_clone(src->l_paddr))) + return -NLE_NOMEM; + + if (src->l_ifalias) + if (!(dst->l_ifalias = strdup(src->l_ifalias))) + return -NLE_NOMEM; + + if (src->l_info_kind) + if (!(dst->l_info_kind = strdup(src->l_info_kind))) + return -NLE_NOMEM; + + if (src->l_info_slave_kind) + if (!(dst->l_info_slave_kind = strdup(src->l_info_slave_kind))) + return -NLE_NOMEM; + + if (src->l_info_ops) { + + rtnl_link_info_ops_get(src->l_info_ops); + dst->l_info_ops = src->l_info_ops; + + if (src->l_info_ops->io_clone) { + err = src->l_info_ops->io_clone(dst, src); + if (err < 0) + return err; + } + } + + if ((err = do_foreach_af(src, af_clone, dst)) < 0) + return err; + + if (src->l_af_ops) + dst->l_af_ops = af_lookup_and_alloc(dst, src->l_af_ops->ao_family); + + if (src->l_phys_port_id) + if (!(dst->l_phys_port_id = nl_data_clone(src->l_phys_port_id))) + return -NLE_NOMEM; + + if (src->l_phys_switch_id) + if (!(dst->l_phys_switch_id = nl_data_clone(src->l_phys_switch_id))) + return -NLE_NOMEM; + + if (src->ce_mask & LINK_ATTR_VF_LIST) + if ((err = rtnl_link_sriov_clone(dst, src)) < 0) + return err; + + return 0; +} + +struct nla_policy rtln_link_policy[IFLA_MAX+1] = { + [IFLA_IFNAME] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, + [IFLA_MTU] = { .type = NLA_U32 }, + [IFLA_TXQLEN] = { .type = NLA_U32 }, + [IFLA_LINK] = { .type = NLA_U32 }, + [IFLA_WEIGHT] = { .type = NLA_U32 }, + [IFLA_MASTER] = { .type = NLA_U32 }, + [IFLA_OPERSTATE] = { .type = NLA_U8 }, + [IFLA_LINKMODE] = { .type = NLA_U8 }, + [IFLA_LINKINFO] = { .type = NLA_NESTED }, + [IFLA_QDISC] = { .type = NLA_STRING, + .maxlen = IFQDISCSIZ }, + [IFLA_STATS] = { .minlen = _nl_offsetofend (struct rtnl_link_stats, tx_compressed) }, + [IFLA_STATS64] = { .minlen = _nl_offsetofend (struct rtnl_link_stats64, tx_compressed) }, + [IFLA_MAP] = { .minlen = sizeof(struct rtnl_link_ifmap) }, + [IFLA_IFALIAS] = { .type = NLA_STRING, .maxlen = IFALIASZ }, + [IFLA_NUM_VF] = { .type = NLA_U32 }, + [IFLA_VFINFO_LIST] = { .type = NLA_NESTED }, + [IFLA_AF_SPEC] = { .type = NLA_NESTED }, + [IFLA_PROMISCUITY] = { .type = NLA_U32 }, + [IFLA_NUM_TX_QUEUES] = { .type = NLA_U32 }, + [IFLA_NUM_RX_QUEUES] = { .type = NLA_U32 }, + [IFLA_GSO_MAX_SEGS] = { .type = NLA_U32 }, + [IFLA_GSO_MAX_SIZE] = { .type = NLA_U32 }, + [IFLA_GROUP] = { .type = NLA_U32 }, + [IFLA_CARRIER] = { .type = NLA_U8 }, + [IFLA_CARRIER_CHANGES] = { .type = NLA_U32 }, + [IFLA_PHYS_PORT_ID] = { .type = NLA_UNSPEC }, + [IFLA_PHYS_PORT_NAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, + [IFLA_PHYS_SWITCH_ID] = { .type = NLA_UNSPEC }, + [IFLA_NET_NS_PID] = { .type = NLA_U32 }, + [IFLA_NET_NS_FD] = { .type = NLA_U32 }, +}; + +static struct nla_policy link_info_policy[IFLA_INFO_MAX+1] = { + [IFLA_INFO_KIND] = { .type = NLA_STRING }, + [IFLA_INFO_DATA] = { .type = NLA_NESTED }, + [IFLA_INFO_XSTATS] = { .type = NLA_NESTED }, +}; + +int rtnl_link_info_parse(struct rtnl_link *link, struct nlattr **tb) +{ + if (tb[IFLA_IFNAME] == NULL) + return -NLE_MISSING_ATTR; + + nla_strlcpy(link->l_name, tb[IFLA_IFNAME], IFNAMSIZ); + link->ce_mask |= LINK_ATTR_IFNAME; + + if (tb[IFLA_STATS]) { + struct rtnl_link_stats *st = nla_data(tb[IFLA_STATS]); + + link->l_stats[RTNL_LINK_RX_PACKETS] = st->rx_packets; + link->l_stats[RTNL_LINK_TX_PACKETS] = st->tx_packets; + link->l_stats[RTNL_LINK_RX_BYTES] = st->rx_bytes; + link->l_stats[RTNL_LINK_TX_BYTES] = st->tx_bytes; + link->l_stats[RTNL_LINK_RX_ERRORS] = st->rx_errors; + link->l_stats[RTNL_LINK_TX_ERRORS] = st->tx_errors; + link->l_stats[RTNL_LINK_RX_DROPPED] = st->rx_dropped; + link->l_stats[RTNL_LINK_TX_DROPPED] = st->tx_dropped; + link->l_stats[RTNL_LINK_MULTICAST] = st->multicast; + link->l_stats[RTNL_LINK_COLLISIONS] = st->collisions; + + link->l_stats[RTNL_LINK_RX_LEN_ERR] = st->rx_length_errors; + link->l_stats[RTNL_LINK_RX_OVER_ERR] = st->rx_over_errors; + link->l_stats[RTNL_LINK_RX_CRC_ERR] = st->rx_crc_errors; + link->l_stats[RTNL_LINK_RX_FRAME_ERR] = st->rx_frame_errors; + link->l_stats[RTNL_LINK_RX_FIFO_ERR] = st->rx_fifo_errors; + link->l_stats[RTNL_LINK_RX_MISSED_ERR] = st->rx_missed_errors; + + link->l_stats[RTNL_LINK_TX_ABORT_ERR] = st->tx_aborted_errors; + link->l_stats[RTNL_LINK_TX_CARRIER_ERR] = st->tx_carrier_errors; + link->l_stats[RTNL_LINK_TX_FIFO_ERR] = st->tx_fifo_errors; + link->l_stats[RTNL_LINK_TX_HBEAT_ERR] = st->tx_heartbeat_errors; + link->l_stats[RTNL_LINK_TX_WIN_ERR] = st->tx_window_errors; + + link->l_stats[RTNL_LINK_RX_COMPRESSED] = st->rx_compressed; + link->l_stats[RTNL_LINK_TX_COMPRESSED] = st->tx_compressed; + + /* beware: @st might not be the full struct, only fields up to + * tx_compressed are present. See _nl_offsetofend() above. */ + + if (_nla_len(tb[IFLA_STATS]) >= _nl_offsetofend (struct rtnl_link_stats, rx_nohandler)) + link->l_stats[RTNL_LINK_RX_NOHANDLER] = st->rx_nohandler; + else + link->l_stats[RTNL_LINK_RX_NOHANDLER] = 0; + + link->ce_mask |= LINK_ATTR_STATS; + } + + if (tb[IFLA_STATS64]) { + /* + * This structure contains 64bit parameters, and per the + * documentation in lib/attr.c, must not be accessed + * directly (because of alignment to 4 instead of 8). + * Therefore, copy the data to the stack and access it from + * there, where it will be aligned to 8. + */ + struct rtnl_link_stats64 st = { 0 }; + + nla_memcpy(&st, tb[IFLA_STATS64], sizeof (st)); + + link->l_stats[RTNL_LINK_RX_PACKETS] = st.rx_packets; + link->l_stats[RTNL_LINK_TX_PACKETS] = st.tx_packets; + link->l_stats[RTNL_LINK_RX_BYTES] = st.rx_bytes; + link->l_stats[RTNL_LINK_TX_BYTES] = st.tx_bytes; + link->l_stats[RTNL_LINK_RX_ERRORS] = st.rx_errors; + link->l_stats[RTNL_LINK_TX_ERRORS] = st.tx_errors; + link->l_stats[RTNL_LINK_RX_DROPPED] = st.rx_dropped; + link->l_stats[RTNL_LINK_TX_DROPPED] = st.tx_dropped; + link->l_stats[RTNL_LINK_MULTICAST] = st.multicast; + link->l_stats[RTNL_LINK_COLLISIONS] = st.collisions; + + link->l_stats[RTNL_LINK_RX_LEN_ERR] = st.rx_length_errors; + link->l_stats[RTNL_LINK_RX_OVER_ERR] = st.rx_over_errors; + link->l_stats[RTNL_LINK_RX_CRC_ERR] = st.rx_crc_errors; + link->l_stats[RTNL_LINK_RX_FRAME_ERR] = st.rx_frame_errors; + link->l_stats[RTNL_LINK_RX_FIFO_ERR] = st.rx_fifo_errors; + link->l_stats[RTNL_LINK_RX_MISSED_ERR] = st.rx_missed_errors; + + link->l_stats[RTNL_LINK_TX_ABORT_ERR] = st.tx_aborted_errors; + link->l_stats[RTNL_LINK_TX_CARRIER_ERR] = st.tx_carrier_errors; + link->l_stats[RTNL_LINK_TX_FIFO_ERR] = st.tx_fifo_errors; + link->l_stats[RTNL_LINK_TX_HBEAT_ERR] = st.tx_heartbeat_errors; + link->l_stats[RTNL_LINK_TX_WIN_ERR] = st.tx_window_errors; + + link->l_stats[RTNL_LINK_RX_COMPRESSED] = st.rx_compressed; + link->l_stats[RTNL_LINK_TX_COMPRESSED] = st.tx_compressed; + + /* beware: @st might not be the full struct, only fields up to + * tx_compressed are present. See _nl_offsetofend() above. */ + + link->l_stats[RTNL_LINK_RX_NOHANDLER] = st.rx_nohandler; + + link->ce_mask |= LINK_ATTR_STATS; + } + + if (tb[IFLA_TXQLEN]) { + link->l_txqlen = nla_get_u32(tb[IFLA_TXQLEN]); + link->ce_mask |= LINK_ATTR_TXQLEN; + } + + if (tb[IFLA_MTU]) { + link->l_mtu = nla_get_u32(tb[IFLA_MTU]); + link->ce_mask |= LINK_ATTR_MTU; + } + + if (tb[IFLA_ADDRESS]) { + link->l_addr = nl_addr_alloc_attr(tb[IFLA_ADDRESS], AF_UNSPEC); + if (link->l_addr == NULL) + return -NLE_NOMEM; + nl_addr_set_family(link->l_addr, + nl_addr_guess_family(link->l_addr)); + link->ce_mask |= LINK_ATTR_ADDR; + } + + if (tb[IFLA_PERM_ADDRESS]) { + link->l_paddr = nl_addr_alloc_attr(tb[IFLA_PERM_ADDRESS], AF_UNSPEC); + if (link->l_paddr == NULL) + return -NLE_NOMEM; + nl_addr_set_family(link->l_paddr, + nl_addr_guess_family(link->l_paddr)); + link->ce_mask |= LINK_ATTR_PERMANENT_ADDR; + } + + if (tb[IFLA_BROADCAST]) { + link->l_bcast = nl_addr_alloc_attr(tb[IFLA_BROADCAST], + AF_UNSPEC); + if (link->l_bcast == NULL) + return -NLE_NOMEM; + nl_addr_set_family(link->l_bcast, + nl_addr_guess_family(link->l_bcast)); + link->ce_mask |= LINK_ATTR_BRD; + } + + if (tb[IFLA_LINK]) { + link->l_link = nla_get_u32(tb[IFLA_LINK]); + link->ce_mask |= LINK_ATTR_LINK; + } + + if (tb[IFLA_LINK_NETNSID]) { + link->l_link_netnsid = nla_get_s32(tb[IFLA_LINK_NETNSID]); + link->ce_mask |= LINK_ATTR_LINK_NETNSID; + } + + if (tb[IFLA_WEIGHT]) { + link->l_weight = nla_get_u32(tb[IFLA_WEIGHT]); + link->ce_mask |= LINK_ATTR_WEIGHT; + } + + if (tb[IFLA_QDISC]) { + nla_strlcpy(link->l_qdisc, tb[IFLA_QDISC], IFQDISCSIZ); + link->ce_mask |= LINK_ATTR_QDISC; + } + + if (tb[IFLA_MAP]) { + nla_memcpy(&link->l_map, tb[IFLA_MAP], + sizeof(struct rtnl_link_ifmap)); + link->ce_mask |= LINK_ATTR_MAP; + } + + if (tb[IFLA_MASTER]) { + link->l_master = nla_get_u32(tb[IFLA_MASTER]); + link->ce_mask |= LINK_ATTR_MASTER; + } + + if (tb[IFLA_CARRIER]) { + link->l_carrier = nla_get_u8(tb[IFLA_CARRIER]); + link->ce_mask |= LINK_ATTR_CARRIER; + } + + if (tb[IFLA_CARRIER_CHANGES]) { + link->l_carrier_changes = nla_get_u32(tb[IFLA_CARRIER_CHANGES]); + link->ce_mask |= LINK_ATTR_CARRIER_CHANGES; + } + + if (tb[IFLA_OPERSTATE]) { + link->l_operstate = nla_get_u8(tb[IFLA_OPERSTATE]); + link->ce_mask |= LINK_ATTR_OPERSTATE; + } + + if (tb[IFLA_LINKMODE]) { + link->l_linkmode = nla_get_u8(tb[IFLA_LINKMODE]); + link->ce_mask |= LINK_ATTR_LINKMODE; + } + + if (tb[IFLA_IFALIAS]) { + link->l_ifalias = nla_strdup(tb[IFLA_IFALIAS]); + if (link->l_ifalias == NULL) + return -NLE_NOMEM; + link->ce_mask |= LINK_ATTR_IFALIAS; + } + + if (tb[IFLA_NET_NS_FD]) { + link->l_ns_fd = nla_get_u32(tb[IFLA_NET_NS_FD]); + link->ce_mask |= LINK_ATTR_NS_FD; + } + + if (tb[IFLA_NET_NS_PID]) { + link->l_ns_pid = nla_get_u32(tb[IFLA_NET_NS_PID]); + link->ce_mask |= LINK_ATTR_NS_PID; + } + + return 0; +} + +static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *n, struct nl_parser_param *pp) +{ + _nl_auto_rtnl_link struct rtnl_link *link = NULL; + struct nla_policy real_link_policy[ARRAY_SIZE(rtln_link_policy)]; + struct nla_policy *link_policy = rtln_link_policy; + struct rtnl_link_af_ops *af_ops_family; + struct ifinfomsg *ifi; + struct nlattr *tb[IFLA_MAX+1]; + int err, family; + + link = rtnl_link_alloc(); + if (link == NULL) + return -NLE_NOMEM; + + link->ce_msgtype = n->nlmsg_type; + + if (!nlmsg_valid_hdr(n, sizeof(*ifi))) + return -NLE_MSG_TOOSHORT; + + ifi = nlmsg_data(n); + link->l_family = family = ifi->ifi_family; + link->l_arptype = ifi->ifi_type; + link->l_index = ifi->ifi_index; + link->l_flags = ifi->ifi_flags; + link->l_change = ifi->ifi_change; + link->ce_mask = (LINK_ATTR_FAMILY | + LINK_ATTR_ARPTYPE| LINK_ATTR_IFINDEX | + LINK_ATTR_FLAGS | LINK_ATTR_CHANGE); + + if ((link->l_af_ops = af_lookup_and_alloc(link, family))) { + if (link->l_af_ops->ao_protinfo_policy) { + _NL_STATIC_ASSERT (sizeof(rtln_link_policy) == sizeof(real_link_policy)); + memcpy(&real_link_policy, rtln_link_policy, sizeof(rtln_link_policy)); + memcpy(&real_link_policy[IFLA_PROTINFO], + link->l_af_ops->ao_protinfo_policy, + sizeof(struct nla_policy)); + link_policy = real_link_policy; + } + } + + af_ops_family = link->l_af_ops; + + err = nlmsg_parse(n, sizeof(*ifi), tb, IFLA_MAX, link_policy); + if (err < 0) + return err; + + err = rtnl_link_info_parse(link, tb); + if (err < 0) + return err; + + if (tb[IFLA_NUM_VF]) { + link->l_num_vf = nla_get_u32(tb[IFLA_NUM_VF]); + link->ce_mask |= LINK_ATTR_NUM_VF; + if (link->l_num_vf && tb[IFLA_VFINFO_LIST]) { + if ((err = rtnl_link_sriov_parse_vflist(link, tb)) < 0) + return err; + link->ce_mask |= LINK_ATTR_VF_LIST; + } + } + + if (tb[IFLA_LINKINFO]) { + struct nlattr *li[IFLA_INFO_MAX+1]; + + err = nla_parse_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO], + link_info_policy); + if (err < 0) + return err; + + if (li[IFLA_INFO_KIND]) { + struct rtnl_link_info_ops *ops; + const char *kind = nla_get_string(li[IFLA_INFO_KIND]); + int af; + + err = rtnl_link_set_type(link, kind); + if (err < 0) + return err; + + if ( (af = nl_str2af(kind)) >= 0 + && !link->l_af_ops + && (link->l_af_ops = af_lookup_and_alloc(link, af))) { + link->l_family = af; + if (link->l_af_ops->ao_protinfo_policy) + tb[IFLA_PROTINFO] = (struct nlattr *)link->l_af_ops->ao_protinfo_policy; + } + + ops = link->l_info_ops; + + if (ops) { + if (ops->io_parse && + (li[IFLA_INFO_DATA] || li[IFLA_INFO_XSTATS])) { + err = ops->io_parse(link, li[IFLA_INFO_DATA], + li[IFLA_INFO_XSTATS]); + if (err < 0) + return err; + } else { + /* XXX: Warn about unparsed info? */ + } + } + + link->ce_mask |= LINK_ATTR_LINKINFO; + } + + if (li[IFLA_INFO_SLAVE_KIND]) { + const char *kind = nla_get_string(li[IFLA_INFO_SLAVE_KIND]); + + err = rtnl_link_set_slave_type(link, kind); + if (err < 0) + return err; + + link->ce_mask |= LINK_ATTR_LINKINFO_SLAVE_KIND; + } + } + + if ( tb[IFLA_PROTINFO] + && link->l_af_ops + && link->l_af_ops->ao_parse_protinfo) { + err = link->l_af_ops->ao_parse_protinfo(link, tb[IFLA_PROTINFO], + link->l_af_data[link->l_family]); + if (err < 0) + return err; + link->ce_mask |= LINK_ATTR_PROTINFO; + } + + if (tb[IFLA_AF_SPEC]) { + /* parsing of IFLA_AF_SPEC is dependent on the family used + * in the request message. + */ + if ( af_ops_family + && af_ops_family->ao_parse_af_full) { + err = af_ops_family->ao_parse_af_full(link, + tb[IFLA_AF_SPEC], + link->l_af_data[af_ops_family->ao_family]); + if (err < 0) + return err; + link->ce_mask |= LINK_ATTR_AF_SPEC; + } else if (family == AF_UNSPEC) { + struct nlattr *af_attr; + int remaining; + + nla_for_each_nested(af_attr, tb[IFLA_AF_SPEC], remaining) { + _nl_auto_rtnl_link_af_ops struct rtnl_link_af_ops *af_ops = NULL; + + af_ops = af_lookup_and_alloc(link, nla_type(af_attr)); + if (af_ops && af_ops->ao_parse_af) { + char *af_data = link->l_af_data[nla_type(af_attr)]; + + err = af_ops->ao_parse_af(link, af_attr, af_data); + if (err < 0) + return err; + } + } + link->ce_mask |= LINK_ATTR_AF_SPEC; + } else { + NL_DBG(3, "IFLA_AF_SPEC parsing not implemented for family %d\n", + family); + } + } + + if (tb[IFLA_PROMISCUITY]) { + link->l_promiscuity = nla_get_u32(tb[IFLA_PROMISCUITY]); + link->ce_mask |= LINK_ATTR_PROMISCUITY; + } + + if (tb[IFLA_NUM_TX_QUEUES]) { + link->l_num_tx_queues = nla_get_u32(tb[IFLA_NUM_TX_QUEUES]); + link->ce_mask |= LINK_ATTR_NUM_TX_QUEUES; + } + + if (tb[IFLA_NUM_RX_QUEUES]) { + link->l_num_rx_queues = nla_get_u32(tb[IFLA_NUM_RX_QUEUES]); + link->ce_mask |= LINK_ATTR_NUM_RX_QUEUES; + } + + if (tb[IFLA_GSO_MAX_SEGS]) { + link->l_gso_max_segs = nla_get_u32(tb[IFLA_GSO_MAX_SEGS]); + link->ce_mask |= LINK_ATTR_GSO_MAX_SEGS; + } + + if (tb[IFLA_GSO_MAX_SIZE]) { + link->l_gso_max_size = nla_get_u32(tb[IFLA_GSO_MAX_SIZE]); + link->ce_mask |= LINK_ATTR_GSO_MAX_SIZE; + } + + if (tb[IFLA_GROUP]) { + link->l_group = nla_get_u32(tb[IFLA_GROUP]); + link->ce_mask |= LINK_ATTR_GROUP; + } + + if (tb[IFLA_PHYS_PORT_ID]) { + link->l_phys_port_id = nl_data_alloc_attr(tb[IFLA_PHYS_PORT_ID]); + if (link->l_phys_port_id == NULL) + return -NLE_NOMEM; + link->ce_mask |= LINK_ATTR_PHYS_PORT_ID; + } + + if (tb[IFLA_PHYS_PORT_NAME]) { + nla_strlcpy(link->l_phys_port_name, tb[IFLA_PHYS_PORT_NAME], IFNAMSIZ); + link->ce_mask |= LINK_ATTR_PHYS_PORT_NAME; + } + + if (tb[IFLA_PHYS_SWITCH_ID]) { + link->l_phys_switch_id = nl_data_alloc_attr(tb[IFLA_PHYS_SWITCH_ID]); + if (link->l_phys_switch_id == NULL) + return -NLE_NOMEM; + link->ce_mask |= LINK_ATTR_PHYS_SWITCH_ID; + } + + return pp->pp_cb((struct nl_object *) link, pp); +} + +static int link_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + _nl_auto_nl_msg struct nl_msg *msg = NULL; + int family = cache->c_iarg1; + struct ifinfomsg hdr = { .ifi_family = family }; + struct rtnl_link_af_ops *ops; + int err; + __u32 ext_filter_mask = RTEXT_FILTER_VF; + + msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_DUMP); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &hdr, sizeof(hdr), NLMSG_ALIGNTO) < 0) + return -NLE_MSGSIZE; + + ops = rtnl_link_af_ops_lookup(family); + if (ops && ops->ao_get_af) { + err = ops->ao_get_af(msg, &ext_filter_mask); + if (err < 0) + return err; + } + + if (ext_filter_mask) { + err = nla_put(msg, IFLA_EXT_MASK, sizeof(ext_filter_mask), &ext_filter_mask); + if (err < 0) + return err; + } + + err = nl_send_auto(sk, msg); + if (err < 0) + return 0; + + return 0; +} + +static void link_dump_line(struct nl_object *obj, struct nl_dump_params *p) +{ + char buf[128]; + struct nl_cache *cache = obj->ce_cache; + struct rtnl_link *link = (struct rtnl_link *) obj; + int fetched_cache = 0; + + if (!cache) { + cache = nl_cache_mngt_require_safe("route/link"); + fetched_cache = 1; + } + + if (link->l_family != AF_UNSPEC) + nl_dump_line(p, "%s ", nl_af2str(link->l_family, buf, sizeof(buf))); + + nl_dump_line(p, "%s %s ", link->l_name, + nl_llproto2str(link->l_arptype, buf, sizeof(buf))); + + if (link->l_addr && !nl_addr_iszero(link->l_addr)) + nl_dump(p, "%s ", nl_addr2str(link->l_addr, buf, sizeof(buf))); + + if (link->l_paddr && !nl_addr_iszero(link->l_paddr)) + nl_dump(p, "permanent address %s ", nl_addr2str(link->l_paddr, buf, sizeof(buf))); + + if (link->ce_mask & LINK_ATTR_MASTER) { + if (cache) { + _nl_auto_rtnl_link struct rtnl_link *master = rtnl_link_get(cache, link->l_master); + + nl_dump(p, "master %s ", master ? master->l_name : "inv"); + } else + nl_dump(p, "master %d ", link->l_master); + } + + rtnl_link_flags2str(link->l_flags, buf, sizeof(buf)); + if (buf[0]) + nl_dump(p, "<%s> ", buf); + + if (link->ce_mask & LINK_ATTR_LINK) { + if ( cache + && !(link->ce_mask & LINK_ATTR_LINK_NETNSID)) { + _nl_auto_rtnl_link struct rtnl_link *ll = rtnl_link_get(cache, link->l_link); + + nl_dump(p, "slave-of %s ", ll ? ll->l_name : "NONE"); + } else + nl_dump(p, "slave-of %d ", link->l_link); + } + if (link->ce_mask & LINK_ATTR_LINK_NETNSID) + nl_dump(p, "link-netnsid %d ", link->l_link_netnsid); + + if (link->ce_mask & LINK_ATTR_GROUP) + nl_dump(p, "group %u ", link->l_group); + + if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_LINE]) + link->l_info_ops->io_dump[NL_DUMP_LINE](link, p); + + do_foreach_af(link, af_dump_line, p); + + nl_dump(p, "\n"); + + if (fetched_cache) + nl_cache_put(cache); +} + +static void link_dump_details(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_link *link = (struct rtnl_link *) obj; + char buf[64]; + + link_dump_line(obj, p); + + nl_dump_line(p, " mtu %u ", link->l_mtu); + nl_dump(p, "txqlen %u weight %u ", link->l_txqlen, link->l_weight); + + if (link->ce_mask & LINK_ATTR_QDISC) + nl_dump(p, "qdisc %s ", link->l_qdisc); + + if (link->ce_mask & LINK_ATTR_MAP && link->l_map.lm_irq) + nl_dump(p, "irq %u ", link->l_map.lm_irq); + + if (link->ce_mask & LINK_ATTR_IFINDEX) + nl_dump(p, "index %u ", link->l_index); + + if (link->ce_mask & LINK_ATTR_PROMISCUITY && link->l_promiscuity > 0) + nl_dump(p, "promisc-mode (%u users) ", link->l_promiscuity); + + nl_dump(p, "\n"); + + if (link->ce_mask & LINK_ATTR_IFALIAS) + nl_dump_line(p, " alias %s\n", link->l_ifalias); + + nl_dump_line(p, " "); + + if (link->ce_mask & LINK_ATTR_NUM_TX_QUEUES) + nl_dump(p, "txq %u ", link->l_num_tx_queues); + + if (link->ce_mask & LINK_ATTR_NUM_RX_QUEUES) + nl_dump(p, "rxq %u ", link->l_num_rx_queues); + + if (link->ce_mask & LINK_ATTR_BRD) + nl_dump(p, "brd %s ", nl_addr2str(link->l_bcast, buf, + sizeof(buf))); + + if ((link->ce_mask & LINK_ATTR_OPERSTATE) && + link->l_operstate != IF_OPER_UNKNOWN) { + rtnl_link_operstate2str(link->l_operstate, buf, sizeof(buf)); + nl_dump(p, "state %s ", buf); + } + + if (link->ce_mask & LINK_ATTR_NUM_VF) + nl_dump(p, "num-vf %u ", link->l_num_vf); + + nl_dump(p, "mode %s ", + rtnl_link_mode2str(link->l_linkmode, buf, sizeof(buf))); + + nl_dump(p, "carrier %s", + rtnl_link_carrier2str(link->l_carrier, buf, sizeof(buf))); + + if (link->ce_mask & LINK_ATTR_CARRIER_CHANGES) + nl_dump(p, " carrier-changes %u", link->l_carrier_changes); + + nl_dump(p, "\n"); + + if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_DETAILS]) + link->l_info_ops->io_dump[NL_DUMP_DETAILS](link, p); + + do_foreach_af(link, af_dump_details, p); + + if (link->ce_mask & LINK_ATTR_VF_LIST) + rtnl_link_sriov_dump_details(link, p); +} + +static void link_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_link *link = (struct rtnl_link *) obj; + char *unit, fmt[64]; + float res; + + link_dump_details(obj, p); + + nl_dump_line(p, " Stats: bytes packets errors " + " dropped fifo-err compressed\n"); + + res = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_RX_BYTES], &unit); + + strcpy(fmt, " RX %X.2f %s %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 "\n"); + fmt[9] = *unit == 'B' ? '9' : '7'; + + nl_dump_line(p, fmt, res, unit, + link->l_stats[RTNL_LINK_RX_PACKETS], + link->l_stats[RTNL_LINK_RX_ERRORS], + link->l_stats[RTNL_LINK_RX_DROPPED], + link->l_stats[RTNL_LINK_RX_FIFO_ERR], + link->l_stats[RTNL_LINK_RX_COMPRESSED]); + + res = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_TX_BYTES], &unit); + + strcpy(fmt, " TX %X.2f %s %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 "\n"); + fmt[9] = *unit == 'B' ? '9' : '7'; + + nl_dump_line(p, fmt, res, unit, + link->l_stats[RTNL_LINK_TX_PACKETS], + link->l_stats[RTNL_LINK_TX_ERRORS], + link->l_stats[RTNL_LINK_TX_DROPPED], + link->l_stats[RTNL_LINK_TX_FIFO_ERR], + link->l_stats[RTNL_LINK_TX_COMPRESSED]); + + nl_dump_line(p, " Errors: length over crc " + " frame missed multicast\n"); + + nl_dump_line(p, " RX %10" PRIu64 " %10" PRIu64 " %10" + PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" + PRIu64 "\n", + link->l_stats[RTNL_LINK_RX_LEN_ERR], + link->l_stats[RTNL_LINK_RX_OVER_ERR], + link->l_stats[RTNL_LINK_RX_CRC_ERR], + link->l_stats[RTNL_LINK_RX_FRAME_ERR], + link->l_stats[RTNL_LINK_RX_MISSED_ERR], + link->l_stats[RTNL_LINK_MULTICAST]); + + nl_dump_line(p, " aborted carrier heartbeat " + " window collision\n"); + + nl_dump_line(p, " TX %10" PRIu64 " %10" PRIu64 " %10" + PRIu64 " %10" PRIu64 " %10" PRIu64 "\n", + link->l_stats[RTNL_LINK_TX_ABORT_ERR], + link->l_stats[RTNL_LINK_TX_CARRIER_ERR], + link->l_stats[RTNL_LINK_TX_HBEAT_ERR], + link->l_stats[RTNL_LINK_TX_WIN_ERR], + link->l_stats[RTNL_LINK_COLLISIONS]); + + if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_STATS]) + link->l_info_ops->io_dump[NL_DUMP_STATS](link, p); + + do_foreach_af(link, af_dump_stats, p); + + if (link->ce_mask & LINK_ATTR_VF_LIST) + rtnl_link_sriov_dump_stats(link, p); +} + +#if 0 +static int link_handle_event(struct nl_object *a, struct rtnl_link_event_cb *cb) +{ + struct rtnl_link *l = (struct rtnl_link *) a; + struct nl_cache *c = dp_cache(a); + int nevents = 0; + + if (l->l_change == ~0U) { + if (l->ce_msgtype == RTM_NEWLINK) + cb->le_register(l); + else + cb->le_unregister(l); + + return 1; + } + + if (l->l_change & IFF_SLAVE) { + if (l->l_flags & IFF_SLAVE) { + struct rtnl_link *m = rtnl_link_get(c, l->l_master); + cb->le_new_bonding(l, m); + if (m) + rtnl_link_put(m); + } else + cb->le_cancel_bonding(l); + } + +#if 0 + if (l->l_change & IFF_UP && l->l_change & IFF_RUNNING) + dp_dump_line(p, line++, "link %s changed state to %s.\n", + l->l_name, l->l_flags & IFF_UP ? "up" : "down"); + + if (l->l_change & IFF_PROMISC) { + dp_new_line(p, line++); + dp_dump(p, "link %s %s promiscuous mode.\n", + l->l_name, l->l_flags & IFF_PROMISC ? "entered" : "left"); + } + + if (line == 0) + dp_dump_line(p, line++, "link %s sent unknown event.\n", + l->l_name); +#endif + + return nevents; +} +#endif + + +static void link_keygen(struct nl_object *obj, uint32_t *hashkey, + uint32_t table_sz) +{ + struct rtnl_link *link = (struct rtnl_link *) obj; + unsigned int lkey_sz; + struct link_hash_key { + uint32_t l_index; + uint32_t l_family; + } _nl_packed lkey; + + lkey_sz = sizeof(lkey); + lkey.l_index = link->l_index; + lkey.l_family = link->l_family; + + *hashkey = nl_hash(&lkey, lkey_sz, 0) % table_sz; + + NL_DBG(5, "link %p key (dev %d fam %d) keysz %d, hash 0x%x\n", + link, lkey.l_index, lkey.l_family, lkey_sz, *hashkey); + + return; +} + +static uint64_t link_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct rtnl_link *a = (struct rtnl_link *) _a; + struct rtnl_link *b = (struct rtnl_link *) _b; + uint64_t diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(LINK_ATTR_IFINDEX, a->l_index != b->l_index); + diff |= _DIFF(LINK_ATTR_MTU, a->l_mtu != b->l_mtu); + diff |= _DIFF(LINK_ATTR_LINK, a->l_link != b->l_link); + diff |= _DIFF(LINK_ATTR_LINK_NETNSID, + a->l_link_netnsid != b->l_link_netnsid); + diff |= _DIFF(LINK_ATTR_TXQLEN, a->l_txqlen != b->l_txqlen); + diff |= _DIFF(LINK_ATTR_WEIGHT, a->l_weight != b->l_weight); + diff |= _DIFF(LINK_ATTR_MASTER, a->l_master != b->l_master); + diff |= _DIFF(LINK_ATTR_FAMILY, a->l_family != b->l_family); + diff |= _DIFF(LINK_ATTR_OPERSTATE, a->l_operstate != b->l_operstate); + diff |= _DIFF(LINK_ATTR_LINKMODE, a->l_linkmode != b->l_linkmode); + diff |= _DIFF(LINK_ATTR_QDISC, strcmp(a->l_qdisc, b->l_qdisc)); + diff |= _DIFF(LINK_ATTR_IFNAME, strcmp(a->l_name, b->l_name)); + diff |= _DIFF(LINK_ATTR_ADDR, nl_addr_cmp(a->l_addr, b->l_addr)); + diff |= _DIFF(LINK_ATTR_BRD, nl_addr_cmp(a->l_bcast, b->l_bcast)); + diff |= _DIFF(LINK_ATTR_PERMANENT_ADDR, nl_addr_cmp(a->l_paddr, b->l_paddr)); + diff |= _DIFF(LINK_ATTR_IFALIAS, strcmp(a->l_ifalias, b->l_ifalias)); + diff |= _DIFF(LINK_ATTR_NUM_VF, a->l_num_vf != b->l_num_vf); + diff |= _DIFF(LINK_ATTR_PROMISCUITY, + a->l_promiscuity != b->l_promiscuity); + diff |= _DIFF(LINK_ATTR_NUM_TX_QUEUES, + a->l_num_tx_queues != b->l_num_tx_queues); + diff |= _DIFF(LINK_ATTR_NUM_RX_QUEUES, + a->l_num_rx_queues != b->l_num_rx_queues); + diff |= _DIFF(LINK_ATTR_GROUP, a->l_group != b->l_group); + + if (flags & LOOSE_COMPARISON) + diff |= _DIFF(LINK_ATTR_FLAGS, + (a->l_flags ^ b->l_flags) & b->l_flag_mask); + else + diff |= _DIFF(LINK_ATTR_FLAGS, a->l_flags != b->l_flags); + + /* + * Compare LINK_ATTR_PROTINFO af_data + */ + if (a->l_family == b->l_family) { + if (rtnl_link_af_data_compare(a, b, a->l_family) != 0) + goto protinfo_mismatch; + } + + diff |= _DIFF(LINK_ATTR_LINKINFO, rtnl_link_info_data_compare(a, b, flags) != 0); +out: + return diff; + +protinfo_mismatch: + diff |= _DIFF(LINK_ATTR_PROTINFO, 1); + goto out; + +#undef _DIFF +} + +static const struct trans_tbl link_attrs[] = { + __ADD(LINK_ATTR_MTU, mtu), + __ADD(LINK_ATTR_LINK, link), + __ADD(LINK_ATTR_TXQLEN, txqlen), + __ADD(LINK_ATTR_WEIGHT, weight), + __ADD(LINK_ATTR_MASTER, master), + __ADD(LINK_ATTR_QDISC, qdisc), + __ADD(LINK_ATTR_MAP, map), + __ADD(LINK_ATTR_ADDR, address), + __ADD(LINK_ATTR_BRD, broadcast), + __ADD(LINK_ATTR_FLAGS, flags), + __ADD(LINK_ATTR_IFNAME, name), + __ADD(LINK_ATTR_IFINDEX, ifindex), + __ADD(LINK_ATTR_FAMILY, family), + __ADD(LINK_ATTR_ARPTYPE, arptype), + __ADD(LINK_ATTR_STATS, stats), + __ADD(LINK_ATTR_CHANGE, change), + __ADD(LINK_ATTR_OPERSTATE, operstate), + __ADD(LINK_ATTR_LINKMODE, linkmode), + __ADD(LINK_ATTR_IFALIAS, ifalias), + __ADD(LINK_ATTR_NUM_VF, num_vf), + __ADD(LINK_ATTR_PROMISCUITY, promiscuity), + __ADD(LINK_ATTR_NUM_TX_QUEUES, num_tx_queues), + __ADD(LINK_ATTR_NUM_RX_QUEUES, num_rx_queues), + __ADD(LINK_ATTR_GSO_MAX_SEGS, gso_max_segs), + __ADD(LINK_ATTR_GSO_MAX_SIZE, gso_max_size), + __ADD(LINK_ATTR_GROUP, group), + __ADD(LINK_ATTR_CARRIER, carrier), + __ADD(LINK_ATTR_CARRIER_CHANGES, carrier_changes), + __ADD(LINK_ATTR_PHYS_PORT_ID, phys_port_id), + __ADD(LINK_ATTR_PHYS_PORT_NAME, phys_port_name), + __ADD(LINK_ATTR_PHYS_SWITCH_ID, phys_switch_id), + __ADD(LINK_ATTR_NS_FD, ns_fd), + __ADD(LINK_ATTR_NS_PID, ns_pid), + __ADD(LINK_ATTR_LINK_NETNSID, link_netnsid), +}; + +static char *link_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, link_attrs, + ARRAY_SIZE(link_attrs)); +} + +/** + * @name Get / List + * @{ + */ + + +/** + * Allocate link cache and fill in all configured links. + * @arg sk Netlink socket. + * @arg family Link address family or AF_UNSPEC + * @arg result Pointer to store resulting cache. + * @arg flags Flags to set in link cache before filling + * + * Allocates and initializes a new link cache. If \c sk is valid, a netlink + * message is sent to the kernel requesting a full dump of all configured + * links. The returned messages are parsed and filled into the cache. If + * the operation succeeds, the resulting cache will contain a link object for + * each link configured in the kernel. If \c sk is NULL, returns 0 but the + * cache is still empty. + * + * If \c family is set to an address family other than \c AF_UNSPEC the + * contents of the cache can be limited to a specific address family. + * Currently the following address families are supported: + * - AF_BRIDGE + * - AF_INET6 + * + * @route_doc{link_list, Get List of Links} + * @see rtnl_link_get() + * @see rtnl_link_get_by_name() + * @return 0 on success or a negative error code. + */ +int rtnl_link_alloc_cache_flags(struct nl_sock *sk, int family, + struct nl_cache **result, unsigned int flags) +{ + struct nl_cache * cache; + int err; + + cache = nl_cache_alloc(&rtnl_link_ops); + if (!cache) + return -NLE_NOMEM; + + cache->c_iarg1 = family; + + if (flags) + nl_cache_set_flags(cache, flags); + + if (sk && (err = nl_cache_refill(sk, cache)) < 0) { + nl_cache_free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** + * Allocate link cache and fill in all configured links. + * @arg sk Netlink socket. + * @arg family Link address family or AF_UNSPEC + * @arg result Pointer to store resulting cache. + * + * Allocates and initializes a new link cache. If \c sk is valid, a netlink + * message is sent to the kernel requesting a full dump of all configured + * links. The returned messages are parsed and filled into the cache. If + * the operation succeeds, the resulting cache will contain a link object for + * each link configured in the kernel. If \c sk is NULL, returns 0 but the + * cache is still empty. + * + * If \c family is set to an address family other than \c AF_UNSPEC the + * contents of the cache can be limited to a specific address family. + * Currently the following address families are supported: + * - AF_BRIDGE + * - AF_INET6 + * + * @route_doc{link_list, Get List of Links} + * @see rtnl_link_get() + * @see rtnl_link_get_by_name() + * @return 0 on success or a negative error code. + */ +int rtnl_link_alloc_cache(struct nl_sock *sk, int family, struct nl_cache **result) +{ + return rtnl_link_alloc_cache_flags(sk, family, result, 0); +} + + +/** + * Lookup link in cache by interface index + * @arg cache Link cache + * @arg ifindex Interface index + * + * Searches through the provided cache looking for a link with matching + * interface index. + * + * @attention The reference counter of the returned link object will be + * incremented. Use rtnl_link_put() to release the reference. + * + * @route_doc{link_list, Get List of Links} + * @see rtnl_link_get_by_name() + * @return Link object or NULL if no match was found. + */ +struct rtnl_link *rtnl_link_get(struct nl_cache *cache, int ifindex) +{ + struct rtnl_link *link; + + if (cache->c_ops != &rtnl_link_ops) + return NULL; + + nl_list_for_each_entry(link, &cache->c_items, ce_list) { + if (link->l_index == ((unsigned)ifindex)) { + nl_object_get((struct nl_object *) link); + return link; + } + } + + return NULL; +} + +/** + * Lookup link in cache by link name + * @arg cache Link cache + * @arg name Name of link + * + * Searches through the provided cache looking for a link with matching + * link name + * + * @attention The reference counter of the returned link object will be + * incremented. Use rtnl_link_put() to release the reference. + * + * @route_doc{link_list, Get List of Links} + * @see rtnl_link_get() + * @return Link object or NULL if no match was found. + */ +struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *cache, + const char *name) +{ + struct rtnl_link *link; + + if (cache->c_ops != &rtnl_link_ops) + return NULL; + + nl_list_for_each_entry(link, &cache->c_items, ce_list) { + if (!strcmp(name, link->l_name)) { + nl_object_get((struct nl_object *) link); + return link; + } + } + + return NULL; +} + +/** + * Construct RTM_GETLINK netlink message + * @arg ifindex Interface index + * @arg name Name of link + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_link_get_kernel() + * with the exception that it will not send the message but return it in + * the provided return pointer instead. + * + * @see rtnl_link_get_kernel() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_build_get_request(int ifindex, const char *name, + struct nl_msg **result) +{ + _nl_auto_nl_msg struct nl_msg *msg = NULL; + struct ifinfomsg ifi; + __u32 vf_mask = RTEXT_FILTER_VF; + + if (ifindex <= 0 && !name) { + APPBUG("ifindex or name must be specified"); + return -NLE_MISSING_ATTR; + } + + memset(&ifi, 0, sizeof(ifi)); + + if (!(msg = nlmsg_alloc_simple(RTM_GETLINK, 0))) + return -NLE_NOMEM; + + if (ifindex > 0) + ifi.ifi_index = ifindex; + + _NL_RETURN_ON_PUT_ERR(nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO)); + + if (name) + _NL_RETURN_ON_PUT_ERR(nla_put_string(msg, IFLA_IFNAME, name)); + + _NL_RETURN_ON_PUT_ERR(nla_put(msg, IFLA_EXT_MASK, sizeof(vf_mask), &vf_mask)); + + *result = _nl_steal_pointer(&msg); + return 0; +} + +/** + * Get a link object directly from kernel + * @arg sk Netlink socket + * @arg ifindex Interface index + * @arg name Name of link + * @arg result Pointer to store resulting link object + * + * This function builds a \c RTM_GETLINK netlink message to request + * a specific link directly from the kernel. The returned answer is + * parsed into a struct rtnl_link object and returned via the result + * pointer or -NLE_OBJ_NOTFOUND is returned if no matching link was + * found. + * + * Older kernels do not support lookup by name. In that case, libnl + * will fail with -NLE_OPNOTSUPP. Note that previous version of libnl + * failed in this case with -NLE_INVAL. You can check libnl behavior + * using NL_CAPABILITY_ROUTE_LINK_GET_KERNEL_FAIL_OPNOTSUPP capability. + * + * @route_doc{link_direct_lookup, Lookup Single Link (Direct Lookup)} + * @return 0 on success or a negative error code. + */ +int rtnl_link_get_kernel(struct nl_sock *sk, int ifindex, const char *name, + struct rtnl_link **result) +{ + _nl_auto_rtnl_link struct rtnl_link *link = NULL; + _nl_auto_nl_msg struct nl_msg *msg = NULL; + int err; + int syserr; + + if ((err = rtnl_link_build_get_request(ifindex, name, &msg)) < 0) + return err; + + err = nl_send_auto(sk, msg); + if (err < 0) + return err; + + err = nl_pickup_keep_syserr(sk, link_msg_parser, (struct nl_object **) &link, &syserr); + if (err < 0) { + if ( syserr == -EINVAL + && ifindex <= 0 + && name + && *name) { + /* Older kernels do not support lookup by ifname. This was added + * by commit kernel a3d1289126e7b14307074b76bf1677015ea5036f . + * Detect this error case and return NLE_OPNOTSUPP instead of + * NLE_INVAL. */ + return -NLE_OPNOTSUPP; + } + return err; + } + + /* If an object has been returned, we also need to wait for the ACK */ + if (err == 0 && link) + wait_for_ack(sk); + + *result = _nl_steal_pointer(&link); + return 0; +} + +/** + * Translate interface index to corresponding link name + * @arg cache Link cache + * @arg ifindex Interface index + * @arg dst String to store name + * @arg len Length of destination string + * + * Translates the specified interface index to the corresponding + * link name and stores the name in the destination string. + * + * @route_doc{link_translate_ifindex, Translating interface index to link name} + * @see rtnl_link_name2i() + * @return Name of link or NULL if no match was found. + */ +char * rtnl_link_i2name(struct nl_cache *cache, int ifindex, char *dst, + size_t len) +{ + _nl_auto_rtnl_link struct rtnl_link *link = NULL; + + link = rtnl_link_get(cache, ifindex); + if (link) { + _nl_strncpy_trunc(dst, link->l_name, len); + return dst; + } + + return NULL; +} + +/** + * Translate link name to corresponding interface index + * @arg cache Link cache + * @arg name Name of link + * + * @route_doc{link_translate_ifindex, Translating interface index to link name} + * @see rtnl_link_i2name() + * @return Interface index or 0 if no match was found. + */ +int rtnl_link_name2i(struct nl_cache *cache, const char *name) +{ + _nl_auto_rtnl_link struct rtnl_link *link = NULL; + + link = rtnl_link_get_by_name(cache, name); + if (link) + return link->l_index; + + return 0; +} + +/** @} */ + +int rtnl_link_fill_info(struct nl_msg *msg, struct rtnl_link *link) +{ + if (link->ce_mask & LINK_ATTR_ADDR) + NLA_PUT_ADDR(msg, IFLA_ADDRESS, link->l_addr); + + if (link->ce_mask & LINK_ATTR_BRD) + NLA_PUT_ADDR(msg, IFLA_BROADCAST, link->l_bcast); + + if (link->ce_mask & LINK_ATTR_MTU) + NLA_PUT_U32(msg, IFLA_MTU, link->l_mtu); + + if (link->ce_mask & LINK_ATTR_TXQLEN) + NLA_PUT_U32(msg, IFLA_TXQLEN, link->l_txqlen); + + if (link->ce_mask & LINK_ATTR_WEIGHT) + NLA_PUT_U32(msg, IFLA_WEIGHT, link->l_weight); + + if (link->ce_mask & LINK_ATTR_IFNAME) + NLA_PUT_STRING(msg, IFLA_IFNAME, link->l_name); + + if (link->ce_mask & LINK_ATTR_OPERSTATE) + NLA_PUT_U8(msg, IFLA_OPERSTATE, link->l_operstate); + + if (link->ce_mask & LINK_ATTR_CARRIER) + NLA_PUT_U8(msg, IFLA_CARRIER, link->l_carrier); + + if (link->ce_mask & LINK_ATTR_LINKMODE) + NLA_PUT_U8(msg, IFLA_LINKMODE, link->l_linkmode); + + if (link->ce_mask & LINK_ATTR_IFALIAS) + NLA_PUT_STRING(msg, IFLA_IFALIAS, link->l_ifalias); + + if (link->ce_mask & LINK_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_LINK, link->l_link); + + if (link->ce_mask & LINK_ATTR_LINK_NETNSID) + NLA_PUT_S32(msg, IFLA_LINK_NETNSID, link->l_link_netnsid); + + if (link->ce_mask & LINK_ATTR_MASTER) + NLA_PUT_U32(msg, IFLA_MASTER, link->l_master); + + if (link->ce_mask & LINK_ATTR_NUM_TX_QUEUES) + NLA_PUT_U32(msg, IFLA_NUM_TX_QUEUES, link->l_num_tx_queues); + + if (link->ce_mask & LINK_ATTR_NUM_RX_QUEUES) + NLA_PUT_U32(msg, IFLA_NUM_RX_QUEUES, link->l_num_rx_queues); + + if (link->ce_mask & LINK_ATTR_NS_FD) + NLA_PUT_U32(msg, IFLA_NET_NS_FD, link->l_ns_fd); + + if (link->ce_mask & LINK_ATTR_NS_PID) + NLA_PUT_U32(msg, IFLA_NET_NS_PID, link->l_ns_pid); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int build_link_msg(int cmd, struct ifinfomsg *hdr, + struct rtnl_link *link, int flags, struct nl_msg **result) +{ + _nl_auto_nl_msg struct nl_msg *msg = NULL; + struct nlattr *af_spec; + + msg = nlmsg_alloc_simple(cmd, flags); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, hdr, sizeof(*hdr), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (rtnl_link_fill_info(msg, link)) + goto nla_put_failure; + + if (link->ce_mask & LINK_ATTR_GROUP) + NLA_PUT_U32(msg, IFLA_GROUP, link->l_group); + + if (link->ce_mask & (LINK_ATTR_LINKINFO|LINK_ATTR_LINKINFO_SLAVE_KIND)) { + struct nlattr *info; + + if (!(info = nla_nest_start(msg, IFLA_LINKINFO))) + goto nla_put_failure; + + if (link->ce_mask & LINK_ATTR_LINKINFO) { + NLA_PUT_STRING(msg, IFLA_INFO_KIND, link->l_info_kind); + + if (link->l_info_ops) { + if (link->l_info_ops->io_put_attrs && + link->l_info_ops->io_put_attrs(msg, link) < 0) + goto nla_put_failure; + } + } + + if (link->ce_mask & LINK_ATTR_LINKINFO_SLAVE_KIND) { + NLA_PUT_STRING(msg, IFLA_INFO_SLAVE_KIND, link->l_info_slave_kind); + } + + nla_nest_end(msg, info); + } + + if (link->ce_mask & LINK_ATTR_VF_LIST) { + if (rtnl_link_sriov_fill_vflist(msg, link) < 0) + goto nla_put_failure; + } + + if (do_foreach_af(link, af_fill_pi, msg) < 0) + goto nla_put_failure; + + if (!(af_spec = nla_nest_start(msg, IFLA_AF_SPEC))) + goto nla_put_failure; + + if (do_foreach_af(link, af_fill, msg) < 0) + goto nla_put_failure; + + nla_nest_end(msg, af_spec); + + *result = _nl_steal_pointer(&msg); + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +/** + * @name Add / Modify + * @{ + */ + +/** + * Build a netlink message requesting the addition of new virtual link + * @arg link new link to add + * @arg flags additional netlink message flags + * @arg result pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_link_add() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_link_add() + * + * @note This operation is not supported on all kernel versions. + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_build_add_request(struct rtnl_link *link, int flags, + struct nl_msg **result) +{ + struct ifinfomsg ifi = { + .ifi_family = link->l_family, + .ifi_index = link->l_index, + .ifi_flags = link->l_flags, + .ifi_change = link->l_flag_mask, + }; + + return build_link_msg(RTM_NEWLINK, &ifi, link, flags, result); +} + +/** + * Add virtual link + * @arg sk netlink socket. + * @arg link new link to add + * @arg flags additional netlink message flags + * + * Builds a \c RTM_NEWLINK netlink message requesting the addition of + * a new virtual link. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @copydoc auto_ack_warning + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_add(struct nl_sock *sk, struct rtnl_link *link, int flags) +{ + struct nl_msg *msg; + int err; + + err = rtnl_link_build_add_request(link, flags, &msg); + if (err < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build a netlink message requesting the modification of link + * @arg orig original link to change + * @arg changes link containing the changes to be made + * @arg flags additional netlink message flags + * @arg result pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_link_change() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_link_change() + * + * @note The resulting message will have message type set to RTM_NEWLINK + * which may not work with older kernels. You may have to modify it + * to RTM_SETLINK (does not allow changing link info attributes) to + * have the change request work with older kernels. + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_build_change_request(struct rtnl_link *orig, + struct rtnl_link *changes, int flags, + struct nl_msg **result) +{ + struct ifinfomsg ifi = { + .ifi_family = orig->l_family, + .ifi_index = orig->l_index, + }; + int err, rt; + + if (changes->ce_mask & LINK_ATTR_FLAGS) { + ifi.ifi_flags = orig->l_flags & ~changes->l_flag_mask; + ifi.ifi_flags |= changes->l_flags; + ifi.ifi_change = changes->l_flag_mask; + } + + if (changes->l_family && changes->l_family != orig->l_family) { + APPBUG("link change: family is immutable"); + return -NLE_IMMUTABLE; + } + + /* Avoid unnecessary name change requests */ + if (orig->ce_mask & LINK_ATTR_IFINDEX && + orig->ce_mask & LINK_ATTR_IFNAME && + changes->ce_mask & LINK_ATTR_IFNAME && + !strcmp(orig->l_name, changes->l_name)) + changes->ce_mask &= ~LINK_ATTR_IFNAME; + + rt = af_request_type(orig->l_family, changes); + + if ((err = build_link_msg(rt, &ifi, changes, flags, result)) < 0) + return err; + + return 0; +} + +/** + * Change link + * @arg sk netlink socket. + * @arg orig original link to be changed + * @arg changes link containing the changes to be made + * @arg flags additional netlink message flags + * + * Builds a \c RTM_NEWLINK netlink message requesting the change of + * a network link. If -EOPNOTSUPP is returned by the kernel, the + * message type will be changed to \c RTM_SETLINK and the message is + * resent to work around older kernel versions. + * + * The link to be changed is looked up based on the interface index + * supplied in the \p orig link. Optionaly the link name is used but + * only if no interface index is provided, otherwise providing an + * link name will result in the link name being changed. + * + * If no matching link exists, the function will return + * -NLE_OBJ_NOTFOUND. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @copydoc auto_ack_warning + * + * @note The link name can only be changed if the link has been put + * in opertional down state. (~IF_UP) + * + * @note On versions up to 3.4.0, \c NLE_SEQ_MISMATCH would be returned if the + * kernel does not supports \c RTM_NEWLINK. It is advised to ignore the + * error code if you cannot upgrade the library. + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_change(struct nl_sock *sk, struct rtnl_link *orig, + struct rtnl_link *changes, int flags) +{ + _nl_auto_nl_msg struct nl_msg *msg = NULL; + int err; + + err = rtnl_link_build_change_request(orig, changes, flags, &msg); + if (err < 0) + return err; + + BUG_ON(msg->nm_nlh->nlmsg_seq != NL_AUTO_SEQ); +retry: + err = nl_send_auto_complete(sk, msg); + if (err < 0) + return err; + + err = wait_for_ack(sk); + if ( err == -NLE_OPNOTSUPP + && msg->nm_nlh->nlmsg_type == RTM_NEWLINK) { + msg->nm_nlh->nlmsg_type = RTM_SETLINK; + msg->nm_nlh->nlmsg_seq = NL_AUTO_SEQ; + goto retry; + } + + if (err < 0) + return err; + + return 0; +} + +/** @} */ + +/** + * @name Delete + * @{ + */ + +/** + * Build a netlink message requesting the deletion of a link + * @arg link Link to delete + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_link_delete() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_link_delete() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_build_delete_request(const struct rtnl_link *link, + struct nl_msg **result) +{ + _nl_auto_nl_msg struct nl_msg *msg = NULL; + struct ifinfomsg ifi = { + .ifi_index = link->l_index, + }; + + if (!(link->ce_mask & (LINK_ATTR_IFINDEX | LINK_ATTR_IFNAME))) { + APPBUG("ifindex or name must be specified"); + return -NLE_MISSING_ATTR; + } + + if (!(msg = nlmsg_alloc_simple(RTM_DELLINK, 0))) + return -NLE_NOMEM; + + _NL_RETURN_ON_PUT_ERR(nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO)); + + if (link->ce_mask & LINK_ATTR_IFNAME) + _NL_RETURN_ON_PUT_ERR(nla_put_string(msg, IFLA_IFNAME, link->l_name)); + + *result = _nl_steal_pointer(&msg); + return 0; +} + +/** + * Delete link + * @arg sk Netlink socket + * @arg link Link to delete + * + * Builds a \c RTM_DELLINK netlink message requesting the deletion of + * a network link which has been previously added to the kernel and + * sends the message to the kernel. + * + * If no matching link exists, the function will return + * -NLE_OBJ_NOTFOUND. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @copydoc auto_ack_warning + * + * @note Only virtual links such as dummy interface or vlan interfaces + * can be deleted. It is not possible to delete physical interfaces + * such as ethernet interfaces or the loopback device. + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_delete(struct nl_sock *sk, const struct rtnl_link *link) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_link_build_delete_request(link, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** @} */ + +/** + * @name Link Object + * @{ + */ + +/** + * Allocate link object + * + * @see rtnl_link_put() + * @return New link object or NULL if allocation failed + */ +struct rtnl_link *rtnl_link_alloc(void) +{ + return (struct rtnl_link *) nl_object_alloc(&link_obj_ops); +} + +/** + * Release a link object reference + * @arg link Link object + */ +void rtnl_link_put(struct rtnl_link *link) +{ + nl_object_put((struct nl_object *) link); +} + +/** + * Set name of link object + * @arg link Link object + * @arg name New name + * + * @note To change the name of a link in the kernel, set the interface + * index to the link you wish to change, modify the link name using + * this function and pass the link object to rtnl_link_change() or + * rtnl_link_add(). + * + * @route_doc{link_attr_name, Link Name} + * @see rtnl_link_get_name() + * @see rtnl_link_set_ifindex() + */ +void rtnl_link_set_name(struct rtnl_link *link, const char *name) +{ + _nl_strncpy_trunc(link->l_name, name, sizeof(link->l_name)); + link->ce_mask |= LINK_ATTR_IFNAME; +} + +/** + * Return name of link object + * @arg link Link object + * + * @route_doc{link_attr_name, Link Name} + * @see rtnl_link_set_name() + * @return Link name or NULL if name is not specified + */ +char *rtnl_link_get_name(struct rtnl_link *link) +{ + return link->ce_mask & LINK_ATTR_IFNAME ? link->l_name : NULL; +} + +/** + * Set the group identifier of a link object + * @arg link Link object + * @arg group Group identifier + */ +void rtnl_link_set_group(struct rtnl_link *link, uint32_t group) +{ + link->l_group = group; + link->ce_mask |= LINK_ATTR_GROUP; +} + +/** + * Return the group identifier of link object + * @arg link Link object + * + * @return Group identifier or 0 if not set. + */ +uint32_t rtnl_link_get_group(struct rtnl_link *link) +{ + return link->l_group; +} + +static inline void __assign_addr(struct rtnl_link *link, struct nl_addr **pos, + struct nl_addr *new, int flag) +{ + if (*pos) + nl_addr_put(*pos); + + nl_addr_get(new); + *pos = new; + + link->ce_mask |= flag; +} + +/** + * Set link layer address of link object + * @arg link Link object + * @arg addr New link layer address + * + * The function increments the reference counter of the address object + * and overwrites any existing link layer address previously assigned. + * + * @route_doc{link_attr_address, Link layer address} + * @see rtnl_link_get_addr() + */ +void rtnl_link_set_addr(struct rtnl_link *link, struct nl_addr *addr) +{ + __assign_addr(link, &link->l_addr, addr, LINK_ATTR_ADDR); +} + +/** + * Return link layer address of link object + * @arg link Link object + * + * @copydoc pointer_lifetime_warning + * @route_doc{link_attr_address, Link Layer Address} + * @see rtnl_link_set_addr() + * @return Link layer address or NULL if not set. + */ +struct nl_addr *rtnl_link_get_addr(struct rtnl_link *link) +{ + return link->ce_mask & LINK_ATTR_ADDR ? link->l_addr : NULL; +} + +/** + * Return permanent link layer adress of link object + * @arg link Link object + * + * @copydoc pointer_lifetime_warning + * @route_doc{link_attr_permaddr, Permanent Link Layer Address} + * @return Permanent link layer address or NULL if not set. + */ +struct nl_addr *rtnl_link_get_perm_addr(struct rtnl_link *link) +{ + return link->ce_mask & LINK_ATTR_PERMANENT_ADDR ? link->l_paddr : NULL; +} + +/** + * Set link layer broadcast address of link object + * @arg link Link object + * @arg addr New broadcast address + * + * The function increments the reference counter of the address object + * and overwrites any existing link layer broadcast address previously + * assigned. + * + * @route_doc{link_attr_broadcast, Link Layer Broadcast Address} + * @see rtnl_link_get_broadcast() + */ +void rtnl_link_set_broadcast(struct rtnl_link *link, struct nl_addr *addr) +{ + __assign_addr(link, &link->l_bcast, addr, LINK_ATTR_BRD); +} + +/** + * Return link layer broadcast address of link object + * @arg link Link object + * + * @copydoc pointer_lifetime_warning + * @route_doc{link_attr_address, Link Layer Address} + * @see rtnl_link_set_broadcast() + * @return Link layer address or NULL if not set. + */ +struct nl_addr *rtnl_link_get_broadcast(struct rtnl_link *link) +{ + return link->ce_mask & LINK_ATTR_BRD ? link->l_bcast : NULL; +} + +/** + * Set flags of link object + * @arg link Link object + * @arg flags Flags + * + * @see rtnl_link_get_flags() + * @see rtnl_link_unset_flags() + */ +void rtnl_link_set_flags(struct rtnl_link *link, unsigned int flags) +{ + link->l_flag_mask |= flags; + link->l_flags |= flags; + link->ce_mask |= LINK_ATTR_FLAGS; +} + +/** + * Unset flags of link object + * @arg link Link object + * @arg flags Flags + * + * @see rtnl_link_set_flags() + * @see rtnl_link_get_flags() + */ +void rtnl_link_unset_flags(struct rtnl_link *link, unsigned int flags) +{ + link->l_flag_mask |= flags; + link->l_flags &= ~flags; + link->ce_mask |= LINK_ATTR_FLAGS; +} + +/** + * Return flags of link object + * @arg link Link object + * + * @route_doc{link_attr_flags, Link Flags} + * @see rtnl_link_set_flags() + * @see rtnl_link_unset_flags() + * @return Link flags or 0 if none have been set. + */ +unsigned int rtnl_link_get_flags(struct rtnl_link *link) +{ + return link->l_flags; +} + +/** + * Set address family of link object + * + * @see rtnl_link_get_family() + */ +void rtnl_link_set_family(struct rtnl_link *link, int family) +{ + link->l_family = family; + link->ce_mask |= LINK_ATTR_FAMILY; + + if (link->l_af_ops) { + int ao_family = link->l_af_ops->ao_family; + + af_free(link, link->l_af_ops, link->l_af_data[ao_family], NULL); + link->l_af_data[ao_family] = NULL; + } + + link->l_af_ops = af_lookup_and_alloc(link, family); +} + +/** + * Return address family of link object + * @arg link Link object + * + * @see rtnl_link_set_family() + * @return Address family or \c AF_UNSPEC if not specified. + */ +int rtnl_link_get_family(struct rtnl_link *link) +{ + return link->ce_mask & LINK_ATTR_FAMILY ? link->l_family : AF_UNSPEC; +} + +/** + * Set hardware type of link object + * @arg link Link object + * @arg arptype New hardware type \c (ARPHRD_*) + * + * @route_doc{link_attr_arptype, Hardware Type} + * @copydoc read_only_attribute + * @see rtnl_link_get_arptype() + */ +void rtnl_link_set_arptype(struct rtnl_link *link, unsigned int arptype) +{ + link->l_arptype = arptype; + link->ce_mask |= LINK_ATTR_ARPTYPE; +} + +/** + * Get hardware type of link object + * @arg link Link object + * + * @route_doc{link_attr_arptype, Hardware Type} + * @see rtnl_link_set_arptype() + * @return Hardware type \c (ARPHRD_ETHER *) or \c ARPHRD_VOID + */ +unsigned int rtnl_link_get_arptype(struct rtnl_link *link) +{ + if (link->ce_mask & LINK_ATTR_ARPTYPE) + return link->l_arptype; + else + return ARPHRD_VOID; +} + +/** + * Set interface index of link object + * @arg link Link object + * @arg ifindex Interface index + * + * @route_doc{link_attr_ifindex, Interface Index} + * @see rtnl_link_get_ifindex() + */ +void rtnl_link_set_ifindex(struct rtnl_link *link, int ifindex) +{ + link->l_index = ifindex; + link->ce_mask |= LINK_ATTR_IFINDEX; +} + + +/** + * Return interface index of link object + * @arg link Link object + * + * @route_doc{link_attr_ifindex, Interface Index} + * @see rtnl_link_set_ifindex() + * @return Interface index or 0 if not set. + */ +int rtnl_link_get_ifindex(struct rtnl_link *link) +{ + return link->l_index; +} + +/** + * Set Maximum Transmission Unit of link object + * @arg link Link object + * @arg mtu New MTU value in number of bytes + * + * @route_doc{link_attr_mtu, Maximum Transmission Unit} + * @see rtnl_link_get_mtu() + */ +void rtnl_link_set_mtu(struct rtnl_link *link, unsigned int mtu) +{ + link->l_mtu = mtu; + link->ce_mask |= LINK_ATTR_MTU; +} + +/** + * Return maximum transmission unit of link object + * @arg link Link object + * + * @route_doc{link_attr_mtu, Maximum Transmission Unit} + * @see rtnl_link_set_mtu() + * @return MTU in bytes or 0 if not set + */ +unsigned int rtnl_link_get_mtu(struct rtnl_link *link) +{ + return link->l_mtu; +} + +/** + * Set transmission queue length + * @arg link Link object + * @arg txqlen New queue length + * + * The unit is dependant on the link type. The most common units is number + * of packets. + * + * @route_doc{link_attr_txqlen, Transmission Queue Length} + */ +void rtnl_link_set_txqlen(struct rtnl_link *link, unsigned int txqlen) +{ + link->l_txqlen = txqlen; + link->ce_mask |= LINK_ATTR_TXQLEN; +} + +/** + * Return transmission queue length + * @arg link Link object + * + * The unit is dependant on the link type. The most common units is number + * of packets. + * + * @route_doc{link_attr_txqlen, Transmission Queue Length} + * @return queue length or 0 if not specified. + */ +unsigned int rtnl_link_get_txqlen(struct rtnl_link *link) +{ + return link->ce_mask & LINK_ATTR_TXQLEN ? link->l_txqlen : 0; +} + +void rtnl_link_set_link(struct rtnl_link *link, int ifindex) +{ + link->l_link = ifindex; + link->ce_mask |= LINK_ATTR_LINK; +} + +int rtnl_link_get_link(struct rtnl_link *link) +{ + return link->l_link; +} + +/** + * Set the netnsid of the link + * @arg link Link object + * @link_netnsid the netnsid to set + * + * Sets the IFLA_LINK_NETNSID attribute of the link + * @returns 0 on success + */ +int rtnl_link_set_link_netnsid(struct rtnl_link *link, int32_t link_netnsid) +{ + link->l_link_netnsid = link_netnsid; + link->ce_mask |= LINK_ATTR_LINK_NETNSID; + return 0; +} + +/** + * Get the netnsid of the link + * @arg link Link object + * @out_link_netnsid the netnsid + * + * Gets the IFLA_LINK_NETNSID attribute of the link + * or returns an error if the value is unset. + * + * @returns 0 on success + */ +int rtnl_link_get_link_netnsid(const struct rtnl_link *link, int32_t *out_link_netnsid) +{ + if (!(link->ce_mask & LINK_ATTR_LINK_NETNSID)) + return -NLE_INVAL; + + *out_link_netnsid = link->l_link_netnsid; + return 0; +} + +/** + * Set master link of link object + * @arg link Link object + * @arg ifindex Interface index of master link + * + * @see rtnl_link_get_master() + */ +void rtnl_link_set_master(struct rtnl_link *link, int ifindex) +{ + link->l_master = ifindex; + link->ce_mask |= LINK_ATTR_MASTER; +} + +/** + * Return master link of link object + * @arg link Link object + * + * @see rtnl_link_set_master() + * @return Interface index of master link or 0 if not specified + */ +int rtnl_link_get_master(struct rtnl_link *link) +{ + return link->l_master; +} + +/** + * Set carrier of link object + * @arg link Link object + * @arg status New carrier status + * + * @see rtnl_link_get_carrier() + */ +void rtnl_link_set_carrier(struct rtnl_link *link, uint8_t status) +{ + link->l_carrier = status; + link->ce_mask |= LINK_ATTR_CARRIER; +} + +/** + * Return carrier status of link object + * @arg link Link object + * + * @see rtnl_link_set_master() + * @return Carrier state. + */ +uint8_t rtnl_link_get_carrier(struct rtnl_link *link) +{ + return link->l_carrier; +} + +/** + * Return carrier on/off changes of link object + * @arg link Link object + * @arg carrier_changes Pointer to store number of carrier changes + * + * @return 0 on success, negative error number otherwise + */ +int rtnl_link_get_carrier_changes(struct rtnl_link *link, uint32_t *carrier_changes) +{ + if (!(link->ce_mask & LINK_ATTR_CARRIER_CHANGES)) + return -NLE_NOATTR; + + if (carrier_changes) + *carrier_changes = link->l_carrier_changes; + + return 0; +} + +/** + * Set operational status of link object + * @arg link Link object + * @arg status New opertional status + * + * @route_doc{link_attr_operstate, Operational Status}} + * @see rtnl_link_get_operstate() + */ +void rtnl_link_set_operstate(struct rtnl_link *link, uint8_t status) +{ + link->l_operstate = status; + link->ce_mask |= LINK_ATTR_OPERSTATE; +} + +/** + * Return operational status of link object + * @arg link Link object + * + * @route_doc{link_attr_operstate, Operational Status} + * @see rtnl_link_set_operstate() + * @return Opertional state or \c IF_OPER_UNKNOWN + */ +uint8_t rtnl_link_get_operstate(struct rtnl_link *link) +{ + return link->l_operstate; +} + +/** + * Set link mode of link object + * @arg link Link object + * @arg mode New link mode + * + * @route_doc{link_attr_mode, Mode} + * @see rtnl_link_get_linkmode() + */ +void rtnl_link_set_linkmode(struct rtnl_link *link, uint8_t mode) +{ + link->l_linkmode = mode; + link->ce_mask |= LINK_ATTR_LINKMODE; +} + +/** + * Return link mode of link object + * @arg link Link object + * + * @route_doc{link_attr_mode, Mode} + * @see rtnl_link_get_linkmode() + * @return Link mode or \c IF_LINK_MODE_DEFAULT + */ +uint8_t rtnl_link_get_linkmode(struct rtnl_link *link) +{ + return link->l_linkmode; +} + +/** + * Return alias name of link object (SNMP IfAlias) + * @arg link Link object + * + * @route_doc{link_attr_alias, Alias} + * @see rtnl_link_set_ifalias() + * @return Alias name or NULL if not set. + */ +const char *rtnl_link_get_ifalias(struct rtnl_link *link) +{ + return link->l_ifalias; +} + +/** + * Set alias name of link object (SNMP IfAlias) + * @arg link Link object + * @arg alias Alias name or NULL to unset + * + * Sets the alias name of the link to the specified name. The alias + * name can be unset by specyfing NULL as the alias. The name will + * be strdup()ed, so no need to provide a persistent character string. + * + * @route_doc{link_attr_alias, Alias} + * @see rtnl_link_get_ifalias() + */ +void rtnl_link_set_ifalias(struct rtnl_link *link, const char *alias) +{ + free(link->l_ifalias); + + if (alias) { + link->l_ifalias = strdup(alias); + link->ce_mask |= LINK_ATTR_IFALIAS; + } else { + link->l_ifalias = NULL; + link->ce_mask &= ~LINK_ATTR_IFALIAS; + } +} + +/** + * Set queueing discipline name of link object + * @arg link Link object + * @arg name Name of queueing discipline + * + * @copydoc read_only_attribute + * + * For more information on how to modify the qdisc of a link, see section + * @ref_route{route_tc, Traffic Control}. + * + * @route_doc{link_attr_qdisc, Queueing Discipline Name} + * @see rtnl_link_get_qdisc() + */ +void rtnl_link_set_qdisc(struct rtnl_link *link, const char *name) +{ + _nl_strncpy_trunc(link->l_qdisc, name, sizeof(link->l_qdisc)); + link->ce_mask |= LINK_ATTR_QDISC; +} + +/** + * Return name of queueing discipline of link object + * @arg link Link object + * + * @route_doc{link_attr_qdisc, Queueing Discipline Name} + * @see rtnl_link_set_qdisc() + * @return Name of qdisc or NULL if not specified. + */ +char *rtnl_link_get_qdisc(struct rtnl_link *link) +{ + return link->ce_mask & LINK_ATTR_QDISC ? link->l_qdisc : NULL; +} + + +/** + * Return number of PCI virtual functions of link object + * @arg link Link object + * @arg num_vf Pointer to store number of VFs + * + * @return 0 on success or -NLE_OPNOTSUPP if not available + */ +int rtnl_link_get_num_vf(struct rtnl_link *link, uint32_t *num_vf) +{ + if (link->ce_mask & LINK_ATTR_NUM_VF) { + *num_vf = link->l_num_vf; + return 0; + } else + return -NLE_OPNOTSUPP; +} + +/** + * Return value of link statistics counter + * @arg link Link object + * @arg id Identifier of statistical counter + * + * @return Value of counter or 0 if not specified. + */ +uint64_t rtnl_link_get_stat(struct rtnl_link *link, rtnl_link_stat_id_t id) +{ + if (id > RTNL_LINK_STATS_MAX) + return 0; + + return link->l_stats[id]; +} + +/** + * Set value of link statistics counter + * @arg link Link object + * @arg id Identifier of statistical counter + * @arg value New value + * + * \note Changing the value of a statistical counter will not change the + * value in the kernel. + * + * @return 0 on success or a negative error code + */ +int rtnl_link_set_stat(struct rtnl_link *link, rtnl_link_stat_id_t id, + const uint64_t value) +{ + if (id > RTNL_LINK_STATS_MAX) + return -NLE_INVAL; + + link->l_stats[id] = value; + + return 0; +} + +/** + * Set type of link object + * @arg link Link object + * @arg type Name of link type + * + * Looks up the link type module and prepares the link to store type + * specific attributes. If a type has been assigned already it will + * be released with all link type specific attributes lost. + * + * @route_doc{link_modules, Link Modules} + * @return 0 on success or a negative error code. + */ +int rtnl_link_set_type(struct rtnl_link *link, const char *type) +{ + struct rtnl_link_info_ops *io; + _nl_auto_free char *kind = NULL; + int err; + + free(link->l_info_kind); + link->ce_mask &= ~LINK_ATTR_LINKINFO; + release_link_info(link); + + if (!type) + return 0; + + kind = strdup(type); + if (!kind) + return -NLE_NOMEM; + + io = rtnl_link_info_ops_lookup(type); + if (io) { + if (io->io_alloc && (err = io->io_alloc(link)) < 0) { + _nl_clear_free(&kind); + return err; + } + + link->l_info_ops = io; + } + + link->l_info_kind = _nl_steal_pointer(&kind); + link->ce_mask |= LINK_ATTR_LINKINFO; + + return 0; +} + +/** + * Return type of link + * @arg link Link object + * + * @route_doc{link_modules, Link Modules} + * @return Name of link type or NULL if not specified. + */ +char *rtnl_link_get_type(struct rtnl_link *link) +{ + return link->l_info_kind; +} + +/** + * Set type of slave link object + * @arg link Link object (slave) + * @arg type Name of link type + * + * If a slave type has been assigned already it will be released. + * + * @route_doc{link_modules, Link Modules} + * @return 0 on success or a negative error code. + */ +int rtnl_link_set_slave_type(struct rtnl_link *link, const char *type) +{ + char *kind = NULL; + + if (type) { + kind = strdup(type); + if (!kind) + return -NLE_NOMEM; + } + + free(link->l_info_slave_kind); + link->l_info_slave_kind = kind; + + if (kind) + link->ce_mask |= LINK_ATTR_LINKINFO_SLAVE_KIND; + else + link->ce_mask &= ~LINK_ATTR_LINKINFO_SLAVE_KIND; + return 0; +} + +/** + * Return type of enslaved link + * @arg link Link object + * + * @route_doc{link_modules, Link Modules} + * @return Name of enslaved link type or NULL if not specified. + */ +const char *rtnl_link_get_slave_type(const struct rtnl_link *link) +{ + return link->l_info_slave_kind; +} + + +/** + * Set link promiscuity count + * @arg link Link object + * @arg count New promiscuity count + * + * @copydoc read_only_attribute + * + * @see rtnl_link_get_promiscuity() + */ +void rtnl_link_set_promiscuity(struct rtnl_link *link, uint32_t count) +{ + link->l_promiscuity = count; + link->ce_mask |= LINK_ATTR_PROMISCUITY; +} + +/** + * Return link promiscuity count + * @arg link Link object + * + * @see rtnl_link_set_promiscuity() + * @return Link promiscuity count or 0 + */ +uint32_t rtnl_link_get_promiscuity(struct rtnl_link *link) +{ + return link->l_promiscuity; +} + +/** + * Set number of TX queues + * @arg link Link object + * @arg nqueues Number of queues + * + * Sets the number of TX queues of the link object. The value is considered + * by the kernel when creating network devices that can be created via + * netlink. The value will be passed on to alloc_netdev_mqs() + * + * Therefore use of rtnl_link_set_num_tx_queues() only makes sense in + * combination with rtnl_link_add() or if the link object is used as a filter. + * + * @see rtnl_link_get_num_tx_queues() + */ +void rtnl_link_set_num_tx_queues(struct rtnl_link *link, uint32_t nqueues) +{ + link->l_num_tx_queues = nqueues; + link->ce_mask |= LINK_ATTR_NUM_TX_QUEUES; +} + +/** + * Return number of TX queues + * @arg link Link object + * + * @return Number of TX queues or 0 + */ +uint32_t rtnl_link_get_num_tx_queues(struct rtnl_link *link) +{ + return link->l_num_tx_queues; +} + +/** + * Set number of RX queues + * @arg link Link object + * @arg nqueues Number of queues + * + * Sets the number of RX queues of the link object. The value is considered + * by the kernel when creating network devices that can be created via + * netlink. The value will be passed on to alloc_netdev_mqs() + * + * Therefore use of rtnl_link_set_num_rx_queues() only makes sense in + * combination with rtnl_link_add() or if the link object is used as a filter. + * + * @see rtnl_link_get_num_rx_queues() + */ +void rtnl_link_set_num_rx_queues(struct rtnl_link *link, uint32_t nqueues) +{ + link->l_num_rx_queues = nqueues; + link->ce_mask |= LINK_ATTR_NUM_RX_QUEUES; +} + +/** + * Return number of RX queues + * @arg link Link object + * + * @return Number of RX queues or 0 + */ +uint32_t rtnl_link_get_num_rx_queues(struct rtnl_link *link) +{ + return link->l_num_rx_queues; +} + +/** + * Return maximum number of segments for generic segmentation offload + * @arg link Link object + * @arg gso_max_segs Pointer to store maximum number GSO segments + * + * @return 0 on success, negative error number otherwise + */ +int rtnl_link_get_gso_max_segs(struct rtnl_link *link, uint32_t *gso_max_segs) +{ + if (!(link->ce_mask & LINK_ATTR_GSO_MAX_SEGS)) + return -NLE_NOATTR; + + if (gso_max_segs) + *gso_max_segs = link->l_gso_max_segs; + + return 0; +} + +/** + * Return maximum size for generic segmentation offload + * @arg link Link object + * @arg gso_max_segs Pointer to store maximum GSO size + * + * @return 0 on success, negative error number otherwise + */ +int rtnl_link_get_gso_max_size(struct rtnl_link *link, uint32_t *gso_max_size) +{ + if (!(link->ce_mask & LINK_ATTR_GSO_MAX_SIZE)) + return -NLE_NOATTR; + + if (gso_max_size) + *gso_max_size = link->l_gso_max_size; + + return 0; +} + +/** + * Return physical port id of link object + * @arg link Link object + * + * @return Physical port id or NULL if not set. + */ +struct nl_data *rtnl_link_get_phys_port_id(struct rtnl_link *link) +{ + return link->l_phys_port_id; +} + +/** + * Return physical port name of link object + * @arg link Link object + * + * @return Physical port name or NULL if not set. + */ +char *rtnl_link_get_phys_port_name(struct rtnl_link *link) +{ + return link->l_phys_port_name; +} + +/* + * Return physical switch id of link object + * @arg link Link object + * + * @return Physical switch id or NULL if not set. + */ +struct nl_data *rtnl_link_get_phys_switch_id(struct rtnl_link *link) +{ + return link->l_phys_switch_id; +} + +void rtnl_link_set_ns_fd(struct rtnl_link *link, int fd) +{ + link->l_ns_fd = fd; + link->ce_mask |= LINK_ATTR_NS_FD; +} + +int rtnl_link_get_ns_fd(struct rtnl_link *link) +{ + return link->l_ns_fd; +} + +void rtnl_link_set_ns_pid(struct rtnl_link *link, pid_t pid) +{ + link->l_ns_pid = pid; + link->ce_mask |= LINK_ATTR_NS_PID; +} + +pid_t rtnl_link_get_ns_pid(struct rtnl_link *link) +{ + return link->l_ns_pid; +} + +/** @} */ + +/** + * @name Master/Slave + * @{ + */ + +/** + * Enslave slave link to master link + * @arg sock netlink socket + * @arg master ifindex of master link + * @arg slave ifindex of slave link + * + * This function is identical to rtnl_link_enslave() except that + * it takes interface indices instead of rtnl_link objects. + * + * @see rtnl_link_enslave() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_enslave_ifindex(struct nl_sock *sock, int master, int slave) +{ + _nl_auto_rtnl_link struct rtnl_link *link = NULL; + int err; + + if (!(link = rtnl_link_alloc())) + return -NLE_NOMEM; + + rtnl_link_set_ifindex(link, slave); + rtnl_link_set_master(link, master); + + if ((err = rtnl_link_change(sock, link, link, 0)) < 0) + return err; + + _nl_clear_pointer(&link, rtnl_link_put); + + /* + * Due to the kernel not signaling whether this operation is + * supported or not, we will retrieve the attribute to see if the + * request was successful. If the master assigned remains unchanged + * we will return NLE_OPNOTSUPP to allow performing backwards + * compatibility of some sort. + */ + if ((err = rtnl_link_get_kernel(sock, slave, NULL, &link)) < 0) + return err; + + if (rtnl_link_get_master(link) != master) + return -NLE_OPNOTSUPP; + + return 0; +} + +/** + * Enslave slave link to master link + * @arg sock netlink socket + * @arg master master link + * @arg slave slave link + * + * Constructs a RTM_NEWLINK or RTM_SETLINK message adding the slave to + * the master and sends the request via the specified netlink socket. + * + * @note The feature of enslaving/releasing via netlink has only been added + * recently to the kernel (Feb 2011). Also, the kernel does not signal + * if the operation is not supported. Therefore this function will + * verify if the master assignment has changed and will return + * -NLE_OPNOTSUPP if it did not. + * + * @see rtnl_link_enslave_ifindex() + * @see rtnl_link_release() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_enslave(struct nl_sock *sock, struct rtnl_link *master, + struct rtnl_link *slave) +{ + return rtnl_link_enslave_ifindex(sock, rtnl_link_get_ifindex(master), + rtnl_link_get_ifindex(slave)); +} + +/** + * Release slave link from its master + * @arg sock netlink socket + * @arg slave slave link + * + * This function is identical to rtnl_link_release() except that + * it takes an interface index instead of a rtnl_link object. + * + * @see rtnl_link_release() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_release_ifindex(struct nl_sock *sock, int slave) +{ + return rtnl_link_enslave_ifindex(sock, 0, slave); +} + +/** + * Release slave link from its master + * @arg sock netlink socket + * @arg slave slave link + * + * Constructs a RTM_NEWLINK or RTM_SETLINK message releasing the slave from + * its master and sends the request via the specified netlink socket. + * + * @note The feature of enslaving/releasing via netlink has only been added + * recently to the kernel (Feb 2011). Also, the kernel does not signal + * if the operation is not supported. Therefore this function will + * verify if the master assignment has changed and will return + * -NLE_OPNOTSUPP if it did not. + * + * @see rtnl_link_release_ifindex() + * @see rtnl_link_enslave() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_release(struct nl_sock *sock, struct rtnl_link *slave) +{ + return rtnl_link_release_ifindex(sock, rtnl_link_get_ifindex(slave)); +} + +/** @} */ + +/** + * @name Utilities + * @{ + */ + +static const struct trans_tbl link_flags[] = { + __ADD(IFF_LOOPBACK, loopback), + __ADD(IFF_BROADCAST, broadcast), + __ADD(IFF_POINTOPOINT, pointopoint), + __ADD(IFF_MULTICAST, multicast), + __ADD(IFF_NOARP, noarp), + __ADD(IFF_ALLMULTI, allmulti), + __ADD(IFF_PROMISC, promisc), + __ADD(IFF_MASTER, master), + __ADD(IFF_SLAVE, slave), + __ADD(IFF_DEBUG, debug), + __ADD(IFF_DYNAMIC, dynamic), + __ADD(IFF_AUTOMEDIA, automedia), + __ADD(IFF_PORTSEL, portsel), + __ADD(IFF_NOTRAILERS, notrailers), + __ADD(IFF_UP, up), + __ADD(IFF_RUNNING, running), + __ADD(IFF_LOWER_UP, lowerup), + __ADD(IFF_DORMANT, dormant), + __ADD(IFF_ECHO, echo), +}; + +char *rtnl_link_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, link_flags, + ARRAY_SIZE(link_flags)); +} + +int rtnl_link_str2flags(const char *name) +{ + return __str2flags(name, link_flags, ARRAY_SIZE(link_flags)); +} + +static const struct trans_tbl link_stats[] = { + __ADD(RTNL_LINK_RX_PACKETS, rx_packets), + __ADD(RTNL_LINK_TX_PACKETS, tx_packets), + __ADD(RTNL_LINK_RX_BYTES, rx_bytes), + __ADD(RTNL_LINK_TX_BYTES, tx_bytes), + __ADD(RTNL_LINK_RX_ERRORS, rx_errors), + __ADD(RTNL_LINK_TX_ERRORS, tx_errors), + __ADD(RTNL_LINK_RX_DROPPED, rx_dropped), + __ADD(RTNL_LINK_TX_DROPPED, tx_dropped), + __ADD(RTNL_LINK_RX_COMPRESSED, rx_compressed), + __ADD(RTNL_LINK_TX_COMPRESSED, tx_compressed), + __ADD(RTNL_LINK_RX_FIFO_ERR, rx_fifo_err), + __ADD(RTNL_LINK_TX_FIFO_ERR, tx_fifo_err), + __ADD(RTNL_LINK_RX_LEN_ERR, rx_len_err), + __ADD(RTNL_LINK_RX_OVER_ERR, rx_over_err), + __ADD(RTNL_LINK_RX_CRC_ERR, rx_crc_err), + __ADD(RTNL_LINK_RX_FRAME_ERR, rx_frame_err), + __ADD(RTNL_LINK_RX_MISSED_ERR, rx_missed_err), + __ADD(RTNL_LINK_TX_ABORT_ERR, tx_abort_err), + __ADD(RTNL_LINK_TX_CARRIER_ERR, tx_carrier_err), + __ADD(RTNL_LINK_TX_HBEAT_ERR, tx_hbeat_err), + __ADD(RTNL_LINK_TX_WIN_ERR, tx_win_err), + __ADD(RTNL_LINK_COLLISIONS, collisions), + __ADD(RTNL_LINK_MULTICAST, multicast), + __ADD(RTNL_LINK_IP6_INPKTS, Ip6InReceives), + __ADD(RTNL_LINK_IP6_INHDRERRORS, Ip6InHdrErrors), + __ADD(RTNL_LINK_IP6_INTOOBIGERRORS, Ip6InTooBigErrors), + __ADD(RTNL_LINK_IP6_INNOROUTES, Ip6InNoRoutes), + __ADD(RTNL_LINK_IP6_INADDRERRORS, Ip6InAddrErrors), + __ADD(RTNL_LINK_IP6_INUNKNOWNPROTOS, Ip6InUnknownProtos), + __ADD(RTNL_LINK_IP6_INTRUNCATEDPKTS, Ip6InTruncatedPkts), + __ADD(RTNL_LINK_IP6_INDISCARDS, Ip6InDiscards), + __ADD(RTNL_LINK_IP6_INDELIVERS, Ip6InDelivers), + __ADD(RTNL_LINK_IP6_OUTFORWDATAGRAMS, Ip6OutForwDatagrams), + __ADD(RTNL_LINK_IP6_OUTPKTS, Ip6OutRequests), + __ADD(RTNL_LINK_IP6_OUTDISCARDS, Ip6OutDiscards), + __ADD(RTNL_LINK_IP6_OUTNOROUTES, Ip6OutNoRoutes), + __ADD(RTNL_LINK_IP6_REASMTIMEOUT, Ip6ReasmTimeout), + __ADD(RTNL_LINK_IP6_REASMREQDS, Ip6ReasmReqds), + __ADD(RTNL_LINK_IP6_REASMOKS, Ip6ReasmOKs), + __ADD(RTNL_LINK_IP6_REASMFAILS, Ip6ReasmFails), + __ADD(RTNL_LINK_IP6_FRAGOKS, Ip6FragOKs), + __ADD(RTNL_LINK_IP6_FRAGFAILS, Ip6FragFails), + __ADD(RTNL_LINK_IP6_FRAGCREATES, Ip6FragCreates), + __ADD(RTNL_LINK_IP6_INMCASTPKTS, Ip6InMcastPkts), + __ADD(RTNL_LINK_IP6_OUTMCASTPKTS, Ip6OutMcastPkts), + __ADD(RTNL_LINK_IP6_INBCASTPKTS, Ip6InBcastPkts), + __ADD(RTNL_LINK_IP6_OUTBCASTPKTS, Ip6OutBcastPkts), + __ADD(RTNL_LINK_IP6_INOCTETS, Ip6InOctets), + __ADD(RTNL_LINK_IP6_OUTOCTETS, Ip6OutOctets), + __ADD(RTNL_LINK_IP6_INMCASTOCTETS, Ip6InMcastOctets), + __ADD(RTNL_LINK_IP6_OUTMCASTOCTETS, Ip6OutMcastOctets), + __ADD(RTNL_LINK_IP6_INBCASTOCTETS, Ip6InBcastOctets), + __ADD(RTNL_LINK_IP6_OUTBCASTOCTETS, Ip6OutBcastOctets), + __ADD(RTNL_LINK_ICMP6_INMSGS, ICMP6_InMsgs), + __ADD(RTNL_LINK_ICMP6_INERRORS, ICMP6_InErrors), + __ADD(RTNL_LINK_ICMP6_OUTMSGS, ICMP6_OutMsgs), + __ADD(RTNL_LINK_ICMP6_OUTERRORS, ICMP6_OutErrors), + __ADD(RTNL_LINK_ICMP6_CSUMERRORS, ICMP6_InCsumErrors), + __ADD(RTNL_LINK_IP6_CSUMERRORS, Ip6_InCsumErrors), + __ADD(RTNL_LINK_IP6_NOECTPKTS, Ip6_InNoECTPkts), + __ADD(RTNL_LINK_IP6_ECT1PKTS, Ip6_InECT1Pkts), + __ADD(RTNL_LINK_IP6_ECT0PKTS, Ip6_InECT0Pkts), + __ADD(RTNL_LINK_IP6_CEPKTS, Ip6_InCEPkts), + __ADD(RTNL_LINK_RX_NOHANDLER, rx_nohandler), + __ADD(RTNL_LINK_REASM_OVERLAPS, ReasmOverlaps), +}; + +char *rtnl_link_stat2str(int st, char *buf, size_t len) +{ + return __type2str(st, buf, len, link_stats, ARRAY_SIZE(link_stats)); +} + +int rtnl_link_str2stat(const char *name) +{ + return __str2type(name, link_stats, ARRAY_SIZE(link_stats)); +} + +static const struct trans_tbl link_operstates[] = { + __ADD(IF_OPER_UNKNOWN, unknown), + __ADD(IF_OPER_NOTPRESENT, notpresent), + __ADD(IF_OPER_DOWN, down), + __ADD(IF_OPER_LOWERLAYERDOWN, lowerlayerdown), + __ADD(IF_OPER_TESTING, testing), + __ADD(IF_OPER_DORMANT, dormant), + __ADD(IF_OPER_UP, up), +}; + +char *rtnl_link_operstate2str(uint8_t st, char *buf, size_t len) +{ + return __type2str(st, buf, len, link_operstates, + ARRAY_SIZE(link_operstates)); +} + +int rtnl_link_str2operstate(const char *name) +{ + return __str2type(name, link_operstates, + ARRAY_SIZE(link_operstates)); +} + +static const struct trans_tbl link_modes[] = { + __ADD(IF_LINK_MODE_DEFAULT, default), + __ADD(IF_LINK_MODE_DORMANT, dormant), +}; + +static const struct trans_tbl carrier_states[] = { + __ADD(0, down), + __ADD(1, up), +}; + +char *rtnl_link_mode2str(uint8_t st, char *buf, size_t len) +{ + return __type2str(st, buf, len, link_modes, ARRAY_SIZE(link_modes)); +} + +int rtnl_link_str2mode(const char *name) +{ + return __str2type(name, link_modes, ARRAY_SIZE(link_modes)); +} + +char *rtnl_link_carrier2str(uint8_t st, char *buf, size_t len) +{ + return __type2str(st, buf, len, carrier_states, + ARRAY_SIZE(carrier_states)); +} + +int rtnl_link_str2carrier(const char *name) +{ + return __str2type(name, carrier_states, ARRAY_SIZE(carrier_states)); +} + +int rtnl_link_has_vf_list(struct rtnl_link *link) { + if (link->ce_mask & LINK_ATTR_VF_LIST) + return 1; + else + return 0; +} + +void rtnl_link_set_vf_list(struct rtnl_link *link) +{ + if (!rtnl_link_has_vf_list(link)) + link->ce_mask |= LINK_ATTR_VF_LIST; +} + +void rtnl_link_unset_vf_list(struct rtnl_link *link) +{ + if (rtnl_link_has_vf_list(link)) + link->ce_mask &= ~LINK_ATTR_VF_LIST; +} + +/** @} */ + +/** + * @name Deprecated Functions + */ + +/** + * @deprecated Use of this function is deprecated, use rtnl_link_set_type() + */ +int rtnl_link_set_info_type(struct rtnl_link *link, const char *type) +{ + return rtnl_link_set_type(link, type); +} + +/** + * @deprecated Use of this function is deprecated, use rtnl_link_get_type() + */ +char *rtnl_link_get_info_type(struct rtnl_link *link) +{ + return rtnl_link_get_type(link); +} + +/** + * @deprecated The weight attribute is unused and obsoleted in all recent kernels + */ +void rtnl_link_set_weight(struct rtnl_link *link, unsigned int weight) +{ + link->l_weight = weight; + link->ce_mask |= LINK_ATTR_WEIGHT; +} + +/** + * @deprecated The weight attribute is unused and obsoleted in all recent kernels + */ +unsigned int rtnl_link_get_weight(struct rtnl_link *link) +{ + return link->l_weight; +} + +/** @} */ + +static struct nl_object_ops link_obj_ops = { + .oo_name = "route/link", + .oo_size = sizeof(struct rtnl_link), + .oo_free_data = link_free_data, + .oo_clone = link_clone, + .oo_dump = { + [NL_DUMP_LINE] = link_dump_line, + [NL_DUMP_DETAILS] = link_dump_details, + [NL_DUMP_STATS] = link_dump_stats, + }, + .oo_compare = link_compare, + .oo_keygen = link_keygen, + .oo_attrs2str = link_attrs2str, + .oo_id_attrs = LINK_ATTR_IFINDEX | LINK_ATTR_FAMILY, +}; + +static struct nl_af_group link_groups[] = { + { AF_UNSPEC, RTNLGRP_LINK }, + { AF_BRIDGE, RTNLGRP_LINK }, + { AF_INET6, RTNLGRP_IPV6_IFINFO }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops rtnl_link_ops = { + .co_name = "route/link", + .co_hdrsize = sizeof(struct ifinfomsg), + .co_msgtypes = { + { RTM_NEWLINK, NL_ACT_NEW, "new" }, + { RTM_DELLINK, NL_ACT_DEL, "del" }, + { RTM_GETLINK, NL_ACT_GET, "get" }, + { RTM_SETLINK, NL_ACT_CHANGE, "set" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = link_groups, + .co_request_update = link_request_update, + .co_msg_parser = link_msg_parser, + .co_obj_ops = &link_obj_ops, +}; + +static void _nl_init link_init(void) +{ + nl_cache_mngt_register(&rtnl_link_ops); +} + +static void _nl_exit link_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_link_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/api.c b/libnl/lib/route/link/api.c new file mode 100644 index 0000000..abc9e6a --- /dev/null +++ b/libnl/lib/route/link/api.c @@ -0,0 +1,439 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +/** + * @ingroup link + * @defgroup link_API Link Modules API + * @brief API for modules implementing specific link types/semantics. + * + * @par 1) Registering/Unregistering a new link info type + * @code + * static struct rtnl_link_info_ops vlan_info_ops = { + * .io_name = "vlan", + * .io_alloc = vlan_alloc, + * .io_parse = vlan_parse, + * .io_dump[NL_DUMP_BRIEF] = vlan_dump_brief, + * .io_dump[NL_DUMP_FULL] = vlan_dump_full, + * .io_free = vlan_free, + * }; + * + * static void _nl_init vlan_init(void) + * { + * rtnl_link_register_info(&vlan_info_ops); + * } + * + * static void _nl_exit vlan_exit(void) + * { + * rtnl_link_unregister_info(&vlan_info_ops); + * } + * @endcode + * + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" + +static NL_LIST_HEAD(info_ops); + +/* lock protecting info_ops and af_ops */ +static NL_RW_LOCK(info_lock); + +static struct rtnl_link_info_ops *__rtnl_link_info_ops_lookup(const char *name) +{ + struct rtnl_link_info_ops *ops; + + nl_list_for_each_entry(ops, &info_ops, io_list) + if (!strcmp(ops->io_name, name)) + return ops; + + return NULL; +} + +/** + * @name Link Info Modules + * @{ + */ + +/** + * Return operations of a specific link info type + * @arg name Name of link info type. + * + * @note The returned pointer must be given back using rtnl_link_info_ops_put() + * + * @return Pointer to operations or NULL if unavailable. + */ +struct rtnl_link_info_ops *rtnl_link_info_ops_lookup(const char *name) +{ + struct rtnl_link_info_ops *ops; + + nl_write_lock(&info_lock); + if ((ops = __rtnl_link_info_ops_lookup(name))) + ops->io_refcnt++; + nl_write_unlock(&info_lock); + + return ops; +} + +/** + * Take reference to a set of operations. + * @arg ops Link info operations. + */ +void rtnl_link_info_ops_get(struct rtnl_link_info_ops *ops) +{ + if (!ops) + return; + + nl_write_lock(&info_lock); + ops->io_refcnt++; + nl_write_unlock(&info_lock); +} + +/** + * Give back reference to a set of operations. + * @arg ops Link info operations. + */ +void rtnl_link_info_ops_put(struct rtnl_link_info_ops *ops) +{ + if (!ops) + return; + + nl_write_lock(&info_lock); + _nl_assert(ops->io_refcnt > 0); + ops->io_refcnt--; + nl_write_unlock(&info_lock); +} + +/** + * Register operations for a link info type + * @arg ops Link info operations + * + * This function must be called by modules implementing a specific link + * info type. It will make the operations implemented by the module + * available for everyone else. + * + * @return 0 on success or a negative error code. + * @return -NLE_INVAL Link info name not specified. + * @return -NLE_EXIST Operations for address family already registered. + */ +int rtnl_link_register_info(struct rtnl_link_info_ops *ops) +{ + int err = 0; + + if (ops->io_name == NULL) + return -NLE_INVAL; + + nl_write_lock(&info_lock); + if (__rtnl_link_info_ops_lookup(ops->io_name)) { + err = -NLE_EXIST; + goto errout; + } + + NL_DBG(1, "Registered link info operations %s\n", ops->io_name); + + nl_list_add_tail(&ops->io_list, &info_ops); +errout: + nl_write_unlock(&info_lock); + + return err; +} + +/** + * Unregister operations for a link info type + * @arg ops Link info operations + * + * This function must be called if a module implementing a specific link + * info type is unloaded or becomes unavailable. It must provide a + * set of operations which have previously been registered using + * rtnl_link_register_info(). + * + * @return 0 on success or a negative error code + * @return _NLE_OPNOTSUPP Link info operations not registered. + * @return -NLE_BUSY Link info operations still in use. + */ +int rtnl_link_unregister_info(struct rtnl_link_info_ops *ops) +{ + struct rtnl_link_info_ops *t; + int err = -NLE_OPNOTSUPP; + + nl_write_lock(&info_lock); + + nl_list_for_each_entry(t, &info_ops, io_list) { + if (t == ops) { + _nl_assert(t->io_refcnt >= 0); + if (t->io_refcnt > 0) { + err = -NLE_BUSY; + goto errout; + } + + nl_list_del(&t->io_list); + + NL_DBG(1, "Unregistered link info operations %s\n", + ops->io_name); + err = 0; + goto errout; + } + } + +errout: + nl_write_unlock(&info_lock); + + return err; +} + +/** @} */ + +/** + * @name Link Address Family Modules + * @{ + */ + +static struct rtnl_link_af_ops *af_ops[AF_MAX]; + +/** + * Return operations of a specific link address family + * @arg family Address family + * + * @note The returned pointer must be given back using rtnl_link_af_ops_put() + * + * @return Pointer to operations or NULL if unavailable. + */ +struct rtnl_link_af_ops *rtnl_link_af_ops_lookup(const unsigned int family) +{ + if (family == AF_UNSPEC || family >= AF_MAX) + return NULL; + + nl_write_lock(&info_lock); + if (af_ops[family]) + af_ops[family]->ao_refcnt++; + nl_write_unlock(&info_lock); + + return af_ops[family]; +} + +/** + * Give back reference to a set of operations. + * @arg ops Address family operations. + */ +void rtnl_link_af_ops_put(struct rtnl_link_af_ops *ops) +{ + if (ops) { + nl_write_lock(&info_lock); + ops->ao_refcnt--; + nl_write_unlock(&info_lock); + } +} + +/** + * Allocate and return data buffer for link address family modules + * @arg link Link object + * @arg ops Address family operations + * + * This function must be called by link address family modules in all + * cases where the API does not provide the data buffer as argument + * already. This typically includes set functions the module provides. + * Calling this function is strictly required to ensure proper allocation + * of the buffer upon first use. Link objects will NOT proactively + * allocate a data buffer for each registered link address family. + * + * @return Pointer to data buffer or NULL on error. + */ +void *rtnl_link_af_alloc(struct rtnl_link *link, + const struct rtnl_link_af_ops *ops) +{ + int family; + + if (!link || !ops) + BUG(); + + family = ops->ao_family; + + if (!link->l_af_data[family]) { + if (!ops->ao_alloc) + BUG(); + + link->l_af_data[family] = ops->ao_alloc(link); + if (!link->l_af_data[family]) + return NULL; + } + + return link->l_af_data[family]; +} + +/** + * Return data buffer for link address family modules + * @arg link Link object + * @arg ops Address family operations + * + * This function returns a pointer to the data buffer for the specified link + * address family module or NULL if the buffer was not allocated yet. This + * function is typically used by get functions of modules which are not + * interested in having the data buffer allocated if no values have been set + * yet. + * + * @return Pointer to data buffer or NULL on error. + */ +void *rtnl_link_af_data(const struct rtnl_link *link, + const struct rtnl_link_af_ops *ops) +{ + if (!link || !ops) + BUG(); + + return link->l_af_data[ops->ao_family]; +} + +/** + * Register operations for a link address family + * @arg ops Address family operations + * + * This function must be called by modules implementing a specific link + * address family. It will make the operations implemented by the module + * available for everyone else. + * + * @return 0 on success or a negative error code. + * @return -NLE_INVAL Address family is out of range (0..AF_MAX) + * @return -NLE_EXIST Operations for address family already registered. + */ +int rtnl_link_af_register(struct rtnl_link_af_ops *ops) +{ + int err = 0; + + if (ops->ao_family == AF_UNSPEC || ops->ao_family >= AF_MAX) + return -NLE_INVAL; + + nl_write_lock(&info_lock); + if (af_ops[ops->ao_family]) { + err = -NLE_EXIST; + goto errout; + } + + ops->ao_refcnt = 0; + af_ops[ops->ao_family] = ops; + + NL_DBG(1, "Registered link address family operations %u\n", + ops->ao_family); + +errout: + nl_write_unlock(&info_lock); + + return err; +} + +/** + * Unregister operations for a link address family + * @arg ops Address family operations + * + * This function must be called if a module implementing a specific link + * address family is unloaded or becomes unavailable. It must provide a + * set of operations which have previously been registered using + * rtnl_link_af_register(). + * + * @return 0 on success or a negative error code + * @return -NLE_INVAL ops is NULL + * @return -NLE_OBJ_NOTFOUND Address family operations not registered. + * @return -NLE_BUSY Address family operations still in use. + */ +int rtnl_link_af_unregister(struct rtnl_link_af_ops *ops) +{ + int err = -NLE_INVAL; + + if (!ops) + return err; + + nl_write_lock(&info_lock); + if (!af_ops[ops->ao_family]) { + err = -NLE_OBJ_NOTFOUND; + goto errout; + } + + if (ops->ao_refcnt > 0) { + err = -NLE_BUSY; + goto errout; + } + + af_ops[ops->ao_family] = NULL; + + NL_DBG(1, "Unregistered link address family operations %u\n", + ops->ao_family); + +errout: + nl_write_unlock(&info_lock); + + return err; +} + +/** + * Compare af data for a link address family + * @arg a Link object a + * @arg b Link object b + * @arg family af data family + * + * This function will compare af_data between two links + * a and b of family given by arg family + * + * @return 0 if address family specific data matches or is not present + * or != 0 if it mismatches. + */ +int rtnl_link_af_data_compare(struct rtnl_link *a, struct rtnl_link *b, + int family) +{ + struct rtnl_link_af_ops *af_ops; + int ret = 0; + + if (!a->l_af_data[family] && !b->l_af_data[family]) + return 0; + + if (!a->l_af_data[family] || !b->l_af_data[family]) + return ~0; + + af_ops = rtnl_link_af_ops_lookup(family); + if (!af_ops) + return ~0; + + if (af_ops->ao_compare == NULL) { + ret = ~0; + goto out; + } + + ret = af_ops->ao_compare(a, b, family, ~0, 0); + +out: + rtnl_link_af_ops_put(af_ops); + + return ret; +} + +/** + * Compare link info data + * @arg a Link object a + * @arg b Link object b + * + * This function will compare link_info data between two links + * a and b + * + * @return 0 if link_info data matches or is not present + * or != 0 if it mismatches. + */ +int rtnl_link_info_data_compare(struct rtnl_link *a, struct rtnl_link *b, int flags) +{ + if (a->l_info_ops != b->l_info_ops) + return ~0; + + if (!a->l_info_ops || !a->l_info_ops->io_compare) + return 0; + + return a->l_info_ops->io_compare(a, b, flags); +} + +/** @} */ + +/** @} */ + diff --git a/libnl/lib/route/link/bonding.c b/libnl/lib/route/link/bonding.c new file mode 100644 index 0000000..53b70ca --- /dev/null +++ b/libnl/lib/route/link/bonding.c @@ -0,0 +1,619 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2011-2013 Thomas Graf + */ + +/** + * @ingroup link + * @defgroup bonding Bonding + * + * @details + * \b Link Type Name: "bond" + * + * @route_doc{link_bonding, Bonding Documentation} + * @{ + */ + +#include "nl-default.h" + +#include +#include + +#include "nl-route.h" +#include "link-api.h" + +#define BOND_HAS_MODE (1 << 0) +#define BOND_HAS_ACTIVE_SLAVE (1 << 1) +#define BOND_HAS_HASHING_TYPE (1 << 2) +#define BOND_HAS_MIIMON (1 << 3) +#define BOND_HAS_MIN_LINKS (1 << 4) + +struct bond_info { + uint32_t ce_mask; /* to support attr macros */ + uint8_t bn_mode; + uint8_t hashing_type; + uint32_t ifindex; + uint32_t miimon; + uint32_t min_links; +}; + +static int bond_info_alloc(struct rtnl_link *link) +{ + struct bond_info *bn; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*bn)); + else { + bn = calloc(1, sizeof(*bn)); + if (!bn) + return -NLE_NOMEM; + + link->l_info = bn; + } + + return 0; +} + +static struct nla_policy bonding_nl_policy[IFLA_BOND_MAX + 1] = { + [IFLA_BOND_MODE] = { .type = NLA_U8 }, + [IFLA_BOND_ACTIVE_SLAVE] = { .type = NLA_U32 }, + [IFLA_BOND_MIIMON] = { .type = NLA_U32 }, + [IFLA_BOND_XMIT_HASH_POLICY] = { .type = NLA_U8 }, + [IFLA_BOND_MIN_LINKS] = { .type = NLA_U32 }, +}; + +static int bond_info_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_BOND_MAX + 1]; + struct bond_info *bn; + int err; + + err = nla_parse_nested(tb, IFLA_BOND_MAX, data, bonding_nl_policy); + if (err < 0) + return err; + + err = bond_info_alloc(link); + if (err < 0) + return err; + + bn = link->l_info; + + if (tb[IFLA_BOND_MODE]) { + bn->bn_mode = nla_get_u8(tb[IFLA_BOND_MODE]); + bn->ce_mask |= BOND_HAS_MODE; + } + + if (tb[IFLA_BOND_ACTIVE_SLAVE]) { + bn->ifindex = nla_get_u32(tb[IFLA_BOND_ACTIVE_SLAVE]); + bn->ce_mask |= BOND_HAS_ACTIVE_SLAVE; + } + + if (tb[IFLA_BOND_MIIMON]) { + bn->hashing_type = nla_get_u32(tb[IFLA_BOND_MIIMON]); + bn->ce_mask |= BOND_HAS_MIIMON; + } + + if (tb[IFLA_BOND_XMIT_HASH_POLICY]) { + bn->hashing_type = nla_get_u8(tb[IFLA_BOND_XMIT_HASH_POLICY]); + bn->ce_mask |= BOND_HAS_HASHING_TYPE; + } + + if (tb[IFLA_BOND_MIN_LINKS]) { + bn->hashing_type = nla_get_u32(tb[IFLA_BOND_MIN_LINKS]); + bn->ce_mask |= BOND_HAS_MIN_LINKS; + } + + return 0; +} + +static void bond_info_free(struct rtnl_link *link) +{ + _nl_clear_free(&link->l_info); +} + +static int bond_info_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct bond_info *bond_dst, *bond_src = src->l_info; + int err; + + _nl_assert(bond_src); + + err = bond_info_alloc(dst); + if (err) + return err; + + bond_dst = dst->l_info; + + _nl_assert(bond_dst); + + *bond_dst = *bond_src; + + return 0; +} + +static int bond_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct bond_info *bn = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + if (bn->ce_mask & BOND_HAS_MODE) + NLA_PUT_U8(msg, IFLA_BOND_MODE, bn->bn_mode); + + if (bn->ce_mask & BOND_HAS_ACTIVE_SLAVE) + NLA_PUT_U32(msg, IFLA_BOND_ACTIVE_SLAVE, bn->ifindex); + + if (bn->ce_mask & BOND_HAS_HASHING_TYPE) + NLA_PUT_U8(msg, IFLA_BOND_XMIT_HASH_POLICY, bn->hashing_type); + + if (bn->ce_mask & BOND_HAS_MIIMON) + NLA_PUT_U32(msg, IFLA_BOND_MIIMON, bn->miimon); + + if (bn->ce_mask & BOND_HAS_MIN_LINKS) + NLA_PUT_U32(msg, IFLA_BOND_MIN_LINKS, bn->min_links); + + nla_nest_end(msg, data); + return 0; + +nla_put_failure: + nla_nest_cancel(msg, data); + return -NLE_MSGSIZE; +} + +static int bond_info_compare(struct rtnl_link *link_a, struct rtnl_link *link_b, + int flags) +{ + struct bond_info *a = link_a->l_info; + struct bond_info *b = link_b->l_info; + uint32_t attrs = flags & LOOSE_COMPARISON ? b->ce_mask : ~0u; + int diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(BOND_HAS_MODE, a->bn_mode != b->bn_mode); + diff |= _DIFF(BOND_HAS_ACTIVE_SLAVE, a->ifindex != b->ifindex); + diff |= _DIFF(BOND_HAS_HASHING_TYPE, + a->hashing_type != b->hashing_type); + diff |= _DIFF(BOND_HAS_MIIMON, a->miimon != b->miimon); + diff |= _DIFF(BOND_HAS_MIN_LINKS, a->min_links != b->min_links); +#undef _DIFF + + return diff; +} + +static struct rtnl_link_info_ops bonding_info_ops = { + .io_name = "bond", + .io_alloc = bond_info_alloc, + .io_parse = bond_info_parse, + .io_clone = bond_info_clone, + .io_put_attrs = bond_put_attrs, + .io_free = bond_info_free, + .io_compare = bond_info_compare, +}; + +#define IS_BOND_INFO_ASSERT(link) \ + do { \ + if (link->l_info_ops != &bonding_info_ops) { \ + APPBUG("Link is not a bond link. Set type \"bond\" first."); \ + } \ + } while (0) + +/** + * Set active slave for bond + * @arg link Link object of type bond + * @arg active ifindex of active slave to set + * + * @return void + */ +void rtnl_link_bond_set_activeslave(struct rtnl_link *link, int active_slave) +{ + struct bond_info *bn = link->l_info; + + IS_BOND_INFO_ASSERT(link); + + bn->ifindex = active_slave; + + bn->ce_mask |= BOND_HAS_ACTIVE_SLAVE; +} + +/** + * Get active slave for bond + * @arg link Link object of type bond + * @arg active_slave Output argument + * + * @return Zero on success, otherwise a negative error code. + * @retval -NLE_NOATTR + * @retval -NLE_INVAL + */ +int rtnl_link_bond_get_activeslave(struct rtnl_link *link, int *active_slave) +{ + struct bond_info *bn = link->l_info; + + IS_BOND_INFO_ASSERT(link); + + if (!(bn->ce_mask & BOND_HAS_ACTIVE_SLAVE)) + return -NLE_NOATTR; + + if (!active_slave) + return -NLE_INVAL; + + *active_slave = bn->ifindex; + + return 0; +} + +/** + * Set bond mode + * @arg link Link object of type bond + * @arg mode bond mode to set + * + * @return void + */ +void rtnl_link_bond_set_mode(struct rtnl_link *link, uint8_t mode) +{ + struct bond_info *bn = link->l_info; + + IS_BOND_INFO_ASSERT(link); + + bn->bn_mode = mode; + + bn->ce_mask |= BOND_HAS_MODE; +} + +/** + * Get bond mode + * @arg link Link object of type bond + * @arg mode Output argument + * + * @return Zero on success, otherwise a negative error code. + * @retval -NLE_NOATTR + * @retval -NLE_INVAL + */ +int rtnl_link_bond_get_mode(struct rtnl_link *link, uint8_t *mode) +{ + struct bond_info *bn = link->l_info; + + IS_BOND_INFO_ASSERT(link); + + if (!(bn->ce_mask & BOND_HAS_MODE)) + return -NLE_NOATTR; + + if (!mode) + return -NLE_INVAL; + + *mode = bn->bn_mode; + + return 0; +} + +/** + * Set hashing type + * @arg link Link object of type bond + * @arg type bond hashing type to set + * + * @return void + */ +void rtnl_link_bond_set_hashing_type(struct rtnl_link *link, uint8_t type) +{ + struct bond_info *bn = link->l_info; + + IS_BOND_INFO_ASSERT(link); + + bn->hashing_type = type; + + bn->ce_mask |= BOND_HAS_HASHING_TYPE; +} + +/** + * Get hashing type + * @arg link Link object of type bond + * @arg type Output argument + * + * @return Zero on success, otherwise a negative error code. + * @retval -NLE_NOATTR + * @retval -NLE_INVAL + */ +int rtnl_link_bond_get_hashing_type(struct rtnl_link *link, uint8_t *type) +{ + struct bond_info *bn = link->l_info; + + IS_BOND_INFO_ASSERT(link); + + if (!(bn->ce_mask & BOND_HAS_HASHING_TYPE)) + return -NLE_NOATTR; + + if (!type) + return -NLE_INVAL; + + *type = bn->hashing_type; + + return 0; +} + +/** + * Set MII monitoring interval + * @arg link Link object of type bond + * @arg miimon interval in milliseconds + * + * @return void + */ +void rtnl_link_bond_set_miimon(struct rtnl_link *link, uint32_t miimon) +{ + struct bond_info *bn = link->l_info; + + IS_BOND_INFO_ASSERT(link); + + bn->miimon = miimon; + + bn->ce_mask |= BOND_HAS_MIIMON; +} + +/** + * Get MII monitoring interval + * @arg link Link object of type bond + * @arg miimon Output argument + * + * @return Zero on success, otherwise a negative error code. + * @retval -NLE_NOATTR + * @retval -NLE_INVAL + */ +int rtnl_link_bond_get_miimon(struct rtnl_link *link, uint32_t *miimon) +{ + struct bond_info *bn = link->l_info; + + IS_BOND_INFO_ASSERT(link); + + if (!(bn->ce_mask & BOND_HAS_MIIMON)) + return -NLE_NOATTR; + + if (!miimon) + return -NLE_INVAL; + + *miimon = bn->miimon; + + return 0; +} + +/** + * Set the minimum number of member ports that must be up before + * marking the bond device as up + * @arg link Link object of type bond + * @arg min_links Number of links + * + * @return void + */ +void rtnl_link_bond_set_min_links(struct rtnl_link *link, uint32_t min_links) +{ + struct bond_info *bn = link->l_info; + + IS_BOND_INFO_ASSERT(link); + + bn->min_links = min_links; + + bn->ce_mask |= BOND_HAS_MIN_LINKS; +} +/** + * Get the minimum number of member ports that must be up before + * marking the bond device as up + * @arg link Link object of type bond + * @arg min_links Output argument. + * + * @return Zero on success, otherwise a negative error code. + * @retval -NLE_NOATTR + * @retval -NLE_INVAL + */ +int rtnl_link_bond_get_min_links(struct rtnl_link *link, uint32_t *min_links) +{ + struct bond_info *bn = link->l_info; + + IS_BOND_INFO_ASSERT(link); + + if (!(bn->ce_mask & BOND_HAS_MIN_LINKS)) + return -NLE_NOATTR; + + if (!min_links) + return -NLE_INVAL; + + *min_links = bn->min_links; + + return 0; +} + +/** + * Allocate link object of type bond + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_bond_alloc(void) +{ + struct rtnl_link *link; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if (rtnl_link_set_type(link, "bond") < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Create a new kernel bonding device + * @arg sock netlink socket + * @arg name name of bonding device or NULL + * @arg opts bonding options (currently unused) + * + * Creates a new bonding device in the kernel. If no name is + * provided, the kernel will automatically pick a name of the + * form "type%d" (e.g. bond0, vlan1, etc.) + * + * The \a opts argument is currently unused. In the future, it + * may be used to carry additional bonding options to be set + * when creating the bonding device. + * + * @note When letting the kernel assign a name, it will become + * difficult to retrieve the interface afterwards because + * you have to guess the name the kernel has chosen. It is + * therefore not recommended to not provide a device name. + * + * @see rtnl_link_bond_enslave() + * @see rtnl_link_bond_release() + * + * @return 0 on success or a negative error code + */ +int rtnl_link_bond_add(struct nl_sock *sock, const char *name, + struct rtnl_link *opts) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_bond_alloc())) + return -NLE_NOMEM; + + if (!name && opts) + name = rtnl_link_get_name(opts); + + if (name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sock, link, NLM_F_CREATE); + + rtnl_link_put(link); + + return err; +} + +/** + * Add a link to a bond (enslave) + * @arg sock netlink socket + * @arg master ifindex of bonding master + * @arg slave ifindex of slave link to add to bond + * + * This function is identical to rtnl_link_bond_enslave() except that + * it takes interface indices instead of rtnl_link objcets. + * + * @see rtnl_link_bond_enslave() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_bond_enslave_ifindex(struct nl_sock *sock, int master, + int slave) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_bond_alloc())) + return -NLE_NOMEM; + + rtnl_link_set_ifindex(link, slave); + rtnl_link_set_master(link, master); + + if ((err = rtnl_link_change(sock, link, link, 0)) < 0) + goto errout; + + rtnl_link_put(link); + + /* + * Due to the kernel not signaling whether this opertion is + * supported or not, we will retrieve the attribute to see if the + * request was successful. If the master assigned remains unchanged + * we will return NLE_OPNOTSUPP to allow performing backwards + * compatibility of some sort. + */ + if ((err = rtnl_link_get_kernel(sock, slave, NULL, &link)) < 0) + return err; + + if (rtnl_link_get_master(link) != master) + err = -NLE_OPNOTSUPP; + +errout: + rtnl_link_put(link); + + return err; +} + +/** + * Add a link to a bond (enslave) + * @arg sock netlink socket + * @arg master bonding master + * @arg slave slave link to add to bond + * + * Constructs a RTM_NEWLINK or RTM_SETLINK message adding the slave to + * the master and sends the request via the specified netlink socket. + * + * @note The feature of enslaving/releasing via netlink has only been added + * recently to the kernel (Feb 2011). Also, the kernel does not signal + * if the operation is not supported. Therefore this function will + * verify if the master assignment has changed and will return + * -NLE_OPNOTSUPP if it did not. + * + * @see rtnl_link_bond_enslave_ifindex() + * @see rtnl_link_bond_release() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_bond_enslave(struct nl_sock *sock, struct rtnl_link *master, + struct rtnl_link *slave) +{ + return rtnl_link_bond_enslave_ifindex(sock, + rtnl_link_get_ifindex(master), + rtnl_link_get_ifindex(slave)); +} + +/** + * Release a link from a bond + * @arg sock netlink socket + * @arg slave slave link to be released + * + * This function is identical to rtnl_link_bond_release() except that + * it takes an interface index instead of a rtnl_link object. + * + * @see rtnl_link_bond_release() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_bond_release_ifindex(struct nl_sock *sock, int slave) +{ + return rtnl_link_bond_enslave_ifindex(sock, 0, slave); +} + +/** + * Release a link from a bond + * @arg sock netlink socket + * @arg slave slave link to be released + * + * Constructs a RTM_NEWLINK or RTM_SETLINK message releasing the slave from + * its master and sends the request via the specified netlink socket. + * + * @note The feature of enslaving/releasing via netlink has only been added + * recently to the kernel (Feb 2011). Also, the kernel does not signal + * if the operation is not supported. Therefore this function will + * verify if the master assignment has changed and will return + * -NLE_OPNOTSUPP if it did not. + * + * @see rtnl_link_bond_release_ifindex() + * @see rtnl_link_bond_enslave() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_bond_release(struct nl_sock *sock, struct rtnl_link *slave) +{ + return rtnl_link_bond_release_ifindex(sock, + rtnl_link_get_ifindex(slave)); +} + +static void _nl_init bonding_init(void) +{ + rtnl_link_register_info(&bonding_info_ops); +} + +static void _nl_exit bonding_exit(void) +{ + rtnl_link_unregister_info(&bonding_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/bridge.c b/libnl/lib/route/link/bridge.c new file mode 100644 index 0000000..2df8d85 --- /dev/null +++ b/libnl/lib/route/link/bridge.c @@ -0,0 +1,1873 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2013 Thomas Graf + */ + +/** + * @ingroup link + * @defgroup bridge Bridging + * + * @details + * @{ + */ + +#include "nl-default.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-static-route/nl-priv-static-route.h" + +#define VLAN_VID_MASK 0x0fff /* VLAN Identifier */ + +/** @cond SKIP */ +#define BRIDGE_ATTR_PORT_STATE (1UL << 0) +#define BRIDGE_ATTR_PRIORITY (1UL << 1) +#define BRIDGE_ATTR_COST (1UL << 2) +#define BRIDGE_ATTR_FLAGS (1UL << 3) +#define BRIDGE_ATTR_PORT_VLAN (1UL << 4) +#define BRIDGE_ATTR_HWMODE (1UL << 5) +#define BRIDGE_ATTR_CONFIG_MODE (1UL << 6) +#define BRIDGE_ATTR_MST (1UL << 7) + +#define PRIV_FLAG_NEW_ATTRS (1UL << 0) + +struct bridge_data +{ + uint8_t b_port_state; + uint8_t b_priv_flags; /* internal flags */ + uint16_t b_hwmode; + uint16_t b_priority; + uint16_t b_config_mode; + uint32_t b_cost; + uint32_t b_flags; + uint32_t b_flags_mask; + uint32_t ce_mask; /* HACK to support attr macros */ + struct rtnl_link_bridge_vlan vlan_info; + struct nl_list_head mst_list; +}; + +struct mst_state_entry { + struct nl_list_head list_node; + uint16_t msti; /* unique within a list */ + uint8_t state; +}; + +static void set_bit(unsigned nr, uint32_t *addr) +{ + if (nr < RTNL_LINK_BRIDGE_VLAN_BITMAP_MAX) + addr[nr / 32] |= (((uint32_t) 1) << (nr % 32)); +} + +static void unset_bit(unsigned nr, uint32_t *addr) +{ + if (nr < RTNL_LINK_BRIDGE_VLAN_BITMAP_MAX) + addr[nr / 32] &= ~(((uint32_t) 1) << (nr % 32)); +} + +static bool vlan_id_untagged(struct rtnl_link_bridge_vlan *vlan_info, uint16_t vid) +{ + uint32_t mask, bit; + + _nl_assert(vid / 32u < ARRAY_SIZE(vlan_info->untagged_bitmap)); + + mask = vlan_info->untagged_bitmap[vid / 32]; + bit = (((uint32_t) 1) << vid % 32); + + return mask & bit; +} + +static int find_next_bit(int i, uint32_t x) +{ + int j; + + if (i >= 32) + return -1; + + /* find first bit */ + if (i < 0) + return __builtin_ffs(x); + + /* mask off prior finds to get next */ + j = __builtin_ffs(x >> i); + return j ? j + i : 0; +} + +static struct rtnl_link_af_ops bridge_ops; + +#define IS_BRIDGE_LINK_ASSERT(link) \ + if (!rtnl_link_is_bridge(link)) { \ + APPBUG("A function was expecting a link object of type bridge."); \ + return -NLE_OPNOTSUPP; \ + } + +static inline struct bridge_data *bridge_data(struct rtnl_link *link) +{ + return rtnl_link_af_data(link, &bridge_ops); +} + +static void *bridge_alloc(struct rtnl_link *link) +{ + struct bridge_data *bridge_data = calloc(1, sizeof(struct bridge_data)); + + if (bridge_data == NULL) + return NULL; + + nl_init_list_head(&bridge_data->mst_list); + + return bridge_data; +} + +static struct mst_state_entry *mst_state_entry_alloc(void) +{ + struct mst_state_entry *entry; + + entry = calloc(1, sizeof(*entry)); + if (entry == NULL) + return NULL; + + nl_init_list_head(&entry->list_node); + + return entry; +} + +static struct mst_state_entry *mst_state_entry_create(uint16_t msti, + uint8_t state) +{ + struct mst_state_entry *entry; + + entry = mst_state_entry_alloc(); + if (entry == NULL) + return NULL; + + entry->msti = msti; + entry->state = state; + + return entry; +} + +static void mst_state_entry_del(struct mst_state_entry *entry) +{ + nl_list_del(&entry->list_node); + free(entry); +} + +static void mst_list_clear(struct nl_list_head *mst_list) +{ + struct mst_state_entry *entry; + struct mst_state_entry *entry_safe; + + nl_list_for_each_entry_safe(entry, entry_safe, mst_list, list_node) + mst_state_entry_del(entry); +} + +static void bridge_data_free(struct bridge_data *bd) +{ + mst_list_clear(&bd->mst_list); + free(bd); +} + +static void bridge_free(struct rtnl_link *link, void *data) +{ + bridge_data_free(data); +} + +static struct mst_state_entry *find_mst_state_entry(struct bridge_data *bd, + uint16_t msti) +{ + struct mst_state_entry *entry; + + nl_list_for_each_entry(entry, &bd->mst_list, list_node) { + if (entry->msti == msti) + return entry; + } + + return NULL; +} + +static struct mst_state_entry * +mst_state_entry_clone(struct mst_state_entry *src) +{ + return mst_state_entry_create(src->msti, src->state); +} + +static void *bridge_clone(struct rtnl_link *link, void *data) +{ + struct bridge_data *src_bd = (struct bridge_data *)data; + struct bridge_data *dst_bd; + struct mst_state_entry *entry; + + dst_bd = calloc(1, sizeof(*dst_bd)); + if (!dst_bd) + return NULL; + + memcpy(dst_bd, src_bd, sizeof(*dst_bd)); + + nl_init_list_head(&dst_bd->mst_list); + + nl_list_for_each_entry(entry, &src_bd->mst_list, list_node) { + struct mst_state_entry *entry_copy = + mst_state_entry_clone(entry); + + if (!entry_copy) { + bridge_data_free(dst_bd); + return NULL; + } + + nl_list_add_tail(&entry_copy->list_node, &dst_bd->mst_list); + } + + return dst_bd; +} + +static struct nla_policy br_attrs_policy[IFLA_BRPORT_MAX+1] = { + [IFLA_BRPORT_STATE] = { .type = NLA_U8 }, + [IFLA_BRPORT_PRIORITY] = { .type = NLA_U16 }, + [IFLA_BRPORT_COST] = { .type = NLA_U32 }, + [IFLA_BRPORT_MODE] = { .type = NLA_U8 }, + [IFLA_BRPORT_GUARD] = { .type = NLA_U8 }, + [IFLA_BRPORT_PROTECT] = { .type = NLA_U8 }, + [IFLA_BRPORT_FAST_LEAVE] = { .type = NLA_U8 }, + [IFLA_BRPORT_LEARNING] = { .type = NLA_U8 }, + [IFLA_BRPORT_LEARNING_SYNC] = { .type = NLA_U8 }, + [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 }, + [IFLA_BRPORT_PROXYARP] = { .type = NLA_U8 }, + [IFLA_BRPORT_PROXYARP_WIFI] = { .type = NLA_U8 }, + [IFLA_BRPORT_MCAST_FLOOD] = { .type = NLA_U8 }, + [IFLA_BRPORT_MCAST_TO_UCAST] = { .type = NLA_U8 }, + [IFLA_BRPORT_VLAN_TUNNEL] = { .type = NLA_U8 }, + [IFLA_BRPORT_BCAST_FLOOD] = { .type = NLA_U8 }, + [IFLA_BRPORT_NEIGH_SUPPRESS] = { .type = NLA_U8 }, + [IFLA_BRPORT_ISOLATED] = { .type = NLA_U8 }, + [IFLA_BRPORT_LOCKED] = { .type = NLA_U8 }, + [IFLA_BRPORT_MAB] = { .type = NLA_U8 }, + [IFLA_BRPORT_NEIGH_VLAN_SUPPRESS] = { .type = NLA_U8 }, +}; + +static void check_flag(struct rtnl_link *link, struct nlattr *attrs[], + int type, int flag) +{ + if (attrs[type] && nla_get_u8(attrs[type])) + rtnl_link_bridge_set_flags(link, flag); +} + +static int bridge_parse_protinfo(struct rtnl_link *link, struct nlattr *attr, + void *data) +{ + struct bridge_data *bd = data; + struct nlattr *br_attrs[IFLA_BRPORT_MAX+1]; + int err; + + /* Backwards compatibility */ + if (!nla_is_nested(attr)) { + if (nla_len(attr) < 1) + return -NLE_RANGE; + + bd->b_port_state = nla_get_u8(attr); + bd->ce_mask |= BRIDGE_ATTR_PORT_STATE; + + return 0; + } + + if ((err = nla_parse_nested(br_attrs, IFLA_BRPORT_MAX, attr, + br_attrs_policy)) < 0) + return err; + + bd->b_priv_flags |= PRIV_FLAG_NEW_ATTRS; + + if (br_attrs[IFLA_BRPORT_STATE]) { + bd->b_port_state = nla_get_u8(br_attrs[IFLA_BRPORT_STATE]); + bd->ce_mask |= BRIDGE_ATTR_PORT_STATE; + } + + if (br_attrs[IFLA_BRPORT_PRIORITY]) { + bd->b_priority = nla_get_u16(br_attrs[IFLA_BRPORT_PRIORITY]); + bd->ce_mask |= BRIDGE_ATTR_PRIORITY; + } + + if (br_attrs[IFLA_BRPORT_COST]) { + bd->b_cost = nla_get_u32(br_attrs[IFLA_BRPORT_COST]); + bd->ce_mask |= BRIDGE_ATTR_COST; + } + + check_flag(link, br_attrs, IFLA_BRPORT_MODE, RTNL_BRIDGE_HAIRPIN_MODE); + check_flag(link, br_attrs, IFLA_BRPORT_GUARD, RTNL_BRIDGE_BPDU_GUARD); + check_flag(link, br_attrs, IFLA_BRPORT_PROTECT, RTNL_BRIDGE_ROOT_BLOCK); + check_flag(link, br_attrs, IFLA_BRPORT_FAST_LEAVE, RTNL_BRIDGE_FAST_LEAVE); + check_flag(link, br_attrs, IFLA_BRPORT_UNICAST_FLOOD, + RTNL_BRIDGE_UNICAST_FLOOD); + check_flag(link, br_attrs, IFLA_BRPORT_LEARNING, RTNL_BRIDGE_LEARNING); + check_flag(link, br_attrs, IFLA_BRPORT_LEARNING_SYNC, + RTNL_BRIDGE_LEARNING_SYNC); + check_flag(link, br_attrs, IFLA_BRPORT_PROXYARP, RTNL_BRIDGE_PROXYARP); + check_flag(link, br_attrs, IFLA_BRPORT_PROXYARP_WIFI, + RTNL_BRIDGE_PROXYARP_WIFI); + check_flag(link, br_attrs, IFLA_BRPORT_MCAST_FLOOD, + RTNL_BRIDGE_MCAST_FLOOD); + check_flag(link, br_attrs, IFLA_BRPORT_MCAST_TO_UCAST, + RTNL_BRIDGE_MCAST_TO_UCAST); + check_flag(link, br_attrs, IFLA_BRPORT_VLAN_TUNNEL, + RTNL_BRIDGE_VLAN_TUNNEL); + check_flag(link, br_attrs, IFLA_BRPORT_BCAST_FLOOD, + RTNL_BRIDGE_BCAST_FLOOD); + check_flag(link, br_attrs, IFLA_BRPORT_NEIGH_SUPPRESS, + RTNL_BRIDGE_NEIGH_SUPPRESS); + check_flag(link, br_attrs, IFLA_BRPORT_ISOLATED, RTNL_BRIDGE_ISOLATED); + check_flag(link, br_attrs, IFLA_BRPORT_LOCKED, RTNL_BRIDGE_LOCKED); + check_flag(link, br_attrs, IFLA_BRPORT_MAB, RTNL_BRIDGE_MAB); + check_flag(link, br_attrs, IFLA_BRPORT_NEIGH_VLAN_SUPPRESS, + RTNL_BRIDGE_NEIGH_VLAN_SUPPRESS); + + return 0; +} + +static const struct nla_policy br_mst_entry_policy[IFLA_BRIDGE_MST_ENTRY_MAX + 1] = { + [IFLA_BRIDGE_MST_ENTRY_MSTI] = { .type = NLA_U16 }, + [IFLA_BRIDGE_MST_ENTRY_STATE] = { .type = NLA_U8 }, +}; + +static int bridge_parse_mst_state_entry(struct nlattr *attr, + struct bridge_data *bd) +{ + struct nlattr *tb[IFLA_BRIDGE_MST_ENTRY_MAX + 1]; + struct mst_state_entry *new_entry; + struct mst_state_entry *existing_entry; + uint16_t msti; + uint8_t state; + + if (nla_parse_nested(tb, IFLA_BRIDGE_MST_ENTRY_MAX, attr, + br_mst_entry_policy) < 0) + return -EINVAL; + + if (!tb[IFLA_BRIDGE_MST_ENTRY_MSTI] || + !tb[IFLA_BRIDGE_MST_ENTRY_STATE]) { + return -EINVAL; + } + + msti = nla_get_u16(tb[IFLA_BRIDGE_MST_ENTRY_MSTI]); + state = nla_get_u8(tb[IFLA_BRIDGE_MST_ENTRY_STATE]); + + existing_entry = find_mst_state_entry(bd, msti); + if (existing_entry) { + existing_entry->state = state; + return 0; + } + + new_entry = mst_state_entry_create(msti, state); + if (!new_entry) + return -ENOMEM; + + nl_list_add_tail(&new_entry->list_node, &bd->mst_list); + bd->ce_mask |= BRIDGE_ATTR_MST; + + return 0; +} + +static int bridge_parse_mst(struct nlattr *mst_attr, struct bridge_data *bd) { + struct nlattr *attr; + int remaining; + + nla_for_each_nested(attr, mst_attr, remaining) { + int err = 0; + + switch (nla_type(attr)) { + case IFLA_BRIDGE_MST_ENTRY: + err = bridge_parse_mst_state_entry(attr, bd); + break; + default: + continue; + } + + if (err) + return err; + } + + return 0; +} + +static int bridge_parse_af_full(struct rtnl_link *link, struct nlattr *attr_full, + void *data) +{ + struct bridge_data *bd = data; + struct bridge_vlan_info *vinfo = NULL; + uint16_t vid_range_start = 0; + uint16_t vid_range_flags = -1; + + struct nlattr *attr; + int remaining; + + nla_for_each_nested(attr, attr_full, remaining) { + + if (nla_type(attr) == IFLA_BRIDGE_MODE) { + bd->b_hwmode = nla_get_u16(attr); + bd->ce_mask |= BRIDGE_ATTR_HWMODE; + continue; + } else if (nla_type(attr) == IFLA_BRIDGE_MST) { + int err = bridge_parse_mst(attr, bd); + if (err < 0) + return err; + continue; + } else if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO) + continue; + + if (nla_len(attr) != sizeof(struct bridge_vlan_info)) + return -EINVAL; + + vinfo = nla_data(attr); + if (!vinfo->vid || vinfo->vid >= VLAN_VID_MASK) + return -EINVAL; + + + if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { + vid_range_start = vinfo->vid; + vid_range_flags = (vinfo->flags ^ BRIDGE_VLAN_INFO_RANGE_BEGIN); + continue; + } + + if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END) { + /* sanity check the range flags */ + if (vid_range_flags != (vinfo->flags ^ BRIDGE_VLAN_INFO_RANGE_END)) { + NL_DBG(1, "VLAN range flags differ; can not handle it.\n"); + return -EINVAL; + } + } else { + vid_range_start = vinfo->vid; + } + + for (; vid_range_start <= vinfo->vid; vid_range_start++) { + if (vinfo->flags & BRIDGE_VLAN_INFO_PVID) + bd->vlan_info.pvid = vinfo->vid; + + if (vinfo->flags & BRIDGE_VLAN_INFO_UNTAGGED) + set_bit(vid_range_start, bd->vlan_info.untagged_bitmap); + + set_bit(vid_range_start, bd->vlan_info.vlan_bitmap); + bd->ce_mask |= BRIDGE_ATTR_PORT_VLAN; + } + + vid_range_flags = -1; + } + + return 0; +} + +int _nl_bridge_fill_vlan_info(struct nl_msg *msg, struct rtnl_link_bridge_vlan * vlan_info) +{ + struct bridge_vlan_info vinfo; + int i = -1, j, k; + int start = -1, prev = -1; + int done; + bool untagged = false; + + for (k = 0; k < RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN; k++) + { + int base_bit; + uint32_t a = vlan_info->vlan_bitmap[k]; + + base_bit = k * 32; + i = -1; + done = 0; + while (!done) + { + j = find_next_bit(i, a); + if (j > 0) + { + /* Skip if id equal to pvid */ + if (vlan_info->pvid != 0 && j - 1 + base_bit == vlan_info->pvid) + goto nxt; + /* first hit of any bit */ + if (start < 0 && prev < 0) + { + start = prev = j - 1 + base_bit; + /* Start range attribute */ + untagged = vlan_id_untagged(vlan_info,start); + vinfo.flags = BRIDGE_VLAN_INFO_RANGE_BEGIN; + vinfo.flags |= untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0; + vinfo.vid = start; + goto nxt; + } + /* this bit is a continuation of prior bits */ + if (j - 2 + base_bit == prev) + { + prev++; + /* Hit end of untagged/tagged range */ + if (untagged != vlan_id_untagged(vlan_info,prev)) + { + /* put vlan into attributes */ + if (start == prev-1) + { + /* only 1 vid in range */ + vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN; + NLA_PUT(msg,IFLA_BRIDGE_VLAN_INFO,sizeof(vinfo),&vinfo); + } + else + { + /* end of untagged/tagged range */ + NLA_PUT(msg,IFLA_BRIDGE_VLAN_INFO,sizeof(vinfo),&vinfo); + + vinfo.flags = BRIDGE_VLAN_INFO_RANGE_END; + vinfo.flags |= untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0; + vinfo.vid = prev-1; + NLA_PUT(msg,IFLA_BRIDGE_VLAN_INFO,sizeof(vinfo),&vinfo); + } + /* start of new range */ + untagged = !untagged; + vinfo.flags = BRIDGE_VLAN_INFO_RANGE_BEGIN; + vinfo.flags |= untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0; + vinfo.vid = prev; + } + goto nxt; + } + } + else + done = 1; + + if (start >= 0) + { + if (done && k < RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN - 1) + break; + + if (vinfo.flags & BRIDGE_VLAN_INFO_RANGE_BEGIN && start != prev) + { + NLA_PUT(msg,IFLA_BRIDGE_VLAN_INFO,sizeof(vinfo),&vinfo); + + vinfo.flags = BRIDGE_VLAN_INFO_RANGE_END; + vinfo.flags |= untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0; + vinfo.vid = prev; + NLA_PUT(msg,IFLA_BRIDGE_VLAN_INFO,sizeof(vinfo),&vinfo); + } + else if (start == prev) + { + vinfo.flags = untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0; + vinfo.vid = start; + NLA_PUT(msg,IFLA_BRIDGE_VLAN_INFO,sizeof(vinfo),&vinfo); + } + + if (done) + break; + } + if (j > 0) + { + start = prev = j - 1 + base_bit; + untagged = vlan_id_untagged(vlan_info,start); + vinfo.flags = BRIDGE_VLAN_INFO_RANGE_BEGIN; + vinfo.flags |= untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0; + vinfo.vid = start; + } +nxt: + i = j; + } + } + + if (vlan_info->pvid != 0) + { + untagged = vlan_id_untagged(vlan_info,vlan_info->pvid); + vinfo.flags = BRIDGE_VLAN_INFO_PVID; + vinfo.flags |= untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0; + vinfo.vid = vlan_info->pvid; + NLA_PUT(msg,IFLA_BRIDGE_VLAN_INFO,sizeof(vinfo),&vinfo); + } + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int bridge_fill_mst(struct nl_msg *msg, struct nl_list_head *mst_list) { + struct nlattr *attr = NULL; + struct nlattr *entry_attr = NULL; + struct mst_state_entry *entry; + + if (nl_list_empty(mst_list)) + return 0; + + attr = nla_nest_start(msg, IFLA_BRIDGE_MST); + if (!attr) + goto err_out; + + nl_list_for_each_entry(entry, mst_list, list_node) { + entry_attr = nla_nest_start(msg, IFLA_BRIDGE_MST_ENTRY); + if (!entry_attr) + goto err_out_nest_cancel_attr; + + NLA_PUT_U16(msg, IFLA_BRIDGE_MST_ENTRY_MSTI, entry->msti); + NLA_PUT_U8(msg, IFLA_BRIDGE_MST_ENTRY_STATE, entry->state); + + nla_nest_end(msg, entry_attr); + } + + nla_nest_end(msg, attr); + + return 0; + +nla_put_failure: + nla_nest_cancel(msg, entry_attr); +err_out_nest_cancel_attr: + nla_nest_cancel(msg, attr); +err_out: + return -NLE_MSGSIZE; +} + +static int bridge_fill_af(struct rtnl_link *link, struct nl_msg *msg, + void *data) +{ + struct bridge_data *bd = data; + + if (bd->ce_mask & BRIDGE_ATTR_HWMODE) + { + NLA_PUT_U16(msg, IFLA_BRIDGE_MODE, bd->b_hwmode); + bd->b_config_mode = BRIDGE_FLAGS_SELF; + bd->ce_mask |= BRIDGE_ATTR_CONFIG_MODE; + } + + if (bd->ce_mask & BRIDGE_ATTR_MST) + { + if (bridge_fill_mst(msg, &bd->mst_list) < 0) { + goto nla_put_failure; + } + } + + if (bd->ce_mask & BRIDGE_ATTR_CONFIG_MODE) + NLA_PUT_U16(msg, IFLA_BRIDGE_FLAGS, bd->b_config_mode); + + if (bd->ce_mask & BRIDGE_ATTR_PORT_VLAN) { + if (_nl_bridge_fill_vlan_info(msg, &bd->vlan_info)) { + goto nla_put_failure; + } + } + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int bridge_fill_pi(struct rtnl_link *link, struct nl_msg *msg, + void *data) +{ + struct bridge_data *bd = data; + + if (bd->ce_mask & BRIDGE_ATTR_FLAGS) { + if (bd->b_flags_mask & RTNL_BRIDGE_BPDU_GUARD) { + NLA_PUT_U8(msg, IFLA_BRPORT_GUARD, + !!(bd->b_flags & RTNL_BRIDGE_BPDU_GUARD)); + } + if (bd->b_flags_mask & RTNL_BRIDGE_HAIRPIN_MODE) { + NLA_PUT_U8(msg, IFLA_BRPORT_MODE, + !!(bd->b_flags & RTNL_BRIDGE_HAIRPIN_MODE)); + } + if (bd->b_flags_mask & RTNL_BRIDGE_FAST_LEAVE) { + NLA_PUT_U8(msg, IFLA_BRPORT_FAST_LEAVE, + !!(bd->b_flags & RTNL_BRIDGE_FAST_LEAVE)); + } + if (bd->b_flags_mask & RTNL_BRIDGE_ROOT_BLOCK) { + NLA_PUT_U8(msg, IFLA_BRPORT_PROTECT, + !!(bd->b_flags & RTNL_BRIDGE_ROOT_BLOCK)); + } + if (bd->b_flags_mask & RTNL_BRIDGE_UNICAST_FLOOD) { + NLA_PUT_U8(msg, IFLA_BRPORT_UNICAST_FLOOD, + !!(bd->b_flags & RTNL_BRIDGE_UNICAST_FLOOD)); + } + if (bd->b_flags_mask & RTNL_BRIDGE_LEARNING) { + NLA_PUT_U8(msg, IFLA_BRPORT_LEARNING, + !!(bd->b_flags & RTNL_BRIDGE_LEARNING)); + } + if (bd->b_flags_mask & RTNL_BRIDGE_LEARNING_SYNC) { + NLA_PUT_U8(msg, IFLA_BRPORT_LEARNING_SYNC, + !!(bd->b_flags & RTNL_BRIDGE_LEARNING_SYNC)); + } + if (bd->b_flags_mask & RTNL_BRIDGE_PROXYARP) { + NLA_PUT_U8(msg, IFLA_BRPORT_PROXYARP, + !!(bd->b_flags & RTNL_BRIDGE_PROXYARP)); + } + if (bd->b_flags_mask & RTNL_BRIDGE_PROXYARP_WIFI) { + NLA_PUT_U8(msg, IFLA_BRPORT_PROXYARP_WIFI, + !!(bd->b_flags & RTNL_BRIDGE_PROXYARP_WIFI)); + } + if (bd->b_flags_mask & RTNL_BRIDGE_MCAST_FLOOD) { + NLA_PUT_U8(msg, IFLA_BRPORT_MCAST_FLOOD, + !!(bd->b_flags & RTNL_BRIDGE_MCAST_FLOOD)); + } + if (bd->b_flags_mask & RTNL_BRIDGE_MCAST_TO_UCAST) { + NLA_PUT_U8(msg, IFLA_BRPORT_MCAST_TO_UCAST, + !!(bd->b_flags & + RTNL_BRIDGE_MCAST_TO_UCAST)); + } + if (bd->b_flags_mask & RTNL_BRIDGE_VLAN_TUNNEL) { + NLA_PUT_U8(msg, IFLA_BRPORT_VLAN_TUNNEL, + !!(bd->b_flags & RTNL_BRIDGE_VLAN_TUNNEL)); + } + if (bd->b_flags_mask & RTNL_BRIDGE_BCAST_FLOOD) { + NLA_PUT_U8(msg, IFLA_BRPORT_BCAST_FLOOD, + !!(bd->b_flags & RTNL_BRIDGE_BCAST_FLOOD)); + } + if (bd->b_flags_mask & RTNL_BRIDGE_NEIGH_SUPPRESS) { + NLA_PUT_U8(msg, IFLA_BRPORT_NEIGH_SUPPRESS, + !!(bd->b_flags & + RTNL_BRIDGE_NEIGH_SUPPRESS)); + } + if (bd->b_flags_mask & RTNL_BRIDGE_ISOLATED) { + NLA_PUT_U8(msg, IFLA_BRPORT_ISOLATED, + !!(bd->b_flags & RTNL_BRIDGE_ISOLATED)); + } + if (bd->b_flags_mask & RTNL_BRIDGE_LOCKED) { + NLA_PUT_U8(msg, IFLA_BRPORT_LOCKED, + !!(bd->b_flags & RTNL_BRIDGE_LOCKED)); + } + if (bd->b_flags_mask & RTNL_BRIDGE_MAB) { + NLA_PUT_U8(msg, IFLA_BRPORT_MAB, + !!(bd->b_flags & RTNL_BRIDGE_MAB)); + } + if (bd->b_flags_mask & RTNL_BRIDGE_NEIGH_VLAN_SUPPRESS) { + NLA_PUT_U8(msg, IFLA_BRPORT_NEIGH_VLAN_SUPPRESS, + !!(bd->b_flags & + RTNL_BRIDGE_NEIGH_VLAN_SUPPRESS)); + } + } + + if (bd->ce_mask & BRIDGE_ATTR_COST) + NLA_PUT_U32(msg, IFLA_BRPORT_COST, bd->b_cost); + + if (bd->ce_mask & BRIDGE_ATTR_PRIORITY) + NLA_PUT_U16(msg, IFLA_BRPORT_PRIORITY, bd->b_priority); + + if (bd->ce_mask & BRIDGE_ATTR_PORT_STATE) + NLA_PUT_U8(msg, IFLA_BRPORT_STATE, bd->b_port_state); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int bridge_override_rtm(struct rtnl_link *link) { + struct bridge_data *bd; + + if (!rtnl_link_is_bridge(link)) + return 0; + + bd = bridge_data(link); + + if (bd->ce_mask & (BRIDGE_ATTR_FLAGS | BRIDGE_ATTR_MST)) + return 1; + + return 0; +} + +static int bridge_get_af(struct nl_msg *msg, uint32_t *ext_filter_mask) +{ + *ext_filter_mask |= RTEXT_FILTER_BRVLAN | RTEXT_FILTER_MST; + return 0; +} + +static void dump_bitmap(struct nl_dump_params *p, const uint32_t *b) +{ + int i = -1, j, k; + int start = -1, prev = -1; + int done, found = 0; + + for (k = 0; k < RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN; k++) { + int base_bit; + uint32_t a = b[k]; + + base_bit = k * 32; + i = -1; + done = 0; + while (!done) { + j = find_next_bit(i, a); + if (j > 0) { + /* first hit of any bit */ + if (start < 0 && prev < 0) { + start = prev = j - 1 + base_bit; + goto next; + } + /* this bit is a continuation of prior bits */ + if (j - 2 + base_bit == prev) { + prev++; + goto next; + } + } else + done = 1; + + if (start >= 0) { + found++; + if (done && k < RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN - 1) + break; + + nl_dump(p, " %d", start); + if (start != prev) + nl_dump(p, "-%d", prev); + + if (done) + break; + } + if (j > 0) + start = prev = j - 1 + base_bit; +next: + i = j; + } + } + if (!found) + nl_dump(p, " "); + + return; +} + +static void rtnl_link_bridge_dump_vlans(struct nl_dump_params *p, + struct bridge_data *bd) +{ + nl_dump(p, "pvid %u", bd->vlan_info.pvid); + + nl_dump(p, " all vlans:"); + dump_bitmap(p, bd->vlan_info.vlan_bitmap); + + nl_dump(p, " untagged vlans:"); + dump_bitmap(p, bd->vlan_info.untagged_bitmap); +} + +static const char *const br_port_state_names[] = { + [BR_STATE_DISABLED] = "disabled", + [BR_STATE_LISTENING] = "listening", + [BR_STATE_LEARNING] = "learning", + [BR_STATE_FORWARDING] = "forwarding", + [BR_STATE_BLOCKING] = "blocking", +}; + +static const char *stp_state2str(uint8_t state) { + if (state > BR_STATE_BLOCKING) + return "unknown"; + + return br_port_state_names[state]; +} + +static void bridge_dump_details(struct rtnl_link *link, + struct nl_dump_params *p, void *data) +{ + struct bridge_data *bd = data; + + nl_dump_line(p, " bridge: "); + + if (bd->ce_mask & BRIDGE_ATTR_PORT_STATE) + nl_dump(p, "port-state %u ", bd->b_port_state); + + if (bd->ce_mask & BRIDGE_ATTR_PRIORITY) + nl_dump(p, "prio %u ", bd->b_priority); + + if (bd->ce_mask & BRIDGE_ATTR_COST) + nl_dump(p, "cost %u ", bd->b_cost); + + if (bd->ce_mask & BRIDGE_ATTR_HWMODE) { + char hbuf[32]; + + rtnl_link_bridge_hwmode2str(bd->b_hwmode, hbuf, sizeof(hbuf)); + nl_dump(p, "hwmode %s", hbuf); + } + + if (bd->ce_mask & BRIDGE_ATTR_PORT_VLAN) + rtnl_link_bridge_dump_vlans(p, bd); + + if (bd->ce_mask & BRIDGE_ATTR_FLAGS) { + char buf[256]; + + rtnl_link_bridge_flags2str(bd->b_flags & bd->b_flags_mask, + buf, sizeof(buf)); + nl_dump(p, "%s", buf); + } + + nl_dump(p, "\n"); + + if (bd->ce_mask & BRIDGE_ATTR_MST && !nl_list_empty(&bd->mst_list)) { + struct mst_state_entry *entry; + + nl_dump_line(p, " mst:\n"); + + nl_list_for_each_entry(entry, &bd->mst_list, list_node) { + nl_dump_line(p, " instance %u: %s\n", + entry->msti, stp_state2str(entry->state)); + } + } +} + +static bool mst_state_entries_are_equal(const struct mst_state_entry *a, + const struct mst_state_entry *b) +{ + return a->msti == b->msti && a->state == b->state; +} + +/** + * Compares two MST lists for equality + * @arg list_1 The first list + * @arg list_2 The second list + * + * This comparison checks that the MST lists have the same entries and that they + * are in the same order (although the ordering is not significant to the + * kernel). + * + * @return true if the lists are equal, false otherwise + */ +static bool msts_lists_are_equal(struct nl_list_head *list_1, + struct nl_list_head *list_2) +{ + struct mst_state_entry *entry_a; + struct mst_state_entry *entry_b; + + entry_a = + nl_list_entry(list_1->next, struct mst_state_entry, list_node); + entry_b = + nl_list_entry(list_2->next, struct mst_state_entry, list_node); + + /* while both lists have items left to process */ + while (&entry_a->list_node != list_1 && &entry_b->list_node != list_2) { + if (!mst_state_entries_are_equal(entry_a, entry_b)) { + return false; + } + + entry_a = nl_list_entry(entry_a->list_node.next, + struct mst_state_entry, list_node); + entry_b = nl_list_entry(entry_b->list_node.next, + struct mst_state_entry, list_node); + } + + /* return true only if both lists were the same length */ + return &entry_a->list_node == list_1 && &entry_b->list_node == list_2; +} + +static int bridge_compare(struct rtnl_link *_a, struct rtnl_link *_b, + int family, uint32_t attrs, int flags) +{ + struct bridge_data *a = bridge_data(_a); + struct bridge_data *b = bridge_data(_b); + int diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(BRIDGE_ATTR_PORT_STATE, + a->b_port_state != b->b_port_state); + diff |= _DIFF(BRIDGE_ATTR_PRIORITY, a->b_priority != b->b_priority); + diff |= _DIFF(BRIDGE_ATTR_COST, a->b_cost != b->b_cost); + diff |= _DIFF(BRIDGE_ATTR_PORT_VLAN, + memcmp(&a->vlan_info, &b->vlan_info, + sizeof(struct rtnl_link_bridge_vlan))); + diff |= _DIFF(BRIDGE_ATTR_HWMODE, a->b_hwmode != b->b_hwmode); + diff |= _DIFF(BRIDGE_ATTR_CONFIG_MODE, a->b_config_mode != b->b_config_mode); + diff |= _DIFF(BRIDGE_ATTR_MST, + !msts_lists_are_equal(&a->mst_list, &b->mst_list)); + + if (flags & LOOSE_COMPARISON) + diff |= _DIFF(BRIDGE_ATTR_FLAGS, + (a->b_flags ^ b->b_flags) & b->b_flags_mask); + else + diff |= _DIFF(BRIDGE_ATTR_FLAGS, a->b_flags != b->b_flags); +#undef _DIFF + + return diff; +} +/** @endcond */ + +/** + * Allocate link object of type bridge + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_bridge_alloc(void) +{ + struct rtnl_link *link; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if (rtnl_link_set_type(link, "bridge") < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Create a new kernel bridge device + * @arg sk netlink socket + * @arg name name of the bridge device or NULL + * + * Creates a new bridge device in the kernel. If no name is + * provided, the kernel will automatically pick a name of the + * form "type%d" (e.g. bridge0, vlan1, etc.) + * + * @return 0 on success or a negative error code +*/ +int rtnl_link_bridge_add(struct nl_sock *sk, const char *name) +{ + int err; + struct rtnl_link *link; + + if (!(link = rtnl_link_bridge_alloc())) + return -NLE_NOMEM; + + if(name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + rtnl_link_put(link); + + return err; +} + +/** + * Check if a link is a bridge + * @arg link Link object + * + * @return 1 if the link is a bridge, 0 otherwise. + */ +int rtnl_link_is_bridge(struct rtnl_link *link) +{ + return link->l_family == AF_BRIDGE && + link->l_af_ops == &bridge_ops; +} + +/** + * Check if bridge has extended information + * @arg link Link object of type bridge + * + * Checks if the bridge object has been constructed based on + * information that is only available in newer kernels. This + * affectes the following functions: + * - rtnl_link_bridge_get_cost() + * - rtnl_link_bridge_get_priority() + * - rtnl_link_bridge_get_flags() + * + * @return 1 if extended information is available, otherwise 0 is returned. + */ +int rtnl_link_bridge_has_ext_info(struct rtnl_link *link) +{ + struct bridge_data *bd; + + if (!rtnl_link_is_bridge(link)) + return 0; + + bd = bridge_data(link); + return !!(bd->b_priv_flags & PRIV_FLAG_NEW_ATTRS); +} + +/** + * Set Spanning Tree Protocol (STP) port state + * @arg link Link object of type bridge + * @arg state New STP port state + * + * The value of state must be one of the following: + * - BR_STATE_DISABLED + * - BR_STATE_LISTENING + * - BR_STATE_LEARNING + * - BR_STATE_FORWARDING + * - BR_STATE_BLOCKING + * + * @see rtnl_link_bridge_get_port_state() + * + * @return 0 on success or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + * @retval -NLE_INVAL Invalid state value (0..BR_STATE_BLOCKING) + */ +int rtnl_link_bridge_set_port_state(struct rtnl_link *link, uint8_t state) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + if (state > BR_STATE_BLOCKING) + return -NLE_INVAL; + + bd->b_port_state = state; + bd->ce_mask |= BRIDGE_ATTR_PORT_STATE; + + return 0; +} + +/** + * Get Spanning Tree Protocol (STP) port state + * @arg link Link object of type bridge + * + * @see rtnl_link_bridge_set_port_state() + * + * @return The STP port state or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_get_port_state(struct rtnl_link *link) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + return bd->b_port_state; +} + +/** + * Set priority + * @arg link Link object of type bridge + * @arg prio Bridge priority + * + * @see rtnl_link_bridge_get_priority() + * + * @return 0 on success or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_set_priority(struct rtnl_link *link, uint16_t prio) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + bd->b_priority = prio; + bd->ce_mask |= BRIDGE_ATTR_PRIORITY; + + return 0; +} + +/** + * Get priority + * @arg link Link object of type bridge + * + * @see rtnl_link_bridge_set_priority() + * + * @return 0 on success or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_get_priority(struct rtnl_link *link) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + return bd->b_priority; +} + +/** + * Set Spanning Tree Protocol (STP) path cost + * @arg link Link object of type bridge + * @arg cost New STP path cost value + * + * @see rtnl_link_bridge_get_cost() + * + * @return The bridge priority or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_set_cost(struct rtnl_link *link, uint32_t cost) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + bd->b_cost = cost; + bd->ce_mask |= BRIDGE_ATTR_COST; + + return 0; +} + +/** + * Get Spanning Tree Protocol (STP) path cost + * @arg link Link object of type bridge + * @arg cost Pointer to store STP cost value + * + * @see rtnl_link_bridge_set_cost() + * + * @return 0 on success or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + * @retval -NLE_INVAL `cost` is not a valid pointer + */ +int rtnl_link_bridge_get_cost(struct rtnl_link *link, uint32_t *cost) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + if (!cost) + return -NLE_INVAL; + + *cost = bd->b_cost; + + return 0; +} + +/** + * Unset flags + * @arg link Link object of type bridge + * @arg flags Bridging flags to unset + * + * @see rtnl_link_bridge_set_flags() + * @see rtnl_link_bridge_get_flags() + * + * @return 0 on success or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_unset_flags(struct rtnl_link *link, unsigned int flags) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + bd->b_flags_mask |= flags; + bd->b_flags &= ~flags; + bd->ce_mask |= BRIDGE_ATTR_FLAGS; + + return 0; +} + +/** + * Set flags + * @arg link Link object of type bridge + * @arg flags Bridging flags to set + * + * Valid flags are: + * - RTNL_BRIDGE_HAIRPIN_MODE + * - RTNL_BRIDGE_BPDU_GUARD + * - RTNL_BRIDGE_ROOT_BLOCK + * - RTNL_BRIDGE_FAST_LEAVE + * - RTNL_BRIDGE_UNICAST_FLOOD + * - RTNL_BRIDGE_LEARNING + * - RTNL_BRIDGE_LEARNING_SYNC + * - RTNL_BRIDGE_PROXYARP + * - RTNL_BRIDGE_PROXYARP_WIFI + * - RTNL_BRIDGE_MCAST_FLOOD + * - RTNL_BRIDGE_MCAST_TO_UCAST + * - RTNL_BRIDGE_VLAN_TUNNEL + * - RTNL_BRIDGE_BCAST_FLOOD + * - RTNL_BRIDGE_NEIGH_SUPPRESS + * - RTNL_BRIDGE_ISOLATED + * - RTNL_BRIDGE_LOCKED + * - RTNL_BRIDGE_MAB + * - RTNL_BRIDGE_NEIGH_VLAN_SUPPRESS + * + * @see rtnl_link_bridge_unset_flags() + * @see rtnl_link_bridge_get_flags() + * + * @return 0 on success or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_set_flags(struct rtnl_link *link, unsigned int flags) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + bd->b_flags_mask |= flags; + bd->b_flags |= flags; + bd->ce_mask |= BRIDGE_ATTR_FLAGS; + + return 0; +} + +/** + * Get flags + * @arg link Link object of type bridge + * + * @see rtnl_link_bridge_set_flags() + * @see rtnl_link_bridge_unset_flags() + * + * @return Flags or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_get_flags(struct rtnl_link *link) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + return bd->b_flags; +} + +/** + * Set link change type to self + * @arg link Link Object of type bridge + * + * This will set the bridge change flag to self, meaning that changes to + * be applied with this link object will be applied directly to the physical + * device in a bridge instead of the virtual device. + * + * @return 0 on success or negative error code + * @return -NLE_OPNOTSUP Link is not a bridge + */ +int rtnl_link_bridge_set_self(struct rtnl_link *link) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + bd->b_config_mode = BRIDGE_FLAGS_SELF; + bd->ce_mask |= BRIDGE_ATTR_CONFIG_MODE; + + return 0; +} + +/** + * Set link change type to master + * @arg link Link Object of type bridge + * + * This will set the bridge change flag to master, meaning that changes to + * be applied with this link object will be applied directly to the virtual + * device in a bridge instead of the physical device. + * + * @return 0 on success or negative error code + * @return -NLE_OPNOTSUP Link is not a bridge + */ +int rtnl_link_bridge_set_master(struct rtnl_link *link) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + bd->b_config_mode = BRIDGE_FLAGS_MASTER; + bd->ce_mask |= BRIDGE_ATTR_CONFIG_MODE; + + return 0; +} + +/** + * Get hardware mode + * @arg link Link object of type bridge + * @arg hwmode Output argument. + * + * @see rtnl_link_bridge_set_hwmode() + * + * @return 0 if hardware mode is present and returned in hwmode + * @return -NLE_NOATTR if hardware mode is not present + * @return -NLE_OPNOTSUP Link is not a bridge + */ +int rtnl_link_bridge_get_hwmode(struct rtnl_link *link, uint16_t *hwmode) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + if (!(bd->ce_mask & BRIDGE_ATTR_HWMODE)) + return -NLE_NOATTR; + + *hwmode = bd->b_hwmode; + return 0; +} + +/** + * Set hardware mode + * @arg link Link object of type bridge + * @arg hwmode Hardware mode to set on link + * + * This will set the hardware mode of a link when it supports hardware + * offloads for bridging. + * @see rtnl_link_bridge_get_hwmode() + * + * Valid modes are: + * - RTNL_BRIDGE_HWMODE_VEB + * - RTNL_BRIDGE_HWMODE_VEPA + * + * When setting hardware mode, the change type will be set to self. + * @see rtnl_link_bridge_set_self() + * + * @return 0 on success or negative error code + * @return -NLE_OPNOTSUP Link is not a bridge + * @return -NLE_INVAL when specified hwmode is unsupported. + */ +int rtnl_link_bridge_set_hwmode(struct rtnl_link *link, uint16_t hwmode) +{ + int err; + struct bridge_data *bd = bridge_data(link); + + if (hwmode > RTNL_BRIDGE_HWMODE_MAX) + return -NLE_INVAL; + + if ((err = rtnl_link_bridge_set_self(link)) < 0) + return err; + + bd->b_hwmode = hwmode; + bd->ce_mask |= BRIDGE_ATTR_HWMODE; + + return 0; +} + + +static const struct trans_tbl bridge_flags[] = { + __ADD(RTNL_BRIDGE_HAIRPIN_MODE, hairpin_mode), + __ADD(RTNL_BRIDGE_BPDU_GUARD, bpdu_guard), + __ADD(RTNL_BRIDGE_ROOT_BLOCK, root_block), + __ADD(RTNL_BRIDGE_FAST_LEAVE, fast_leave), + __ADD(RTNL_BRIDGE_UNICAST_FLOOD, flood), + __ADD(RTNL_BRIDGE_LEARNING, learning), + __ADD(RTNL_BRIDGE_LEARNING_SYNC, learning_sync), + __ADD(RTNL_BRIDGE_PROXYARP, proxy_arp), + __ADD(RTNL_BRIDGE_PROXYARP_WIFI, proxy_arp_wifi), + __ADD(RTNL_BRIDGE_MCAST_FLOOD, mcast_flood), + __ADD(RTNL_BRIDGE_MCAST_TO_UCAST, mcast_to_unicast), + __ADD(RTNL_BRIDGE_VLAN_TUNNEL, vlan_tunnel), + __ADD(RTNL_BRIDGE_BCAST_FLOOD, bcast_flood), + __ADD(RTNL_BRIDGE_NEIGH_SUPPRESS, neigh_suppress), + __ADD(RTNL_BRIDGE_ISOLATED, isolated), + __ADD(RTNL_BRIDGE_LOCKED, locked), + __ADD(RTNL_BRIDGE_MAB, mab), + __ADD(RTNL_BRIDGE_NEIGH_VLAN_SUPPRESS, neigh_vlan_suppress), +}; + +/** + * @name Flag Translation + * @{ + */ + +char *rtnl_link_bridge_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, bridge_flags, ARRAY_SIZE(bridge_flags)); +} + +int rtnl_link_bridge_str2flags(const char *name) +{ + return __str2flags(name, bridge_flags, ARRAY_SIZE(bridge_flags)); +} + +/** @} */ + +static const struct trans_tbl port_states[] = { + __ADD(BR_STATE_DISABLED, disabled), + __ADD(BR_STATE_LISTENING, listening), + __ADD(BR_STATE_LEARNING, learning), + __ADD(BR_STATE_FORWARDING, forwarding), + __ADD(BR_STATE_BLOCKING, blocking), +}; + +/** + * @name Port State Translation + * @{ + */ + +char *rtnl_link_bridge_portstate2str(int st, char *buf, size_t len) +{ + return __type2str(st, buf, len, port_states, ARRAY_SIZE(port_states)); +} + +int rtnl_link_bridge_str2portstate(const char *name) +{ + return __str2type(name, port_states, ARRAY_SIZE(port_states)); +} + +/** @} */ + +static const struct trans_tbl hw_modes[] = { + __ADD(RTNL_BRIDGE_HWMODE_VEB, veb), + __ADD(RTNL_BRIDGE_HWMODE_VEPA, vepa), + __ADD(RTNL_BRIDGE_HWMODE_UNDEF, undef), +}; + +/** + * @name Hardware Mode Translation + * @{ + */ + +char *rtnl_link_bridge_hwmode2str(uint16_t st, char *buf, size_t len) { + return __type2str(st, buf, len, hw_modes, ARRAY_SIZE(hw_modes)); +} + +uint16_t rtnl_link_bridge_str2hwmode(const char *name) +{ + return __str2type(name, hw_modes, ARRAY_SIZE(hw_modes)); +} + +/** @} */ + +/** + * Enable the ability to set vlan info + * @arg link Link object of type bridge + * + * @return 0 on success or negative error code + * @return -NLE_OPNOTSUP Link is not a bridge + */ +int rtnl_link_bridge_enable_vlan(struct rtnl_link *link) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + bd->ce_mask |= BRIDGE_ATTR_PORT_VLAN; + + return 0; +} + +/** + * @name Quality of Service + * @{ + */ + +/** + * Set port vlan membership range + * @arg link Link object of type bridge + * @arg start Start of membership range. + * @arg end End of membership range. + * @arg untagged Set membership range to be untagged. + * + * This will set the vlan membership range for a bridge port. + * This will unset the untagged membership if untagged is false. + * Supported range is 1-4094 + * + * @return 0 on success or negative error code + * @return -NLE_NOATTR if port vlan attribute not present + * @return -NLE_OPNOTSUP Link is not a bridge + * @return -NLE_INVAL range is not in supported range. + */ +int rtnl_link_bridge_set_port_vlan_map_range (struct rtnl_link *link, uint16_t start, uint16_t end, int untagged) +{ + struct rtnl_link_bridge_vlan * vinfo; + + IS_BRIDGE_LINK_ASSERT(link); + + vinfo = rtnl_link_bridge_get_port_vlan(link); + + if (!vinfo) + return -NLE_NOATTR; + + if (start == 0 || start > end || end >= VLAN_VID_MASK) + return -NLE_INVAL; + + for (uint16_t i = start; i <= end; i++) + { + set_bit(i,vinfo->vlan_bitmap); + if (untagged) { + set_bit(i,vinfo->untagged_bitmap); + } else { + unset_bit(i,vinfo->untagged_bitmap); + } + } + return 0; +} + +/** + * Unset port vlan membership range + * @arg link Link object of type bridge + * @arg start Start of membership range. + * @arg end End of membership range. + * + * This will unset the vlan membership range for a bridge port + * for both tagged and untagged membership. + * Supported range is 1-4094 + * + * @return 0 on success or negative error code + * @return -NLE_NOATTR if port vlan attribute not present + * @return -NLE_OPNOTSUP Link is not a bridge + * @return -NLE_INVAL range is not in supported range. + */ +int rtnl_link_bridge_unset_port_vlan_map_range (struct rtnl_link *link, uint16_t start, uint16_t end) +{ + struct rtnl_link_bridge_vlan * vinfo; + + IS_BRIDGE_LINK_ASSERT(link); + + vinfo = rtnl_link_bridge_get_port_vlan(link); + + if (!vinfo) + return -NLE_NOATTR; + + if (start == 0 || start > end || end >= VLAN_VID_MASK) + return -NLE_INVAL; + + for (uint16_t i = start; i <= end; i++) + { + unset_bit(i,vinfo->vlan_bitmap); + unset_bit(i,vinfo->untagged_bitmap); + } + return 0; +} + +/** + * Set port primary vlan id + * @arg link Link object of type bridge + * @arg pvid PVID to set. + * @arg untagged Set vlan id to be untagged. + * + * This will set the primary vlan id for a bridge port. + * Supported range is 0-4094, Setting pvid to 0 will unset it. + * You will most likely want to set/unset pvid in the vlan map. + * @see rtnl_link_bridge_set_port_vlan_map_range() + * @see rtnl_link_bridge_unset_port_vlan_map_range() + * + * @return 0 on success or negative error code + * @return -NLE_NOATTR if port vlan attribute not present + * @return -NLE_OPNOTSUP Link is not a bridge + * @return -NLE_INVAL PVID is above supported range. + */ +int rtnl_link_bridge_set_port_vlan_pvid (struct rtnl_link *link, uint16_t pvid) +{ + struct rtnl_link_bridge_vlan * vinfo; + + IS_BRIDGE_LINK_ASSERT(link); + + vinfo = rtnl_link_bridge_get_port_vlan(link); + + if (!vinfo) + return -NLE_NOATTR; + + if (pvid >= VLAN_VID_MASK) + return -NLE_INVAL; + + vinfo->pvid = pvid; + + return 0; +} + +/** @} */ + +int rtnl_link_bridge_pvid(struct rtnl_link *link) +{ + struct bridge_data *bd; + + IS_BRIDGE_LINK_ASSERT(link); + + bd = link->l_af_data[AF_BRIDGE]; + if (bd->ce_mask & BRIDGE_ATTR_PORT_VLAN) + return (int) bd->vlan_info.pvid; + + return -EINVAL; +} + +int rtnl_link_bridge_has_vlan(struct rtnl_link *link) +{ + struct bridge_data *bd; + int i; + + IS_BRIDGE_LINK_ASSERT(link); + + bd = link->l_af_data[AF_BRIDGE]; + if (bd->ce_mask & BRIDGE_ATTR_PORT_VLAN) { + if (bd->vlan_info.pvid) + return 1; + + for (i = 0; i < RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN; ++i) { + if (bd->vlan_info.vlan_bitmap[i] || + bd->vlan_info.untagged_bitmap[i]) + return 1; + } + } + return 0; +} + +struct rtnl_link_bridge_vlan *rtnl_link_bridge_get_port_vlan(struct rtnl_link *link) +{ + struct bridge_data *data; + + if (!rtnl_link_is_bridge(link)) + return NULL; + + data = link->l_af_data[AF_BRIDGE]; + if (data && (data->ce_mask & BRIDGE_ATTR_PORT_VLAN)) + return &data->vlan_info; + + return NULL; +} + +/** + * Set the Multiple Spanning Tree (MST) port state for a given MST instance + * @arg link Link object of type bridge + * @arg instance MST instance number + * @arg state Port state to set (BR_STATE_*) + * + * @return 0 on success or a negative error code + * @return -NLE_INVAL link is NULL + * @return -NLE_OPNOTSUP Link is not a bridge + * @return -NLE_NOMEM Memory allocation failed + */ +int rtnl_link_bridge_set_mst_port_state(struct rtnl_link *link, + uint16_t instance, uint8_t state) +{ + struct bridge_data *bd; + struct mst_state_entry *existing_entry; + struct mst_state_entry *new_entry; + + if (link == NULL) + return -NLE_INVAL; + + IS_BRIDGE_LINK_ASSERT(link); + + bd = bridge_data(link); + if (bd == NULL) + return -NLE_OPNOTSUPP; + + existing_entry = find_mst_state_entry(bd, instance); + + if (existing_entry != NULL) { + existing_entry->state = state; + return 0; + } + + new_entry = mst_state_entry_create(instance, state); + if (new_entry == NULL) + return -NLE_NOMEM; + + nl_list_add_tail(&new_entry->list_node, &bd->mst_list); + bd->ce_mask |= BRIDGE_ATTR_MST; + + return 0; +} + +/** + * Get the Multiple Spanning Tree (MST) port state for a given MST instance + * @arg link Link object of type bridge + * @arg instance MST instance number + * + * @return The state (BR_STATE_*) on success, or a negative error code + * @return -NLE_INVAL link is NULL + * @return -NLE_OPNOTSUP Link is not a bridge + * @return -NLE_OBJ_NOTFOUND MST instance not found + */ +int rtnl_link_bridge_get_mst_port_state(struct rtnl_link *link, + uint16_t instance) +{ + struct bridge_data *bd; + struct mst_state_entry *entry; + + if (link == NULL) + return -NLE_INVAL; + + IS_BRIDGE_LINK_ASSERT(link); + + bd = bridge_data(link); + if (bd == NULL) + return -NLE_OPNOTSUPP; + + entry = find_mst_state_entry(bd, instance); + + if (entry == NULL) + return -NLE_OBJ_NOTFOUND; + + return entry->state; +} + +/** + * Delete the Multiple Spanning Tree (MST) port state for a given MST instance + * @arg link Link object of type bridge + * @arg instance MST instance number + * + * @return 0 on success or a negative error code + * @return -NLE_INVAL link is NULL + * @return -NLE_OPNOTSUP Link is not a bridge + * @return -NLE_OBJ_NOTFOUND MST instance not found + */ +int rtnl_link_bridge_del_mst_port_state(struct rtnl_link *link, + uint16_t instance) +{ + struct bridge_data *bd; + struct mst_state_entry *entry; + + if (link == NULL) + return -NLE_INVAL; + + IS_BRIDGE_LINK_ASSERT(link); + + bd = bridge_data(link); + if (bd == NULL) + return -NLE_OPNOTSUPP; + + entry = find_mst_state_entry(bd, instance); + + if (entry == NULL) + return -NLE_OBJ_NOTFOUND; + + mst_state_entry_del(entry); + + if (nl_list_empty(&bd->mst_list)) + bd->ce_mask &= ~BRIDGE_ATTR_MST; + + return 0; +} + +/** + * Delete all Multiple Spanning Tree (MST) port state information + * @arg link Link object of type bridge + * + * @return 0 on success or a negative error code + * @return -NLE_INVAL link is NULL + * @return -NLE_OPNOTSUP Link is not a bridge + */ +int rtnl_link_bridge_clear_mst_port_state_info(struct rtnl_link *link) +{ + struct bridge_data *bd; + + if (link == NULL) + return -NLE_INVAL; + + IS_BRIDGE_LINK_ASSERT(link); + + bd = bridge_data(link); + if (bd == NULL) + return -NLE_OPNOTSUPP; + + mst_list_clear(&bd->mst_list); + bd->ce_mask &= ~BRIDGE_ATTR_MST; + + return 0; +} + +/** + * Iterate over all Multiple Spanning Tree (MST) port state entries + * @arg link Link object of type bridge + * @arg cb Callback function + * @arg arg User provided data argument to pass to the callback + * function + * + * The callback function is called for each MST entry. It is passed the MST + * instance ID, state (BR_STATE_*), and an optional user provided data argument. + * MST entries should not be added or removed by the callback function. + * + * @return 0 on success or a negative error code + * @return -NLE_INVAL link or cb is NULL + * @return -NLE_OPNOTSUP Link is not a bridge + */ +int rtnl_link_bridge_foreach_mst_entry( + struct rtnl_link *link, + void (*cb)(uint16_t instance, uint8_t state, void *arg), void *arg) +{ + struct bridge_data *bd; + struct mst_state_entry *entry; + + if (link == NULL || cb == NULL) + return -NLE_INVAL; + + IS_BRIDGE_LINK_ASSERT(link); + + bd = bridge_data(link); + if (bd == NULL) + return -NLE_OPNOTSUPP; + + nl_list_for_each_entry(entry, &bd->mst_list, list_node) { + cb(entry->msti, entry->state, arg); + } + + return 0; +} + +static struct rtnl_link_af_ops bridge_ops = { + .ao_family = AF_BRIDGE, + .ao_alloc = &bridge_alloc, + .ao_clone = &bridge_clone, + .ao_free = &bridge_free, + .ao_parse_protinfo = &bridge_parse_protinfo, + .ao_dump[NL_DUMP_DETAILS] = &bridge_dump_details, + .ao_compare = &bridge_compare, + .ao_parse_af_full = &bridge_parse_af_full, + .ao_get_af = &bridge_get_af, + .ao_fill_af = &bridge_fill_af, + .ao_fill_pi = &bridge_fill_pi, + .ao_fill_pi_flags = NLA_F_NESTED, + .ao_override_rtm = &bridge_override_rtm, + .ao_fill_af_no_nest = 1, +}; + +static void _nl_init bridge_init(void) +{ + rtnl_link_af_register(&bridge_ops); +} + +static void _nl_exit bridge_exit(void) +{ + rtnl_link_af_unregister(&bridge_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/bridge_info.c b/libnl/lib/route/link/bridge_info.c new file mode 100644 index 0000000..c078de5 --- /dev/null +++ b/libnl/lib/route/link/bridge_info.c @@ -0,0 +1,873 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2022 MaxLinear, Inc. + */ + +/** + * @ingroup link + * @defgroup bridge Bridging + * + * @details + * @{ + */ + +#include "nl-default.h" + +#include + +#include + +#include "nl-route.h" +#include "link-api.h" + +#define BRIDGE_ATTR_VLAN_FILTERING (1 << 0) +#define BRIDGE_ATTR_VLAN_PROTOCOL (1 << 1) +#define BRIDGE_ATTR_VLAN_STATS_ENABLED (1 << 2) +#define BRIDGE_ATTR_AGEING_TIME (1 << 3) +#define BRIDGE_ATTR_VLAN_DEFAULT_PVID (1 << 4) +#define BRIDGE_ATTR_NF_CALL_IPTABLES (1 << 5) +#define BRIDGE_ATTR_NF_CALL_IP6TABLES (1 << 6) +#define BRIDGE_ATTR_NF_CALL_ARPTABLES (1 << 7) +#define BRIDGE_ATTR_STP_STATE (1 << 8) +#define BRIDGE_ATTR_MCAST_ROUTER (1 << 9) +#define BRIDGE_ATTR_MCAST_SNOOPING (1 << 10) +#define BRIDGE_ATTR_BOOLOPT (1 << 11) + +struct bridge_info { + uint32_t ce_mask; /* to support attr macros */ + uint32_t b_ageing_time; + uint16_t b_vlan_protocol; + uint16_t b_vlan_default_pvid; + uint8_t b_vlan_filtering; + uint8_t b_vlan_stats_enabled; + uint8_t b_nf_call_iptables; + uint8_t b_nf_call_ip6tables; + uint8_t b_nf_call_arptables; + uint32_t b_stp_state; + uint8_t b_mcast_router; + uint8_t b_mcast_snooping; + struct br_boolopt_multi b_boolopts; +}; + +static const struct nla_policy bi_attrs_policy[IFLA_BR_MAX + 1] = { + [IFLA_BR_AGEING_TIME] = { .type = NLA_U32 }, + [IFLA_BR_VLAN_DEFAULT_PVID] = { .type = NLA_U16 }, + [IFLA_BR_VLAN_FILTERING] = { .type = NLA_U8 }, + [IFLA_BR_VLAN_PROTOCOL] = { .type = NLA_U16 }, + [IFLA_BR_VLAN_STATS_ENABLED] = { .type = NLA_U8 }, + [IFLA_BR_NF_CALL_IPTABLES] = { .type = NLA_U8 }, + [IFLA_BR_NF_CALL_IP6TABLES] = { .type = NLA_U8 }, + [IFLA_BR_NF_CALL_ARPTABLES] = { .type = NLA_U8 }, + [IFLA_BR_STP_STATE] = { .type = NLA_U32 }, + [IFLA_BR_MCAST_ROUTER] = { .type = NLA_U8 }, + [IFLA_BR_MCAST_SNOOPING] = { .type = NLA_U8 }, + [IFLA_BR_MULTI_BOOLOPT] = { .type = NLA_BINARY, + .minlen = sizeof(struct br_boolopt_multi), + .maxlen = sizeof(struct br_boolopt_multi) }, +}; + +static inline struct bridge_info *bridge_info(struct rtnl_link *link) +{ + return link->l_info; +} + +static int bridge_info_alloc(struct rtnl_link *link) +{ + struct bridge_info *bi; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*bi)); + else { + bi = calloc(1, sizeof(*bi)); + if (!bi) + return -NLE_NOMEM; + + link->l_info = bi; + } + + return 0; +} + +static int bridge_info_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_BR_MAX + 1]; + struct bridge_info *bi; + int err; + + NL_DBG(3, "Parsing Bridge link info\n"); + + if ((err = nla_parse_nested(tb, IFLA_BR_MAX, data, bi_attrs_policy)) < + 0) + return err; + + if ((err = bridge_info_alloc(link)) < 0) + return err; + + bi = link->l_info; + + if (tb[IFLA_BR_AGEING_TIME]) { + bi->b_ageing_time = nla_get_u32(tb[IFLA_BR_AGEING_TIME]); + bi->ce_mask |= BRIDGE_ATTR_AGEING_TIME; + } + + if (tb[IFLA_BR_VLAN_DEFAULT_PVID]) { + bi->b_vlan_default_pvid = + nla_get_u16(tb[IFLA_BR_VLAN_DEFAULT_PVID]); + bi->ce_mask |= BRIDGE_ATTR_VLAN_DEFAULT_PVID; + } + + if (tb[IFLA_BR_VLAN_FILTERING]) { + bi->b_vlan_filtering = nla_get_u8(tb[IFLA_BR_VLAN_FILTERING]); + bi->ce_mask |= BRIDGE_ATTR_VLAN_FILTERING; + } + + if (tb[IFLA_BR_VLAN_PROTOCOL]) { + bi->b_vlan_protocol = + ntohs(nla_get_u16(tb[IFLA_BR_VLAN_PROTOCOL])); + bi->ce_mask |= BRIDGE_ATTR_VLAN_PROTOCOL; + } + + if (tb[IFLA_BR_VLAN_STATS_ENABLED]) { + bi->b_vlan_stats_enabled = + nla_get_u8(tb[IFLA_BR_VLAN_STATS_ENABLED]); + bi->ce_mask |= BRIDGE_ATTR_VLAN_STATS_ENABLED; + } + + if (tb[IFLA_BR_NF_CALL_IPTABLES]) { + bi->b_nf_call_iptables = + nla_get_u8(tb[IFLA_BR_NF_CALL_IPTABLES]); + bi->ce_mask |= BRIDGE_ATTR_NF_CALL_IPTABLES; + } + + if (tb[IFLA_BR_NF_CALL_IP6TABLES]) { + bi->b_nf_call_ip6tables = + nla_get_u8(tb[IFLA_BR_NF_CALL_IP6TABLES]); + bi->ce_mask |= BRIDGE_ATTR_NF_CALL_IP6TABLES; + } + + if (tb[IFLA_BR_NF_CALL_ARPTABLES]) { + bi->b_nf_call_arptables = + nla_get_u8(tb[IFLA_BR_NF_CALL_ARPTABLES]); + bi->ce_mask |= BRIDGE_ATTR_NF_CALL_ARPTABLES; + } + + if (tb[IFLA_BR_STP_STATE]) { + bi->b_stp_state = nla_get_u32(tb[IFLA_BR_STP_STATE]); + bi->ce_mask |= BRIDGE_ATTR_STP_STATE; + } + + if (tb[IFLA_BR_MCAST_ROUTER]) { + bi->b_mcast_router = nla_get_u8(tb[IFLA_BR_MCAST_ROUTER]); + bi->ce_mask |= BRIDGE_ATTR_MCAST_ROUTER; + } + + if (tb[IFLA_BR_MCAST_SNOOPING]) { + bi->b_mcast_snooping = nla_get_u8(tb[IFLA_BR_MCAST_SNOOPING]); + bi->ce_mask |= BRIDGE_ATTR_MCAST_SNOOPING; + } + + if (tb[IFLA_BR_MULTI_BOOLOPT]) { + nla_memcpy(&bi->b_boolopts, tb[IFLA_BR_MULTI_BOOLOPT], + sizeof(bi->b_boolopts)); + bi->ce_mask |= BRIDGE_ATTR_BOOLOPT; + } + + return 0; +} + +static int bridge_info_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct bridge_info *bi = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + + if (bi->ce_mask & BRIDGE_ATTR_AGEING_TIME) + NLA_PUT_U32(msg, IFLA_BR_AGEING_TIME, bi->b_ageing_time); + + if (bi->ce_mask & BRIDGE_ATTR_VLAN_FILTERING) + NLA_PUT_U8(msg, IFLA_BR_VLAN_FILTERING, bi->b_vlan_filtering); + + if (bi->ce_mask & BRIDGE_ATTR_VLAN_DEFAULT_PVID) + NLA_PUT_U16(msg, IFLA_BR_VLAN_DEFAULT_PVID, + bi->b_vlan_default_pvid); + + if (bi->ce_mask & BRIDGE_ATTR_VLAN_PROTOCOL) + NLA_PUT_U16(msg, IFLA_BR_VLAN_PROTOCOL, + htons(bi->b_vlan_protocol)); + + if (bi->ce_mask & BRIDGE_ATTR_VLAN_STATS_ENABLED) + NLA_PUT_U8(msg, IFLA_BR_VLAN_STATS_ENABLED, + bi->b_vlan_stats_enabled); + + if (bi->ce_mask & BRIDGE_ATTR_NF_CALL_IPTABLES) + NLA_PUT_U8(msg, IFLA_BR_NF_CALL_IPTABLES, + bi->b_nf_call_iptables); + + if (bi->ce_mask & BRIDGE_ATTR_NF_CALL_IP6TABLES) + NLA_PUT_U8(msg, IFLA_BR_NF_CALL_IP6TABLES, + bi->b_nf_call_ip6tables); + + if (bi->ce_mask & BRIDGE_ATTR_NF_CALL_ARPTABLES) + NLA_PUT_U8(msg, IFLA_BR_NF_CALL_ARPTABLES, + bi->b_nf_call_arptables); + + if (bi->ce_mask & BRIDGE_ATTR_STP_STATE) + NLA_PUT_U32(msg, IFLA_BR_STP_STATE, bi->b_stp_state); + + if (bi->ce_mask & BRIDGE_ATTR_MCAST_ROUTER) + NLA_PUT_U8(msg, IFLA_BR_MCAST_ROUTER, bi->b_mcast_router); + + if (bi->ce_mask & BRIDGE_ATTR_MCAST_SNOOPING) + NLA_PUT_U8(msg, IFLA_BR_MCAST_SNOOPING, bi->b_mcast_snooping); + + if (bi->ce_mask & BRIDGE_ATTR_BOOLOPT) + NLA_PUT(msg, IFLA_BR_MULTI_BOOLOPT, sizeof(bi->b_boolopts), + &bi->b_boolopts); + + nla_nest_end(msg, data); + return 0; + +nla_put_failure: + nla_nest_cancel(msg, data); + return -NLE_MSGSIZE; +} + +static void bridge_info_free(struct rtnl_link *link) +{ + _nl_clear_free(&link->l_info); +} + +static struct rtnl_link_info_ops bridge_info_ops = { + .io_name = "bridge", + .io_alloc = bridge_info_alloc, + .io_parse = bridge_info_parse, + .io_put_attrs = bridge_info_put_attrs, + .io_free = bridge_info_free, +}; + +#define IS_BRIDGE_INFO_ASSERT(link) \ + do { \ + if ((link)->l_info_ops != &bridge_info_ops) { \ + APPBUG("Link is not a bridge link. Set type \"bridge\" first."); \ + } \ + } while (0) + +/** + * Set ageing time for dynamic forwarding entries + * @arg link Link object of type bridge + * @arg ageing_time Interval to set. + * + * @see rtnl_link_bridge_get_ageing_time() + * + * @return void + */ +void rtnl_link_bridge_set_ageing_time(struct rtnl_link *link, + uint32_t ageing_time) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + bi->b_ageing_time = ageing_time; + + bi->ce_mask |= BRIDGE_ATTR_AGEING_TIME; +} + +/** + * Get ageing time for dynamic forwarding entries + * @arg link Link object of type bridge + * @arg ageing_time Output argument. + * + * @see rtnl_link_bridge_set_ageing_time() + * @return Zero on success, otherwise a negative error code. + * @retval -NLE_NOATTR + * @retval -NLE_INVAL + */ +int rtnl_link_bridge_get_ageing_time(struct rtnl_link *link, + uint32_t *ageing_time) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + if (!(bi->ce_mask & BRIDGE_ATTR_AGEING_TIME)) + return -NLE_NOATTR; + + if (!ageing_time) + return -NLE_INVAL; + + *ageing_time = bi->b_ageing_time; + + return 0; +} + +/** + * Set VLAN filtering flag + * @arg link Link object of type bridge + * @arg vlan_filtering VLAN_filtering boolean flag to set. + * + * @see rtnl_link_bridge_get_vlan_filtering() + * + * @return void + */ +void rtnl_link_bridge_set_vlan_filtering(struct rtnl_link *link, + uint8_t vlan_filtering) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + bi->b_vlan_filtering = vlan_filtering; + + bi->ce_mask |= BRIDGE_ATTR_VLAN_FILTERING; +} + +/** + * Get VLAN filtering flag + * @arg link Link object of type bridge + * @arg vlan_filtering Output argument. + * + * @see rtnl_link_bridge_set_vlan_filtering() + * + * @return Zero on success, otherwise a negative error code. + * @retval -NLE_NOATTR + * @retval -NLE_INVAL + */ +int rtnl_link_bridge_get_vlan_filtering(struct rtnl_link *link, + uint8_t *vlan_filtering) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + if (!(bi->ce_mask & BRIDGE_ATTR_VLAN_FILTERING)) + return -NLE_NOATTR; + + if (!vlan_filtering) + return -NLE_INVAL; + + *vlan_filtering = bi->b_vlan_filtering; + return 0; +} + +/** + * Set VLAN protocol + * @arg link Link object of type bridge + * @arg vlan_protocol VLAN protocol to set. The protocol + * numbers is in host byte order. + * + * @see rtnl_link_bridge_get_vlan_protocol() + * + * @return void + */ +void rtnl_link_bridge_set_vlan_protocol(struct rtnl_link *link, + uint16_t vlan_protocol) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + bi->b_vlan_protocol = vlan_protocol; + + bi->ce_mask |= BRIDGE_ATTR_VLAN_PROTOCOL; +} + +/** + * Get VLAN protocol + * @arg link Link object of type bridge + * @arg vlan_protocol Output argument. The protocol number is in host byte order. + * + * @see rtnl_link_bridge_set_vlan_protocol() + * + * @return Zero on success, otherwise a negative error code. + * @retval -NLE_NOATTR + * @retval -NLE_INVAL + */ +int rtnl_link_bridge_get_vlan_protocol(struct rtnl_link *link, + uint16_t *vlan_protocol) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + if (!(bi->ce_mask & BRIDGE_ATTR_VLAN_PROTOCOL)) + return -NLE_NOATTR; + + if (!vlan_protocol) + return -NLE_INVAL; + + *vlan_protocol = bi->b_vlan_protocol; + + return 0; +} + +/** + * Set VLAN default pvid + * @arg link Link object of type bridge + * @arg default pvid VLAN default pvid to set. + * + * @see rtnl_link_bridge_get_vlan_default_pvid() + * + * @return void + */ +void rtnl_link_bridge_set_vlan_default_pvid(struct rtnl_link *link, + uint16_t default_pvid) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + bi->b_vlan_default_pvid = default_pvid; + + bi->ce_mask |= BRIDGE_ATTR_VLAN_DEFAULT_PVID; +} + +/** + * Get VLAN default pvid + * @arg link Link object of type bridge + * @arg default_pvid Output argument. + * + * @see rtnl_link_bridge_set_vlan_default_pvid() + * + * @return Zero on success, otherwise a negative error code. + * @retval -NLE_NOATTR + * @retval -NLE_INVAL + */ +int rtnl_link_bridge_get_vlan_default_pvid(struct rtnl_link *link, + uint16_t *default_pvid) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + if (!(bi->ce_mask & BRIDGE_ATTR_VLAN_DEFAULT_PVID)) + return -NLE_NOATTR; + + if (!default_pvid) + return -NLE_INVAL; + + *default_pvid = bi->b_vlan_default_pvid; + + return 0; +} + +/** + * Set VLAN stats enabled flag + * @arg link Link object of type bridge + * @arg vlan_stats_enabled VLAN stats enabled flag to set + * + * @see rtnl_link_bridge_get_vlan_stats_enabled() + * + * @return void + */ +void rtnl_link_bridge_set_vlan_stats_enabled(struct rtnl_link *link, + uint8_t vlan_stats_enabled) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + bi->b_vlan_stats_enabled = vlan_stats_enabled; + + bi->ce_mask |= BRIDGE_ATTR_VLAN_STATS_ENABLED; +} + +/** + * Get VLAN stats enabled flag + * @arg link Link object of type bridge + * @arg vlan_stats_enabled Output argument. + * + * @see rtnl_link_bridge_set_vlan_stats_enabled() + * + * @return Zero on success, otherwise a negative error code. + * @retval -NLE_NOATTR + * @retval -NLE_INVAL + */ +int rtnl_link_bridge_get_vlan_stats_enabled(struct rtnl_link *link, + uint8_t *vlan_stats_enabled) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + if (!(bi->ce_mask & BRIDGE_ATTR_VLAN_STATS_ENABLED)) + return -NLE_NOATTR; + + if (!vlan_stats_enabled) + return -NLE_INVAL; + + *vlan_stats_enabled = bi->b_vlan_stats_enabled; + + return 0; +} + +/** + * Set call enabled flag for passing IPv4 traffic to iptables + * @arg link Link object of type bridge + * @arg call_enabled call enabled boolean flag to set. + * + * @see rtnl_link_bridge_get_nf_call_iptables() + * + * @return void + */ +void rtnl_link_bridge_set_nf_call_iptables(struct rtnl_link *link, + uint8_t call_enabled) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + bi->b_nf_call_iptables = call_enabled; + + bi->ce_mask |= BRIDGE_ATTR_NF_CALL_IPTABLES; +} + +/** + * Get call enabled flag for passing IPv4 traffic to iptables + * @arg link Link object of type bridge + * @arg call_enabled Output argument. + * + * @see rtnl_link_bridge_set_nf_call_iptables() + * + * @return Zero on success, otherwise a negative error code. + * @retval -NLE_NOATTR + * @retval -NLE_INVAL + */ +int rtnl_link_bridge_get_nf_call_iptables(struct rtnl_link *link, + uint8_t *call_enabled) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + if (!(bi->ce_mask & BRIDGE_ATTR_NF_CALL_IPTABLES)) + return -NLE_NOATTR; + + if (!call_enabled) + return -NLE_INVAL; + + *call_enabled = bi->b_nf_call_iptables; + + return 0; +} + +/** + * Set call enabled flag for passing IPv6 traffic to ip6tables + * @arg link Link object of type bridge + * @arg call_enabled call enabled boolean flag to set. + * + * @see rtnl_link_bridge_get_nf_call_ip6tables() + * + * @return void + */ +void rtnl_link_bridge_set_nf_call_ip6tables(struct rtnl_link *link, + uint8_t call_enabled) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + bi->b_nf_call_ip6tables = call_enabled; + + bi->ce_mask |= BRIDGE_ATTR_NF_CALL_IP6TABLES; +} + +/** + * Get call enabled flag for passing IPv6 traffic to iptables + * @arg link Link object of type bridge + * @arg call_enabled Output argument. + * + * @see rtnl_link_bridge_set_nf_call_ip6tables() + * + * @return Zero on success, otherwise a negative error code. + * @retval -NLE_NOATTR + * @retval -NLE_INVAL + */ +int rtnl_link_bridge_get_nf_call_ip6tables(struct rtnl_link *link, + uint8_t *call_enabled) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + if (!(bi->ce_mask & BRIDGE_ATTR_NF_CALL_IP6TABLES)) + return -NLE_NOATTR; + + if (!call_enabled) + return -NLE_INVAL; + + *call_enabled = bi->b_nf_call_ip6tables; + + return 0; +} + +/** + * Set call enabled flag for passing ARP traffic to arptables + * @arg link Link object of type bridge + * @arg call_enabled call enabled boolean flag to set. + * + * @see rtnl_link_bridge_get_nf_call_arptables() + * + * @return void + */ +void rtnl_link_bridge_set_nf_call_arptables(struct rtnl_link *link, + uint8_t call_enabled) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + bi->b_nf_call_arptables = call_enabled; + + bi->ce_mask |= BRIDGE_ATTR_NF_CALL_ARPTABLES; +} + +/** + * Get call enabled flag for passing ARP traffic to arptables + * @arg link Link object of type bridge + * @arg call_enabled Output argument. + * + * @see rtnl_link_bridge_set_nf_call_arptables() + * + * @return Zero on success, otherwise a negative error code. + * @retval -NLE_NOATTR + * @retval -NLE_INVAL + */ +int rtnl_link_bridge_get_nf_call_arptables(struct rtnl_link *link, + uint8_t *call_enabled) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + if (!(bi->ce_mask & BRIDGE_ATTR_NF_CALL_ARPTABLES)) + return -NLE_NOATTR; + + if (!call_enabled) + return -NLE_INVAL; + + *call_enabled = bi->b_nf_call_arptables; + + return 0; +} + +/** + * Set STP state + * @arg link Link object of type bridge + * @arg stp_state STP state to set. Typically 0 or 1. + * + * @see rtnl_link_bridge_get_stp_state() + * + * @return void + */ +void rtnl_link_bridge_set_stp_state(struct rtnl_link *link, uint32_t stp_state) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + bi->b_stp_state = stp_state; + + bi->ce_mask |= BRIDGE_ATTR_STP_STATE; +} + +/** + * Get STP state + * @arg link Link object of type bridge + * @arg stp_state Output argument. + * + * @see rtnl_link_bridge_set_stp_state() + * + * @return Zero on success, otherwise a negative error code. + * @retval -NLE_NOATTR + * @retval -NLE_INVAL + */ +int rtnl_link_bridge_get_stp_state(struct rtnl_link *link, uint32_t *stp_state) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + if (!(bi->ce_mask & BRIDGE_ATTR_STP_STATE)) + return -NLE_NOATTR; + + if (!stp_state) + return -NLE_INVAL; + + *stp_state = bi->b_stp_state; + return 0; +} + +/** + * Set multicast router type + * @arg link Link object of type bridge + * @arg type Multicast router type (MDB_RTR_TYPE_*) + * + * @see rtnl_link_bridge_get_mcast_router() + * + * @return void + */ +void rtnl_link_bridge_set_mcast_router(struct rtnl_link *link, uint8_t type) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + bi->b_mcast_router = type; + + bi->ce_mask |= BRIDGE_ATTR_MCAST_ROUTER; +} + +/** + * Get multicast router type + * @arg link Link object of type bridge + * @arg type Output argument. + * + * @see rtnl_link_bridge_set_mcast_router() + * + * @return Zero on success, otherwise a negative error code. + * @retval -NLE_NOATTR + * @retval -NLE_INVAL + */ +int rtnl_link_bridge_get_mcast_router(struct rtnl_link *link, uint8_t *type) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + if (!(bi->ce_mask & BRIDGE_ATTR_MCAST_ROUTER)) + return -NLE_NOATTR; + + if (!type) + return -NLE_INVAL; + + *type = bi->b_mcast_router; + return 0; +} + +/** + * Set multicast snooping + * @arg link Link object of type bridge + * @arg value Value to set. Typically 0 or 1. + * + * @see rtnl_link_bridge_get_mcast_snooping() + * + * @return void + */ +void rtnl_link_bridge_set_mcast_snooping(struct rtnl_link *link, uint8_t value) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + bi->b_mcast_snooping = value; + + bi->ce_mask |= BRIDGE_ATTR_MCAST_SNOOPING; +} + +/** + * Get multicast snooping value + * @arg link Link object of type bridge + * @arg value Output argument. + * + * @see rtnl_link_bridge_set_mcast_snooping() + * + * @return Zero on success, otherwise a negative error code. + * @retval -NLE_NOATTR + * @retval -NLE_INVAL + */ +int rtnl_link_bridge_get_mcast_snooping(struct rtnl_link *link, uint8_t *value) +{ + struct bridge_info *bi = bridge_info(link); + + IS_BRIDGE_INFO_ASSERT(link); + + if (!(bi->ce_mask & BRIDGE_ATTR_MCAST_SNOOPING)) + return -NLE_NOATTR; + + if (!value) + return -NLE_INVAL; + + *value = bi->b_mcast_snooping; + return 0; +} + +/** + * Set a the value of a boolopt + * @arg link Link object of type bridge + * @arg opt Option to modify (BR_BOOLOPT_*) + * @arg value Value to set the option to. 0 or 1. + * + * @see rtnl_link_bridge_get_boolopt() + * + * @return Zero on success, otherwise a negative error code. + * @retval -NLE_INVAL + */ +int rtnl_link_bridge_set_boolopt(struct rtnl_link *link, int opt, int value) +{ + struct bridge_info *bi = bridge_info(link); + uint32_t mask; + + IS_BRIDGE_INFO_ASSERT(link); + + if (opt < 0 || opt >= 32 || !(value == 0 || value == 1)) + return -NLE_INVAL; + + mask = 1ul << opt; + + if (value) + bi->b_boolopts.optval |= mask; + else + bi->b_boolopts.optval &= ~mask; + + bi->b_boolopts.optmask |= mask; + bi->ce_mask |= BRIDGE_ATTR_BOOLOPT; + + return 0; +} + +/** + * Get the value of a boolopt + * @arg link Link object of type bridge + * @arg opt Option to get (BR_BOOLOPT_*). + * + * @see rtnl_link_bridge_set_boolopt() + * + * @return The value of the boolopt (0 or 1), otherwise a negative error code. + * @retval -NLE_NOATTR + * @retval -NLE_INVAL + */ +int rtnl_link_bridge_get_boolopt(struct rtnl_link *link, int opt) +{ + struct bridge_info *bi = bridge_info(link); + uint32_t mask; + + IS_BRIDGE_INFO_ASSERT(link); + + if (opt < 0 || opt >= 32) + return -NLE_INVAL; + + mask = 1ul << opt; + + if (!(bi->ce_mask & BRIDGE_ATTR_BOOLOPT) || + !(bi->b_boolopts.optmask & mask)) + return -NLE_NOATTR; + + return !!(bi->b_boolopts.optval & mask); +} + +static void _nl_init bridge_info_init(void) +{ + rtnl_link_register_info(&bridge_info_ops); +} + +static void _nl_exit bridge_info_exit(void) +{ + rtnl_link_unregister_info(&bridge_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/can.c b/libnl/lib/route/link/can.c new file mode 100644 index 0000000..86e6684 --- /dev/null +++ b/libnl/lib/route/link/can.c @@ -0,0 +1,957 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2012 Benedikt Spranger + */ + +/** + * @ingroup link + * @defgroup can CAN + * Controller Area Network link module + * + * @details + * \b Link Type Name: "can" + * + * @route_doc{link_can, CAN Documentation} + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" + +/** @cond SKIP */ +#define CAN_HAS_BITTIMING (1<<0) +#define CAN_HAS_BITTIMING_CONST (1<<1) +#define CAN_HAS_CLOCK (1<<2) +#define CAN_HAS_STATE (1<<3) +#define CAN_HAS_CTRLMODE (1<<4) +#define CAN_HAS_RESTART_MS (1<<5) +#define CAN_HAS_RESTART (1<<6) +#define CAN_HAS_BERR_COUNTER (1<<7) +#define CAN_HAS_DATA_BITTIMING (1<<8) +#define CAN_HAS_DATA_BITTIMING_CONST (1<<9) +#define CAN_HAS_DEVICE_STATS (1<<10) + +struct can_info { + uint32_t ci_state; + uint32_t ci_restart; + uint32_t ci_restart_ms; + struct can_ctrlmode ci_ctrlmode; + struct can_bittiming ci_bittiming; + struct can_bittiming_const ci_bittiming_const; + struct can_clock ci_clock; + struct can_berr_counter ci_berr_counter; + uint32_t ci_mask; + struct can_bittiming ci_data_bittiming; + struct can_bittiming_const ci_data_bittiming_const; + struct can_device_stats ci_device_stats; +}; + +/** @endcond */ + +static struct nla_policy can_policy[IFLA_CAN_MAX + 1] = { + [IFLA_CAN_STATE] = { .type = NLA_U32 }, + [IFLA_CAN_CTRLMODE] = { .minlen = sizeof(struct can_ctrlmode) }, + [IFLA_CAN_RESTART_MS] = { .type = NLA_U32 }, + [IFLA_CAN_RESTART] = { .type = NLA_U32 }, + [IFLA_CAN_BITTIMING] = { .minlen = sizeof(struct can_bittiming) }, + [IFLA_CAN_BITTIMING_CONST] + = { .minlen = sizeof(struct can_bittiming_const) }, + [IFLA_CAN_CLOCK] = { .minlen = sizeof(struct can_clock) }, + [IFLA_CAN_BERR_COUNTER] = { .minlen = sizeof(struct can_berr_counter) }, + [IFLA_CAN_DATA_BITTIMING] + = { .minlen = sizeof(struct can_bittiming) }, + [IFLA_CAN_DATA_BITTIMING_CONST] + = { .minlen = sizeof(struct can_bittiming_const) }, +}; + +static int can_alloc(struct rtnl_link *link) +{ + struct can_info *ci; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*ci)); + else { + ci = calloc(1, sizeof(*ci)); + if (!ci) + return -NLE_NOMEM; + + link->l_info = ci; + } + + return 0; +} + +static int can_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_CAN_MAX+1]; + struct can_info *ci; + int err; + + NL_DBG(3, "Parsing CAN link info\n"); + + if ((err = nla_parse_nested(tb, IFLA_CAN_MAX, data, can_policy)) < 0) + goto errout; + + if ((err = can_alloc(link)) < 0) + goto errout; + + ci = link->l_info; + + if (tb[IFLA_CAN_STATE]) { + ci->ci_state = nla_get_u32(tb[IFLA_CAN_STATE]); + ci->ci_mask |= CAN_HAS_STATE; + } + + if (tb[IFLA_CAN_RESTART]) { + ci->ci_restart = nla_get_u32(tb[IFLA_CAN_RESTART]); + ci->ci_mask |= CAN_HAS_RESTART; + } + + if (tb[IFLA_CAN_RESTART_MS]) { + ci->ci_restart_ms = nla_get_u32(tb[IFLA_CAN_RESTART_MS]); + ci->ci_mask |= CAN_HAS_RESTART_MS; + } + + if (tb[IFLA_CAN_CTRLMODE]) { + nla_memcpy(&ci->ci_ctrlmode, tb[IFLA_CAN_CTRLMODE], + sizeof(ci->ci_ctrlmode)); + ci->ci_mask |= CAN_HAS_CTRLMODE; + } + + if (tb[IFLA_CAN_BITTIMING]) { + nla_memcpy(&ci->ci_bittiming, tb[IFLA_CAN_BITTIMING], + sizeof(ci->ci_bittiming)); + ci->ci_mask |= CAN_HAS_BITTIMING; + } + + if (tb[IFLA_CAN_BITTIMING_CONST]) { + nla_memcpy(&ci->ci_bittiming_const, + tb[IFLA_CAN_BITTIMING_CONST], + sizeof(ci->ci_bittiming_const)); + ci->ci_mask |= CAN_HAS_BITTIMING_CONST; + } + + if (tb[IFLA_CAN_CLOCK]) { + nla_memcpy(&ci->ci_clock, tb[IFLA_CAN_CLOCK], + sizeof(ci->ci_clock)); + ci->ci_mask |= CAN_HAS_CLOCK; + } + + if (tb[IFLA_CAN_BERR_COUNTER]) { + nla_memcpy(&ci->ci_berr_counter, tb[IFLA_CAN_BERR_COUNTER], + sizeof(ci->ci_berr_counter)); + ci->ci_mask |= CAN_HAS_BERR_COUNTER; + } + + if (tb[IFLA_CAN_DATA_BITTIMING]) { + nla_memcpy(&ci->ci_data_bittiming, tb[IFLA_CAN_DATA_BITTIMING], + sizeof(ci->ci_data_bittiming)); + ci->ci_mask |= CAN_HAS_DATA_BITTIMING; + } + + if (tb[IFLA_CAN_DATA_BITTIMING_CONST]) { + nla_memcpy(&ci->ci_data_bittiming_const, tb[IFLA_CAN_DATA_BITTIMING_CONST], + sizeof(ci->ci_data_bittiming_const)); + ci->ci_mask |= CAN_HAS_DATA_BITTIMING_CONST; + } + + if (xstats && _nla_len(xstats) >= sizeof(ci->ci_device_stats)) { + nla_memcpy(&ci->ci_device_stats, xstats, sizeof(ci->ci_device_stats)); + ci->ci_mask |= CAN_HAS_DEVICE_STATS; + } + + err = 0; +errout: + return err; +} + +static void can_free(struct rtnl_link *link) +{ + struct can_info *ci = link->l_info; + + free(ci); + link->l_info = NULL; +} + +static char *print_can_state (uint32_t state) +{ + char *text; + + switch (state) + { + case CAN_STATE_ERROR_ACTIVE: + text = "error active"; + break; + case CAN_STATE_ERROR_WARNING: + text = "error warning"; + break; + case CAN_STATE_ERROR_PASSIVE: + text = "error passive"; + break; + case CAN_STATE_BUS_OFF: + text = "bus off"; + break; + case CAN_STATE_STOPPED: + text = "stopped"; + break; + case CAN_STATE_SLEEPING: + text = "sleeping"; + break; + default: + text = "unknown state"; + } + + return text; +} + +static void can_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct can_info *ci = link->l_info; + char buf [64]; + + rtnl_link_can_ctrlmode2str(ci->ci_ctrlmode.flags, buf, sizeof(buf)); + nl_dump(p, "bitrate %d %s <%s>", + ci->ci_bittiming.bitrate, print_can_state(ci->ci_state), buf); +} + +static void can_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct can_info *ci = link->l_info; + + can_dump_line(link, p); + + if (ci->ci_mask & CAN_HAS_RESTART) { + if (ci->ci_restart) + nl_dump_line(p," restarting\n"); + } + + if (ci->ci_mask & CAN_HAS_RESTART_MS) { + nl_dump_line(p," restart interval %d ms\n", + ci->ci_restart_ms); + } + + if (ci->ci_mask & CAN_HAS_BITTIMING) { + nl_dump_line(p," sample point %f %%\n", + ((float) ci->ci_bittiming.sample_point)/10); + nl_dump_line(p," time quanta %d ns\n", + ci->ci_bittiming.tq); + nl_dump_line(p," propagation segment %d tq\n", + ci->ci_bittiming.prop_seg); + nl_dump_line(p," phase buffer segment1 %d tq\n", + ci->ci_bittiming.phase_seg1); + nl_dump_line(p," phase buffer segment2 %d tq\n", + ci->ci_bittiming.phase_seg2); + nl_dump_line(p," synchronisation jump width %d tq\n", + ci->ci_bittiming.sjw); + nl_dump_line(p," bitrate prescaler %d\n", + ci->ci_bittiming.brp); + } + + if (ci->ci_mask & CAN_HAS_BITTIMING_CONST) { + nl_dump_line(p," minimum tsig1 %d tq\n", + ci->ci_bittiming_const.tseg1_min); + nl_dump_line(p," maximum tsig1 %d tq\n", + ci->ci_bittiming_const.tseg1_max); + nl_dump_line(p," minimum tsig2 %d tq\n", + ci->ci_bittiming_const.tseg2_min); + nl_dump_line(p," maximum tsig2 %d tq\n", + ci->ci_bittiming_const.tseg2_max); + nl_dump_line(p," maximum sjw %d tq\n", + ci->ci_bittiming_const.sjw_max); + nl_dump_line(p," minimum brp %d\n", + ci->ci_bittiming_const.brp_min); + nl_dump_line(p," maximum brp %d\n", + ci->ci_bittiming_const.brp_max); + nl_dump_line(p," brp increment %d\n", + ci->ci_bittiming_const.brp_inc); + } + + if (ci->ci_mask & CAN_HAS_CLOCK) { + nl_dump_line(p," base freq %u Hz\n", ci->ci_clock.freq); + + } + + if (ci->ci_mask & CAN_HAS_BERR_COUNTER) { + nl_dump_line(p," bus error RX %d\n", + ci->ci_berr_counter.rxerr); + nl_dump_line(p," bus error TX %d\n", + ci->ci_berr_counter.txerr); + } +} + +static void can_dump_stats(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct can_info *ci = link->l_info; + + can_dump_details(link, p); + + if (ci->ci_mask & CAN_HAS_DEVICE_STATS) { + nl_dump_line(p," bus errors %d\n", + ci->ci_device_stats.bus_error); + nl_dump_line(p," error warning state changes %d\n", + ci->ci_device_stats.error_warning); + nl_dump_line(p," error passive state changes %d\n", + ci->ci_device_stats.error_passive); + nl_dump_line(p," bus off state changes %d\n", + ci->ci_device_stats.bus_off); + nl_dump_line(p," arbitration lost errors %d\n", + ci->ci_device_stats.arbitration_lost); + nl_dump_line(p," restarts %d\n", + ci->ci_device_stats.restarts); + } +} + +static int can_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct can_info *cdst, *csrc = src->l_info; + int ret; + + dst->l_info = NULL; + ret = rtnl_link_set_type(dst, "can"); + if (ret < 0) + return ret; + + cdst = malloc(sizeof(*cdst)); + if (!cdst) + return -NLE_NOMEM; + + *cdst = *csrc; + dst->l_info = cdst; + + return 0; +} + +static int can_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct can_info *ci = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + + if (ci->ci_mask & CAN_HAS_RESTART) + NLA_PUT_U32(msg, IFLA_CAN_RESTART, ci->ci_restart); + + if (ci->ci_mask & CAN_HAS_RESTART_MS) + NLA_PUT_U32(msg, IFLA_CAN_RESTART_MS, ci->ci_restart_ms); + + if (ci->ci_mask & CAN_HAS_CTRLMODE) + NLA_PUT(msg, IFLA_CAN_CTRLMODE, sizeof(ci->ci_ctrlmode), + &ci->ci_ctrlmode); + + if (ci->ci_mask & CAN_HAS_BITTIMING) + NLA_PUT(msg, IFLA_CAN_BITTIMING, sizeof(ci->ci_bittiming), + &ci->ci_bittiming); + + if (ci->ci_mask & CAN_HAS_BITTIMING_CONST) + NLA_PUT(msg, IFLA_CAN_BITTIMING_CONST, + sizeof(ci->ci_bittiming_const), + &ci->ci_bittiming_const); + + if (ci->ci_mask & CAN_HAS_CLOCK) + NLA_PUT(msg, IFLA_CAN_CLOCK, sizeof(ci->ci_clock), + &ci->ci_clock); + + if (ci->ci_mask & CAN_HAS_DATA_BITTIMING) + NLA_PUT(msg, IFLA_CAN_DATA_BITTIMING, sizeof(ci->ci_data_bittiming), + &ci->ci_data_bittiming); + + if (ci->ci_mask & CAN_HAS_DATA_BITTIMING_CONST) + NLA_PUT(msg, IFLA_CAN_DATA_BITTIMING_CONST, sizeof(ci->ci_data_bittiming_const), + &ci->ci_data_bittiming_const); + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static struct rtnl_link_info_ops can_info_ops = { + .io_name = "can", + .io_alloc = can_alloc, + .io_parse = can_parse, + .io_dump = { + [NL_DUMP_LINE] = can_dump_line, + [NL_DUMP_DETAILS] = can_dump_details, + [NL_DUMP_STATS] = can_dump_stats, + }, + .io_clone = can_clone, + .io_put_attrs = can_put_attrs, + .io_free = can_free, +}; + +/** @cond SKIP */ +#define IS_CAN_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &can_info_ops) { \ + APPBUG("Link is not a CAN link. set type \"can\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +/** + * @name CAN Object + * @{ + */ + +/** + * Check if link is a CAN link + * @arg link Link object + * + * @return True if link is a CAN link, otherwise false is returned. + */ +int rtnl_link_is_can(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "can"); +} + +/** + * Restart CAN device + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_restart(struct rtnl_link *link) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + ci->ci_restart = 1; + ci->ci_restart |= CAN_HAS_RESTART; + + return 0; +} + +/** + * Get CAN base frequency + * @arg link Link object + * @arg freq frequency in Hz + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_freq(struct rtnl_link *link, uint32_t *freq) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!freq) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_CLOCK) + *freq = ci->ci_clock.freq; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Get CAN state + * @arg link Link object + * @arg state CAN bus state + * @return 0 on success or a negative error code + */ +int rtnl_link_can_state(struct rtnl_link *link, uint32_t *state) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!state) + return -NLE_INVAL; + + *state = ci->ci_state; + + return 0; +} + +/** + * Get CAN RX bus error count + * @arg link Link object + * + * @return RX bus error count on success or a negative error code + */ +int rtnl_link_can_berr_rx(struct rtnl_link *link) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + if (ci->ci_mask & CAN_HAS_BERR_COUNTER) + return ci->ci_berr_counter.rxerr; + else + return -NLE_AGAIN; +} + +/** + * Get CAN TX bus error count + * @arg link Link object + * + * @return TX bus error count on success or a negative error code + */ +int rtnl_link_can_berr_tx(struct rtnl_link *link) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + if (ci->ci_mask & CAN_HAS_BERR_COUNTER) + return ci->ci_berr_counter.txerr; + else + return -NLE_AGAIN; +} + +/** + * Get CAN bus error count + * @arg link Link object + * @arg berr Bus error count + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_berr(struct rtnl_link *link, struct can_berr_counter *berr) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!berr) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_BERR_COUNTER) + *berr = ci->ci_berr_counter; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Get CAN hardware-dependent bit-timing constant + * @arg link Link object + * @arg bt_const Bit-timing constant + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_bt_const(struct rtnl_link *link, + struct can_bittiming_const *bt_const) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!bt_const) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_BITTIMING_CONST) + *bt_const = ci->ci_bittiming_const; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Get CAN device bit-timing + * @arg link Link object + * @arg bit_timing CAN bit-timing + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_bittiming(struct rtnl_link *link, + struct can_bittiming *bit_timing) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!bit_timing) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_BITTIMING) + *bit_timing = ci->ci_bittiming; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set CAN device bit-timing + * @arg link Link object + * @arg bit_timing CAN bit-timing + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_set_bittiming(struct rtnl_link *link, + const struct can_bittiming *bit_timing) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!bit_timing) + return -NLE_INVAL; + + ci->ci_bittiming = *bit_timing; + ci->ci_mask |= CAN_HAS_BITTIMING; + + return 0; +} + +/** + * Get CAN device bit-timing + * @arg link Link object + * @arg bitrate CAN bitrate + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_bitrate(struct rtnl_link *link, uint32_t *bitrate) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!bitrate) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_BITTIMING) + *bitrate = ci->ci_bittiming.bitrate; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set CAN device bit-rate + * @arg link Link object + * @arg bitrate CAN bitrate + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_set_bitrate(struct rtnl_link *link, uint32_t bitrate) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + ci->ci_bittiming.bitrate = bitrate; + ci->ci_mask |= CAN_HAS_BITTIMING; + + return 0; +} + +/** + * Get CAN device sample point + * @arg link Link object + * @arg sp CAN sample point + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_sample_point(struct rtnl_link *link, uint32_t *sp) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!sp) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_BITTIMING) + *sp = ci->ci_bittiming.sample_point; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set CAN device sample point + * @arg link Link object + * @arg sp CAN sample point + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_set_sample_point(struct rtnl_link *link, uint32_t sp) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + ci->ci_bittiming.sample_point = sp; + ci->ci_mask |= CAN_HAS_BITTIMING; + + return 0; +} + +/** + * Get CAN device restart intervall + * @arg link Link object + * @arg interval Restart intervall in ms + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_restart_ms(struct rtnl_link *link, uint32_t *interval) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!interval) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_RESTART_MS) + *interval = ci->ci_restart_ms; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set CAN device restart intervall + * @arg link Link object + * @arg interval Restart intervall in ms + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_set_restart_ms(struct rtnl_link *link, uint32_t interval) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + ci->ci_restart_ms = interval; + ci->ci_mask |= CAN_HAS_RESTART_MS; + + return 0; +} + +/** + * Get CAN control mode + * @arg link Link object + * @arg ctrlmode CAN control mode + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_ctrlmode(struct rtnl_link *link, uint32_t *ctrlmode) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!ctrlmode) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_CTRLMODE) + *ctrlmode = ci->ci_ctrlmode.flags; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set a CAN Control Mode + * @arg link Link object + * @arg ctrlmode CAN control mode + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_set_ctrlmode(struct rtnl_link *link, uint32_t ctrlmode) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + ci->ci_ctrlmode.flags |= ctrlmode; + ci->ci_ctrlmode.mask |= ctrlmode; + ci->ci_mask |= CAN_HAS_CTRLMODE; + + return 0; +} + +/** + * Unset a CAN Control Mode + * @arg link Link object + * @arg ctrlmode CAN control mode + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_unset_ctrlmode(struct rtnl_link *link, uint32_t ctrlmode) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + ci->ci_ctrlmode.flags &= ~ctrlmode; + ci->ci_ctrlmode.mask |= ctrlmode; + ci->ci_mask |= CAN_HAS_CTRLMODE; + + return 0; +} + +/** + * Get CAN FD hardware-dependent data bit-timing constant + * @arg link Link object + * @arg data_bt_const CAN FD data bit-timing constant + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_data_bittiming_const(struct rtnl_link *link, + struct can_bittiming_const *data_bt_const) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!data_bt_const) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_DATA_BITTIMING_CONST) + *data_bt_const = ci->ci_data_bittiming_const; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set CAN FD device data bit-timing-const + * @arg link Link object + * @arg data_bit_timing CAN FD data bit-timing + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_set_data_bittiming_const(struct rtnl_link *link, + const struct can_bittiming_const *data_bt_const) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!data_bt_const) + return -NLE_INVAL; + + ci->ci_data_bittiming_const = *data_bt_const; + ci->ci_mask |= CAN_HAS_DATA_BITTIMING_CONST; + + return 0; +} + +/** + * Get CAN FD device data bit-timing + * @arg link Link object + * @arg data_bit_timing CAN FD data bit-timing + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_data_bittiming(struct rtnl_link *link, + struct can_bittiming *data_bit_timing) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!data_bit_timing) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_DATA_BITTIMING) + *data_bit_timing = ci->ci_data_bittiming; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set CAN FD device data bit-timing + * @arg link Link object + * @arg data_bit_timing CAN FD data bit-timing + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_set_data_bittiming(struct rtnl_link *link, + const struct can_bittiming *data_bit_timing) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!data_bit_timing) + return -NLE_INVAL; + + ci->ci_data_bittiming = *data_bit_timing; + ci->ci_mask |= CAN_HAS_DATA_BITTIMING; + + return 0; +} + +/** + * Get CAN device stats + * @arg link Link object + * @arg device_stats CAN device stats + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_device_stats(struct rtnl_link* link, + struct can_device_stats *device_stats) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!device_stats) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_DEVICE_STATS) + *device_stats = ci->ci_device_stats; + else + return -NLE_MISSING_ATTR; + + return 0; +} + +/** @} */ + +/** + * @name Control Mode Translation + * @{ + */ + +static const struct trans_tbl can_ctrlmode[] = { + __ADD(CAN_CTRLMODE_LOOPBACK, loopback), + __ADD(CAN_CTRLMODE_LISTENONLY, listen-only), + __ADD(CAN_CTRLMODE_3_SAMPLES, triple-sampling), + __ADD(CAN_CTRLMODE_ONE_SHOT, one-shot), + __ADD(CAN_CTRLMODE_BERR_REPORTING, berr-reporting), + __ADD(CAN_CTRLMODE_FD, fd), + __ADD(CAN_CTRLMODE_PRESUME_ACK, presume-ack), + __ADD(CAN_CTRLMODE_FD_NON_ISO, fd-non-iso), +}; + +char *rtnl_link_can_ctrlmode2str(int ctrlmode, char *buf, size_t len) +{ + return __flags2str(ctrlmode, buf, len, can_ctrlmode, + ARRAY_SIZE(can_ctrlmode)); +} + +int rtnl_link_can_str2ctrlmode(const char *name) +{ + return __str2flags(name, can_ctrlmode, ARRAY_SIZE(can_ctrlmode)); +} + +/** @} */ + +static void _nl_init can_init(void) +{ + rtnl_link_register_info(&can_info_ops); +} + +static void _nl_exit can_exit(void) +{ + rtnl_link_unregister_info(&can_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/dummy.c b/libnl/lib/route/link/dummy.c new file mode 100644 index 0000000..83d6ba7 --- /dev/null +++ b/libnl/lib/route/link/dummy.c @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2011 Thomas Graf + */ + +/** + * @ingroup link + * @defgroup dummy Dummy + * + * @details + * \b Link Type Name: "dummy" + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include "link-api.h" + +static struct rtnl_link_info_ops dummy_info_ops = { + .io_name = "dummy", +}; + +static void _nl_init dummy_init(void) +{ + rtnl_link_register_info(&dummy_info_ops); +} + +static void _nl_exit dummy_exit(void) +{ + rtnl_link_unregister_info(&dummy_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/geneve.c b/libnl/lib/route/link/geneve.c new file mode 100644 index 0000000..61dd1f1 --- /dev/null +++ b/libnl/lib/route/link/geneve.c @@ -0,0 +1,803 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2018 Wang Jian + */ + +/** + * @ingroup link + * @defgroup geneve Geneve + * Generic Network Virtualization Encapsulation + * + * @details + * \b Link Type Name: "geneve" + * + * @route_doc{link_geneve, Geneve Documentation} + * + * @{ + */ +#include "nl-default.h" + +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" + +/** @cond SKIP */ +#define GENEVE_ATTR_ID (1<<0) +#define GENEVE_ATTR_REMOTE (1<<1) +#define GENEVE_ATTR_REMOTE6 (1<<2) +#define GENEVE_ATTR_TTL (1<<3) +#define GENEVE_ATTR_TOS (1<<4) +#define GENEVE_ATTR_LABEL (1<<5) +#define GENEVE_ATTR_PORT (1<<6) +#define GENEVE_ATTR_FLAGS (1<<7) +#define GENEVE_ATTR_UDP_CSUM (1<<8) +#define GENEVE_ATTR_UDP_ZERO_CSUM6_TX (1<<9) +#define GENEVE_ATTR_UDP_ZERO_CSUM6_RX (1<<10) + +struct geneve_info +{ + uint32_t id; + uint32_t remote; + struct in6_addr remote6; + uint8_t ttl; + uint8_t tos; + uint32_t label; + uint16_t port; + uint8_t flags; + uint8_t udp_csum; + uint8_t udp_zero_csum6_tx; + uint8_t udp_zero_csum6_rx; + uint32_t mask; +}; + +/** @endcond */ + +static struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = { + [IFLA_GENEVE_ID] = { .type = NLA_U32 }, + [IFLA_GENEVE_REMOTE] = { .minlen = sizeof(uint32_t) }, + [IFLA_GENEVE_REMOTE6] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_GENEVE_TTL] = { .type = NLA_U8 }, + [IFLA_GENEVE_TOS] = { .type = NLA_U8 }, + [IFLA_GENEVE_LABEL] = { .type = NLA_U32 }, + [IFLA_GENEVE_PORT] = { .type = NLA_U16 }, + [IFLA_GENEVE_COLLECT_METADATA] = { .type = NLA_FLAG }, + [IFLA_GENEVE_UDP_CSUM] = { .type = NLA_U8 }, + [IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = { .type = NLA_U8 }, + [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NLA_U8 }, +}; + +static int geneve_alloc(struct rtnl_link *link) +{ + struct geneve_info *geneve; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*geneve)); + else { + if ((geneve = calloc(1, sizeof(*geneve))) == NULL) + return -NLE_NOMEM; + link->l_info = geneve; + } + + return 0; +} + +static int geneve_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_GENEVE_MAX + 1]; + struct geneve_info *geneve; + int err = 0; + + NL_DBG(3, "Parsing Geneve link info\n"); + + err = nla_parse_nested(tb, IFLA_GENEVE_MAX, data, geneve_policy); + if (err < 0) + return err; + + err = geneve_alloc(link); + if (err < 0) + return err; + + geneve = link->l_info; + + if (tb[IFLA_GENEVE_ID]) { + geneve->id = nla_get_u32(tb[IFLA_GENEVE_ID]); + geneve->mask |= GENEVE_ATTR_ID; + } + + if (tb[IFLA_GENEVE_REMOTE]) { + nla_memcpy(&geneve->remote, tb[IFLA_GENEVE_REMOTE], + sizeof(geneve->remote)); + geneve->mask |= GENEVE_ATTR_REMOTE; + geneve->mask &= ~GENEVE_ATTR_REMOTE6; + } + if (tb[IFLA_GENEVE_REMOTE6]) { + nla_memcpy(&geneve->remote6, tb[IFLA_GENEVE_REMOTE6], + sizeof(geneve->remote6)); + geneve->mask |= GENEVE_ATTR_REMOTE6; + geneve->mask &= ~GENEVE_ATTR_REMOTE; + } + + if (tb[IFLA_GENEVE_TTL]) { + geneve->ttl = nla_get_u8(tb[IFLA_GENEVE_TTL]); + geneve->mask |= GENEVE_ATTR_TTL; + } + + if (tb[IFLA_GENEVE_TOS]) { + geneve->tos = nla_get_u8(tb[IFLA_GENEVE_TOS]); + geneve->mask |= GENEVE_ATTR_TOS; + } + + if (tb[IFLA_GENEVE_LABEL]) { + geneve->label = nla_get_u32(tb[IFLA_GENEVE_LABEL]); + geneve->mask |= GENEVE_ATTR_LABEL; + } + + if (tb[IFLA_GENEVE_PORT]) { + geneve->port = nla_get_u16(tb[IFLA_GENEVE_PORT]); + geneve->mask |= GENEVE_ATTR_PORT; + } + + if (tb[IFLA_GENEVE_COLLECT_METADATA]) + geneve->flags |= RTNL_LINK_GENEVE_F_COLLECT_METADATA; + + if (tb[IFLA_GENEVE_UDP_CSUM]) { + geneve->udp_csum = nla_get_u8(tb[IFLA_GENEVE_UDP_CSUM]); + geneve->mask |= GENEVE_ATTR_UDP_CSUM; + } + + if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]) { + geneve->udp_zero_csum6_tx = nla_get_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]); + geneve->mask |= GENEVE_ATTR_UDP_ZERO_CSUM6_TX; + } + + if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]) { + geneve->udp_zero_csum6_rx = nla_get_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]); + geneve->mask |= GENEVE_ATTR_UDP_ZERO_CSUM6_RX; + } + + return err; +} + +static void geneve_free(struct rtnl_link *link) +{ + struct geneve_info *geneve = link->l_info; + + free(geneve); + link->l_info = NULL; +} + +static void geneve_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct geneve_info *geneve = link->l_info; + + nl_dump(p, "geneve-id %u", geneve->id); +} + +static void geneve_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct geneve_info *geneve = link->l_info; + char addr[INET6_ADDRSTRLEN]; + + nl_dump_line(p, " geneve-id %u\n", geneve->id); + + if (geneve->mask & GENEVE_ATTR_REMOTE) { + nl_dump(p, " remote "); + nl_dump_line(p, "%s\n", + _nl_inet_ntop(AF_INET, &geneve->remote, addr)); + } else if (geneve->mask & GENEVE_ATTR_REMOTE6) { + nl_dump(p, " remote "); + nl_dump_line(p, "%s\n", + _nl_inet_ntop(AF_INET6, &geneve->remote6, addr)); + } + + if (geneve->mask & GENEVE_ATTR_TTL) { + nl_dump(p, " ttl "); + nl_dump_line(p, "%u\n", geneve->ttl); + } + + if (geneve->mask & GENEVE_ATTR_TOS) { + nl_dump(p, " tos "); + nl_dump_line(p, "%u\n", geneve->tos); + } + + if (geneve->mask & GENEVE_ATTR_PORT) { + nl_dump(p, " port "); + nl_dump_line(p, "%u\n", ntohs(geneve->port)); + } + + if (geneve->mask & GENEVE_ATTR_LABEL) { + nl_dump(p, " label "); + nl_dump_line(p, "%u\n", ntohl(geneve->label)); + } + + if (geneve->mask & GENEVE_ATTR_UDP_CSUM) { + nl_dump(p, " UDP checksum "); + if (geneve->udp_csum) + nl_dump_line(p, "enabled (%#x)\n", geneve->udp_csum); + else + nl_dump_line(p, "disabled\n"); + } + + if (geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_TX) { + nl_dump(p, " udp-zero-csum6-tx "); + if (geneve->udp_zero_csum6_tx) + nl_dump_line(p, "enabled (%#x)\n", geneve->udp_zero_csum6_tx); + else + nl_dump_line(p, "disabled\n"); + } + + if (geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_RX) { + nl_dump(p, " udp-zero-csum6-rx "); + if (geneve->udp_zero_csum6_rx) + nl_dump_line(p, "enabled (%#x)\n", geneve->udp_zero_csum6_rx); + else + nl_dump_line(p, "disabled\n"); + } + + if (geneve->flags & RTNL_LINK_GENEVE_F_COLLECT_METADATA) + nl_dump(p, " collect-metadata\n"); +} + +static int geneve_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct geneve_info *gdst, *gsrc; + int err; + + gsrc = src->l_info; + dst->l_info = NULL; + err = rtnl_link_set_type(dst, "geneve"); + if (err < 0) + return err; + + gdst = dst->l_info; + + if (!gsrc || !gdst) + return -NLE_NOMEM; + + memcpy(gdst, gsrc, sizeof(struct geneve_info)); + + return 0; +} + +static int geneve_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct geneve_info *geneve = link->l_info; + struct nlattr *data; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + return -NLE_MSGSIZE; + + if (geneve->mask & GENEVE_ATTR_ID) + NLA_PUT_U32(msg, IFLA_GENEVE_ID, geneve->id); + + if (geneve->mask & GENEVE_ATTR_REMOTE) + NLA_PUT(msg, IFLA_GENEVE_REMOTE, + sizeof(geneve->remote), &geneve->remote); + + if (geneve->mask & GENEVE_ATTR_REMOTE6) + NLA_PUT(msg, IFLA_GENEVE_REMOTE6, + sizeof(geneve->remote6), &geneve->remote6); + + if (geneve->mask & GENEVE_ATTR_TTL) + NLA_PUT_U8(msg, IFLA_GENEVE_TTL, geneve->ttl); + + if (geneve->mask & GENEVE_ATTR_TOS) + NLA_PUT_U8(msg, IFLA_GENEVE_TOS, geneve->tos); + + if (geneve->mask & GENEVE_ATTR_LABEL) + NLA_PUT_U32(msg, IFLA_GENEVE_LABEL, geneve->label); + + if (geneve->mask & GENEVE_ATTR_PORT) + NLA_PUT_U32(msg, IFLA_GENEVE_PORT, geneve->port); + + if (geneve->mask & GENEVE_ATTR_UDP_CSUM) + NLA_PUT_U8(msg, IFLA_GENEVE_UDP_CSUM, geneve->udp_csum); + + if (geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_TX) + NLA_PUT_U8(msg, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, geneve->udp_zero_csum6_tx); + + if (geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_RX) + NLA_PUT_U8(msg, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, geneve->udp_zero_csum6_rx); + + if (geneve->flags & RTNL_LINK_GENEVE_F_COLLECT_METADATA) + NLA_PUT_FLAG(msg, IFLA_GENEVE_COLLECT_METADATA); + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static struct rtnl_link_info_ops geneve_info_ops = { + .io_name = "geneve", + .io_alloc = geneve_alloc, + .io_parse = geneve_parse, + .io_dump = { + [NL_DUMP_LINE] = geneve_dump_line, + [NL_DUMP_DETAILS] = geneve_dump_details, + }, + .io_clone = geneve_clone, + .io_put_attrs = geneve_put_attrs, + .io_free = geneve_free, +}; + + +/** @cond SKIP */ +#define IS_GENEVE_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &geneve_info_ops) { \ + APPBUG("Link is not a geneve link. set type \"geneve\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +/** + * @name Geneve Object + * @{ + */ + +/** + * Allocate link object of type Geneve + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_geneve_alloc(void) +{ + struct rtnl_link *link; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if (rtnl_link_set_type(link, "geneve") < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a Geneve link + * @arg link Link object + * + * @return True if link is a Geneve link, otherwisee false is returned. + */ +int rtnl_link_is_geneve(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "geneve"); +} + +/** + * Set Geneve Network Indentifier + * @arg link Link object + * @arg id Geneve network identifier + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_set_id(struct rtnl_link *link, uint32_t id) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (id > RTNL_GENEVE_ID_MAX) + return -NLE_INVAL; + + geneve->id = id; + geneve->mask |= GENEVE_ATTR_ID; + + return 0; +} + +/** + * Get Geneve Network Identifier + * @arg link Link object + * @arg id Pointer to store network identifier + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_get_id(struct rtnl_link *link, uint32_t *id) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (!id) + return -NLE_INVAL; + + if (geneve->mask & GENEVE_ATTR_ID) + *id = geneve->id; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set Geneve unicast destination IP address + * @arg link Link object + * @arg addr The unicast destination IP address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_set_remote(struct rtnl_link *link, struct nl_addr *addr) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if ((nl_addr_get_family(addr) == AF_INET) && + (nl_addr_get_len(addr) == sizeof(geneve->remote))) { + memcpy(&geneve->remote, nl_addr_get_binary_addr(addr), + sizeof(geneve->remote)); + geneve->mask |= GENEVE_ATTR_REMOTE; + geneve->mask &= ~GENEVE_ATTR_REMOTE6; + } else if ((nl_addr_get_family(addr) == AF_INET6) && + (nl_addr_get_len(addr) == sizeof(geneve->remote6))) { + memcpy(&geneve->remote6, nl_addr_get_binary_addr(addr), + sizeof(geneve->remote6)); + geneve->mask |= GENEVE_ATTR_REMOTE6; + geneve->mask &= ~GENEVE_ATTR_REMOTE; + } else + return -NLE_INVAL; + + return 0; +} + +/** + * Get Geneve unicast destination IP address + * @arg link Link object + * @arg addr Pointer to store unicast destination IP addree + * + * @return 0 on success or a a negative error code + */ +int rtnl_link_geneve_get_remote(struct rtnl_link *link, struct nl_addr **addr) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (!addr) + return -NLE_INVAL; + + if (geneve->mask & GENEVE_ATTR_REMOTE) + *addr = nl_addr_build(AF_INET, &geneve->remote, sizeof(geneve->remote)); + else if (geneve->mask & GENEVE_ATTR_REMOTE6) + *addr = nl_addr_build(AF_INET6, &geneve->remote6, sizeof(geneve->remote6)); + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set IP TTL value to use for Geneve + * @arg link Link object + * @arg ttl TTL value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_set_ttl(struct rtnl_link *link, uint8_t ttl) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + geneve->ttl = ttl; + geneve->mask |= GENEVE_ATTR_TTL; + + return 0; +} + +/** + * Get IP TTL value to use for Geneve + * @arg link Link object + * + * @return TTL value on success or a negative error code + */ +int rtnl_link_geneve_get_ttl(struct rtnl_link *link) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (!(geneve->mask & GENEVE_ATTR_TTL)) + return -NLE_AGAIN; + + return geneve->ttl; +} + +/** + * Set IP ToS value to use for Geneve + * @arg link Link object + * @arg tos ToS value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_set_tos(struct rtnl_link *link, uint8_t tos) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + geneve->tos = tos; + geneve->mask |= GENEVE_ATTR_TOS; + + return 0; +} + +/** + * Get IP ToS value to use for Geneve + * @arg link Link object + * + * @return ToS value on success or a negative error code + */ +int rtnl_link_geneve_get_tos(struct rtnl_link *link) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (!(geneve->mask & GENEVE_ATTR_TOS)) + return -NLE_AGAIN; + + return geneve->tos; +} + +/** + * Set UDP destination port to use for Geneve + * @arg link Link object + * @arg port Destination port + * + * @return 0 on success or a negative error code + */ + +int rtnl_link_geneve_set_port(struct rtnl_link *link, uint32_t port) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + geneve->port = htons(port); + geneve->mask |= GENEVE_ATTR_PORT; + + return 0; +} + +/** + * Get UDP destination port to use for Geneve + * @arg link Link object + * @arg port Pointer to store destination port + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_get_port(struct rtnl_link *link, uint32_t *port) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (!port) + return -NLE_INVAL; + + if (!(geneve->mask & GENEVE_ATTR_PORT)) + return -NLE_NOATTR; + + *port = ntohs(geneve->port); + + return 0; +} + +/** + * Set flow label to use for Geneve + * @arg link Link object + * @arg label Destination label + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_set_label(struct rtnl_link *link, uint32_t label) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + geneve->label = htonl(label); + geneve->mask |= GENEVE_ATTR_LABEL; + + return 0; +} + +/** + * Get flow label to use for Geneve + * @arg link Link object + * @arg label Pointer to store destination label + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_get_label(struct rtnl_link *link, uint32_t *label) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (!label) + return -NLE_INVAL; + if (!(geneve->mask & GENEVE_ATTR_LABEL)) + return -NLE_NOATTR; + + *label = ntohl(geneve->label); + + return 0; +} + +/** + * Set UDP checksum status to use for Geneve + * @arg link Link object + * @arg csum Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_set_udp_csum(struct rtnl_link *link, uint8_t csum) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + geneve->udp_csum = csum; + geneve->mask |= GENEVE_ATTR_UDP_CSUM; + + return 0; +} + +/** + * Get UDP checksum status to use for Geneve + * @arg link Link object + * + * @return status value on success or a negative error code + */ +int rtnl_link_geneve_get_udp_csum(struct rtnl_link *link) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (!(geneve->mask & GENEVE_ATTR_UDP_CSUM)) + return -NLE_NOATTR; + + return geneve->udp_csum; +} + +/** + * Set skip UDP checksum transmitted over IPv6 status to use for Geneve + * @arg link Link object + * @arg csum Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_set_udp_zero_csum6_tx(struct rtnl_link *link, uint8_t csum) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + geneve->udp_zero_csum6_tx = csum; + geneve->mask |= GENEVE_ATTR_UDP_ZERO_CSUM6_TX; + + return 0; +} + +/** + * Get skip UDP checksum transmitted over IPv6 status to use for Geneve + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_geneve_get_udp_zero_csum6_tx(struct rtnl_link *link) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (!(geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_TX)) + return -NLE_NOATTR; + + return geneve->udp_zero_csum6_tx; +} + +/** + * Set skip UDP checksum received over IPv6 status to use for Geneve + * @arg link Link object + * @arg csum Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_set_udp_zero_csum6_rx(struct rtnl_link *link, uint8_t csum) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + geneve->udp_zero_csum6_rx = csum; + geneve->mask |= GENEVE_ATTR_UDP_ZERO_CSUM6_RX; + + return 0; +} + +/** + * Get skip UDP checksum received over IPv6 status to use for Geneve + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_geneve_get_udp_zero_csum6_rx(struct rtnl_link *link) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (!(geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_RX)) + return -NLE_NOATTR; + + return geneve->udp_zero_csum6_rx; +} + +/** + * Set Geneve flags + * @arg link Link object + * @arg flags Which flags to set + * @arg enable Boolean enabling or disabling flag + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_set_flags(struct rtnl_link *link, uint8_t flags, int enable) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (flags & ~RTNL_LINK_GENEVE_F_COLLECT_METADATA) + return -NLE_INVAL; + + if (enable) + geneve->flags = flags; + else + geneve->flags &= ~flags; + + return 0; +} + +/** + * Get Geneve flags + * @arg link Link object + * @arg flags Pointer to store flags + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_get_flags(struct rtnl_link *link, uint8_t *flags) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + *flags = geneve->flags; + return 0; +} + +/** @} */ +static void _nl_init geneve_init(void) +{ + rtnl_link_register_info(&geneve_info_ops); +} + +static void _nl_exit geneve_exit(void) +{ + rtnl_link_unregister_info(&geneve_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/ifb.c b/libnl/lib/route/link/ifb.c new file mode 100644 index 0000000..d5fc685 --- /dev/null +++ b/libnl/lib/route/link/ifb.c @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Cong Wang + */ + +/** + * @ingroup link + * @defgroup ifb Intermediate Functional Block + * + * @details + * \b Link Type Name: "ifb" + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include "link-api.h" + +static struct rtnl_link_info_ops ifb_info_ops = { + .io_name = "ifb", +}; + +static void _nl_init ifb_init(void) +{ + rtnl_link_register_info(&ifb_info_ops); +} + +static void _nl_exit ifb_exit(void) +{ + rtnl_link_unregister_info(&ifb_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/inet.c b/libnl/lib/route/link/inet.c new file mode 100644 index 0000000..7ed705e --- /dev/null +++ b/libnl/lib/route/link/inet.c @@ -0,0 +1,294 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +/** + * @ingroup link_API + * @defgroup link_inet IPv4 Link Module + * @brief Implementation of IPv4 specific link attributes + * + * + * + * @par Example: Reading the value of IPV4_DEVCONF_FORWARDING + * @code + * struct nl_cache *cache; + * struct rtnl_link *link; + * uint32_t value; + * + * // Allocate a link cache + * rtnl_link_alloc_cache(sock, AF_UNSPEC, &cache); + * + * // Search for the link we wish to see the value from + * link = rtnl_link_get_by_name(cache, "eth0"); + * + * // Read the value of the setting IPV4_DEVCONF_FORWARDING + * if (rtnl_link_inet_get_conf(link, IPV4_DEVCONF_FORWARDING, &value) < 0) + * // Error: Unable to read config setting + * + * printf("forwarding is %s\n", value ? "enabled" : "disabled"); + * @endcode + * + * @par Example: Changing the value of IPV4_DEVCONF_FOWARDING + * @code + * // + * // ... Continueing from the previous example ... + * // + * + * struct rtnl_link *new; + * + * // Allocate a new link to store the changes we wish to make. + * new = rtnl_link_alloc(); + * + * // Set IPV4_DEVCONF_FORWARDING to '1' + * rtnl_link_inet_set_conf(new, IPV4_DEVCONF_FORWARDING, 1); + * + * // Send the change request to the kernel. + * rtnl_link_change(sock, link, new, 0); + * @endcode + * + * @{ + */ + + +#include "nl-default.h" + +#include + +#include +#include +#include +#include + +#include "link-api.h" + +/** @cond SKIP */ +struct inet_data +{ + uint8_t i_confset[IPV4_DEVCONF_MAX]; + uint32_t i_conf[IPV4_DEVCONF_MAX]; +}; +/** @endcond */ + +static void *inet_alloc(struct rtnl_link *link) +{ + return calloc(1, sizeof(struct inet_data)); +} + +static void *inet_clone(struct rtnl_link *link, void *data) +{ + struct inet_data *id; + + if ((id = inet_alloc(link))) + memcpy(id, data, sizeof(*id)); + + return id; +} + +static void inet_free(struct rtnl_link *link, void *data) +{ + free(data); +} + +static struct nla_policy inet_policy[IFLA_INET_MAX+1] = { + [IFLA_INET_CONF] = { .minlen = 4 }, +}; + +static int inet_parse_af(struct rtnl_link *link, struct nlattr *attr, void *data) +{ + struct inet_data *id = data; + struct nlattr *tb[IFLA_INET_MAX+1]; + int err; + + err = nla_parse_nested(tb, IFLA_INET_MAX, attr, inet_policy); + if (err < 0) + return err; + if (tb[IFLA_INET_CONF] && nla_len(tb[IFLA_INET_CONF]) % 4) + return -EINVAL; + + if (tb[IFLA_INET_CONF]) { + int i; + int len = _NL_MIN(IPV4_DEVCONF_MAX, nla_len(tb[IFLA_INET_CONF]) / 4); + + for (i = 0; i < len; i++) + id->i_confset[i] = 1; + nla_memcpy(&id->i_conf, tb[IFLA_INET_CONF], sizeof(id->i_conf)); + } + + return 0; +} + +static int inet_fill_af(struct rtnl_link *link, struct nl_msg *msg, void *data) +{ + struct inet_data *id = data; + struct nlattr *nla; + int i; + + if (!(nla = nla_nest_start(msg, IFLA_INET_CONF))) + return -NLE_MSGSIZE; + + for (i = 0; i < IPV4_DEVCONF_MAX; i++) + if (id->i_confset[i]) + NLA_PUT_U32(msg, i+1, id->i_conf[i]); + + nla_nest_end(msg, nla); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static const struct trans_tbl inet_devconf[] = { + __ADD(IPV4_DEVCONF_FORWARDING, forwarding), + __ADD(IPV4_DEVCONF_MC_FORWARDING, mc_forwarding), + __ADD(IPV4_DEVCONF_PROXY_ARP, proxy_arp), + __ADD(IPV4_DEVCONF_ACCEPT_REDIRECTS, accept_redirects), + __ADD(IPV4_DEVCONF_SECURE_REDIRECTS, secure_redirects), + __ADD(IPV4_DEVCONF_SEND_REDIRECTS, send_redirects), + __ADD(IPV4_DEVCONF_SHARED_MEDIA, shared_media), + __ADD(IPV4_DEVCONF_RP_FILTER, rp_filter), + __ADD(IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE, accept_source_route), + __ADD(IPV4_DEVCONF_BOOTP_RELAY, bootp_relay), + __ADD(IPV4_DEVCONF_LOG_MARTIANS, log_martians), + __ADD(IPV4_DEVCONF_TAG, tag), + __ADD(IPV4_DEVCONF_ARPFILTER, arpfilter), + __ADD(IPV4_DEVCONF_MEDIUM_ID, medium_id), + __ADD(IPV4_DEVCONF_NOXFRM, noxfrm), + __ADD(IPV4_DEVCONF_NOPOLICY, nopolicy), + __ADD(IPV4_DEVCONF_FORCE_IGMP_VERSION, force_igmp_version), + __ADD(IPV4_DEVCONF_ARP_ANNOUNCE, arp_announce), + __ADD(IPV4_DEVCONF_ARP_IGNORE, arp_ignore), + __ADD(IPV4_DEVCONF_PROMOTE_SECONDARIES, promote_secondaries), + __ADD(IPV4_DEVCONF_ARP_ACCEPT, arp_accept), + __ADD(IPV4_DEVCONF_ARP_NOTIFY, arp_notify), + __ADD(IPV4_DEVCONF_ACCEPT_LOCAL, accept_local), + __ADD(IPV4_DEVCONF_SRC_VMARK, src_vmark), + __ADD(IPV4_DEVCONF_PROXY_ARP_PVLAN, proxy_arp_pvlan), + __ADD(IPV4_DEVCONF_ROUTE_LOCALNET, route_localnet), + __ADD(IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL, igmpv2_unsolicited_report_interval), + __ADD(IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL, igmpv3_unsolicited_report_interval), +}; + +const char *rtnl_link_inet_devconf2str(int type, char *buf, size_t len) +{ + return __type2str(type, buf, len, inet_devconf, + ARRAY_SIZE(inet_devconf)); +} + +int rtnl_link_inet_str2devconf(const char *name) +{ + return __str2type(name, inet_devconf, ARRAY_SIZE(inet_devconf)); +} + +static void inet_dump_details(struct rtnl_link *link, + struct nl_dump_params *p, void *data) +{ + struct inet_data *id = data; + char buf[64]; + int i, n = 0; + + nl_dump_line(p, " ipv4 devconf:\n"); + nl_dump_line(p, " "); + + for (i = 0; i < IPV4_DEVCONF_MAX; i++) { + nl_dump_line(p, "%-19s %3u", + rtnl_link_inet_devconf2str(i+1, buf, sizeof(buf)), + id->i_confset[i] ? id->i_conf[i] : 0); + + if (++n == 3) { + nl_dump(p, "\n"); + nl_dump_line(p, " "); + n = 0; + } else + nl_dump(p, " "); + } + + if (n != 0) + nl_dump(p, "\n"); +} + +static struct rtnl_link_af_ops inet_ops = { + .ao_family = AF_INET, + .ao_alloc = &inet_alloc, + .ao_clone = &inet_clone, + .ao_free = &inet_free, + .ao_parse_af = &inet_parse_af, + .ao_fill_af = &inet_fill_af, + .ao_dump[NL_DUMP_DETAILS] = &inet_dump_details, +}; + +/** + * Get value of a ipv4 link configuration setting + * @arg link Link object + * @arg cfgid Configuration identifier + * @arg res Result pointer + * + * Stores the value of the specified configuration setting in the provided + * result pointer. + * + * @return 0 on success or a negative error code. + * @return -NLE_RANGE cfgid is out of range, 1..IPV4_DEVCONF_MAX + * @return -NLE_NOATTR configuration setting not available + * @return -NLE_INVAL cfgid not set. If the link was received via netlink, + * it means that the cfgid is not supported. + */ +int rtnl_link_inet_get_conf(struct rtnl_link *link, const unsigned int cfgid, + uint32_t *res) +{ + struct inet_data *id; + + if (cfgid == 0 || cfgid > IPV4_DEVCONF_MAX) + return -NLE_RANGE; + + if (!(id = rtnl_link_af_data(link, &inet_ops))) + return -NLE_NOATTR; + + if (!id->i_confset[cfgid - 1]) + return -NLE_INVAL; + *res = id->i_conf[cfgid - 1]; + + return 0; +} + +/** + * Change value of a ipv4 link configuration setting + * @arg link Link object + * @arg cfgid Configuration identifier + * @arg value New value + * + * Changes the value in the per link ipv4 configuration array. + * + * @return 0 on success or a negative error code. + * @return -NLE_RANGE cfgid is out of range, 1..IPV4_DEVCONF_MAX + * @return -NLE_NOMEM memory allocation failed + */ +int rtnl_link_inet_set_conf(struct rtnl_link *link, const unsigned int cfgid, + uint32_t value) +{ + struct inet_data *id; + + if (!(id = rtnl_link_af_alloc(link, &inet_ops))) + return -NLE_NOMEM; + + if (cfgid == 0 || cfgid > IPV4_DEVCONF_MAX) + return -NLE_RANGE; + + id->i_confset[cfgid - 1] = 1; + id->i_conf[cfgid - 1] = value; + + return 0; +} + + +static void _nl_init inet_init(void) +{ + rtnl_link_af_register(&inet_ops); +} + +static void _nl_exit inet_exit(void) +{ + rtnl_link_af_unregister(&inet_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/inet6.c b/libnl/lib/route/link/inet6.c new file mode 100644 index 0000000..87b057f --- /dev/null +++ b/libnl/lib/route/link/inet6.c @@ -0,0 +1,740 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#include "nl-default.h" + +#include +#include + +#include +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" +#include "nl-priv-static-route/nl-priv-static-route.h" + +#define I6_ADDR_GEN_MODE_UNKNOWN UINT8_MAX + +struct inet6_data +{ + uint32_t i6_flags; + struct ifla_cacheinfo i6_cacheinfo; + uint32_t i6_conf[DEVCONF_MAX]; + struct in6_addr i6_token; + uint8_t i6_conf_len; + uint8_t i6_addr_gen_mode; +}; + +static void *inet6_alloc(struct rtnl_link *link) +{ + struct inet6_data *i6; + + i6 = calloc(1, sizeof(struct inet6_data)); + if (i6) + i6->i6_addr_gen_mode = I6_ADDR_GEN_MODE_UNKNOWN; + + return i6; +} + +static void *inet6_clone(struct rtnl_link *link, void *data) +{ + struct inet6_data *i6; + + if ((i6 = inet6_alloc(link))) + memcpy(i6, data, sizeof(*i6)); + + return i6; +} + +static void inet6_free(struct rtnl_link *link, void *data) +{ + free(data); +} + +static struct nla_policy inet6_policy[IFLA_INET6_MAX+1] = { + [IFLA_INET6_FLAGS] = { .type = NLA_U32 }, + [IFLA_INET6_CACHEINFO] = { .minlen = sizeof(struct ifla_cacheinfo) }, + [IFLA_INET6_CONF] = { .minlen = 4 }, + [IFLA_INET6_STATS] = { .minlen = 8 }, + [IFLA_INET6_ICMP6STATS] = { .minlen = 8 }, + [IFLA_INET6_TOKEN] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_INET6_ADDR_GEN_MODE] = { .type = NLA_U8 }, +}; + +static const uint8_t map_stat_id_from_IPSTATS_MIB_v1[__IPSTATS_MIB_MAX] = { + /* 14a196807482e6fc74f15fc03176d5c08880588f^:include/linux/snmp.h + * version before the API change in commit 14a196807482e6fc74f15fc03176d5c08880588f. + * This version was valid since commit edf391ff17232f097d72441c9ad467bcb3b5db18, which + * predates support for parsing IFLA_PROTINFO in libnl3. Such an even older meaning of + * the flags is not supported in libnl3. */ + [ 1] = RTNL_LINK_IP6_INPKTS, /* IPSTATS_MIB_INPKTS */ + [ 2] = RTNL_LINK_IP6_INHDRERRORS, /* IPSTATS_MIB_INHDRERRORS */ + [ 3] = RTNL_LINK_IP6_INTOOBIGERRORS, /* IPSTATS_MIB_INTOOBIGERRORS */ + [ 4] = RTNL_LINK_IP6_INNOROUTES, /* IPSTATS_MIB_INNOROUTES */ + [ 5] = RTNL_LINK_IP6_INADDRERRORS, /* IPSTATS_MIB_INADDRERRORS */ + [ 6] = RTNL_LINK_IP6_INUNKNOWNPROTOS, /* IPSTATS_MIB_INUNKNOWNPROTOS */ + [ 7] = RTNL_LINK_IP6_INTRUNCATEDPKTS, /* IPSTATS_MIB_INTRUNCATEDPKTS */ + [ 8] = RTNL_LINK_IP6_INDISCARDS, /* IPSTATS_MIB_INDISCARDS */ + [ 9] = RTNL_LINK_IP6_INDELIVERS, /* IPSTATS_MIB_INDELIVERS */ + [10] = RTNL_LINK_IP6_OUTFORWDATAGRAMS, /* IPSTATS_MIB_OUTFORWDATAGRAMS */ + [11] = RTNL_LINK_IP6_OUTPKTS, /* IPSTATS_MIB_OUTPKTS */ + [12] = RTNL_LINK_IP6_OUTDISCARDS, /* IPSTATS_MIB_OUTDISCARDS */ + [13] = RTNL_LINK_IP6_OUTNOROUTES, /* IPSTATS_MIB_OUTNOROUTES */ + [14] = RTNL_LINK_IP6_REASMTIMEOUT, /* IPSTATS_MIB_REASMTIMEOUT */ + [15] = RTNL_LINK_IP6_REASMREQDS, /* IPSTATS_MIB_REASMREQDS */ + [16] = RTNL_LINK_IP6_REASMOKS, /* IPSTATS_MIB_REASMOKS */ + [17] = RTNL_LINK_IP6_REASMFAILS, /* IPSTATS_MIB_REASMFAILS */ + [18] = RTNL_LINK_IP6_FRAGOKS, /* IPSTATS_MIB_FRAGOKS */ + [19] = RTNL_LINK_IP6_FRAGFAILS, /* IPSTATS_MIB_FRAGFAILS */ + [20] = RTNL_LINK_IP6_FRAGCREATES, /* IPSTATS_MIB_FRAGCREATES */ + [21] = RTNL_LINK_IP6_INMCASTPKTS, /* IPSTATS_MIB_INMCASTPKTS */ + [22] = RTNL_LINK_IP6_OUTMCASTPKTS, /* IPSTATS_MIB_OUTMCASTPKTS */ + [23] = RTNL_LINK_IP6_INBCASTPKTS, /* IPSTATS_MIB_INBCASTPKTS */ + [24] = RTNL_LINK_IP6_OUTBCASTPKTS, /* IPSTATS_MIB_OUTBCASTPKTS */ + [25] = RTNL_LINK_IP6_INOCTETS, /* IPSTATS_MIB_INOCTETS */ + [26] = RTNL_LINK_IP6_OUTOCTETS, /* IPSTATS_MIB_OUTOCTETS */ + [27] = RTNL_LINK_IP6_INMCASTOCTETS, /* IPSTATS_MIB_INMCASTOCTETS */ + [28] = RTNL_LINK_IP6_OUTMCASTOCTETS, /* IPSTATS_MIB_OUTMCASTOCTETS */ + [29] = RTNL_LINK_IP6_INBCASTOCTETS, /* IPSTATS_MIB_INBCASTOCTETS */ + [30] = RTNL_LINK_IP6_OUTBCASTOCTETS, /* IPSTATS_MIB_OUTBCASTOCTETS */ +}; + +static const uint8_t map_stat_id_from_IPSTATS_MIB_v2[__IPSTATS_MIB_MAX] = { + /* d8ec26d7f8287f5788a494f56e8814210f0e64be:include/uapi/linux/snmp.h + * version since the API change in commit 14a196807482e6fc74f15fc03176d5c08880588f */ + [ 1] = RTNL_LINK_IP6_INPKTS, /* IPSTATS_MIB_INPKTS */ + [ 2] = RTNL_LINK_IP6_INOCTETS, /* IPSTATS_MIB_INOCTETS */ + [ 3] = RTNL_LINK_IP6_INDELIVERS, /* IPSTATS_MIB_INDELIVERS */ + [ 4] = RTNL_LINK_IP6_OUTFORWDATAGRAMS, /* IPSTATS_MIB_OUTFORWDATAGRAMS */ + [ 5] = RTNL_LINK_IP6_OUTPKTS, /* IPSTATS_MIB_OUTPKTS */ + [ 6] = RTNL_LINK_IP6_OUTOCTETS, /* IPSTATS_MIB_OUTOCTETS */ + [ 7] = RTNL_LINK_IP6_INHDRERRORS, /* IPSTATS_MIB_INHDRERRORS */ + [ 8] = RTNL_LINK_IP6_INTOOBIGERRORS, /* IPSTATS_MIB_INTOOBIGERRORS */ + [ 9] = RTNL_LINK_IP6_INNOROUTES, /* IPSTATS_MIB_INNOROUTES */ + [10] = RTNL_LINK_IP6_INADDRERRORS, /* IPSTATS_MIB_INADDRERRORS */ + [11] = RTNL_LINK_IP6_INUNKNOWNPROTOS, /* IPSTATS_MIB_INUNKNOWNPROTOS */ + [12] = RTNL_LINK_IP6_INTRUNCATEDPKTS, /* IPSTATS_MIB_INTRUNCATEDPKTS */ + [13] = RTNL_LINK_IP6_INDISCARDS, /* IPSTATS_MIB_INDISCARDS */ + [14] = RTNL_LINK_IP6_OUTDISCARDS, /* IPSTATS_MIB_OUTDISCARDS */ + [15] = RTNL_LINK_IP6_OUTNOROUTES, /* IPSTATS_MIB_OUTNOROUTES */ + [16] = RTNL_LINK_IP6_REASMTIMEOUT, /* IPSTATS_MIB_REASMTIMEOUT */ + [17] = RTNL_LINK_IP6_REASMREQDS, /* IPSTATS_MIB_REASMREQDS */ + [18] = RTNL_LINK_IP6_REASMOKS, /* IPSTATS_MIB_REASMOKS */ + [19] = RTNL_LINK_IP6_REASMFAILS, /* IPSTATS_MIB_REASMFAILS */ + [20] = RTNL_LINK_IP6_FRAGOKS, /* IPSTATS_MIB_FRAGOKS */ + [21] = RTNL_LINK_IP6_FRAGFAILS, /* IPSTATS_MIB_FRAGFAILS */ + [22] = RTNL_LINK_IP6_FRAGCREATES, /* IPSTATS_MIB_FRAGCREATES */ + [23] = RTNL_LINK_IP6_INMCASTPKTS, /* IPSTATS_MIB_INMCASTPKTS */ + [24] = RTNL_LINK_IP6_OUTMCASTPKTS, /* IPSTATS_MIB_OUTMCASTPKTS */ + [25] = RTNL_LINK_IP6_INBCASTPKTS, /* IPSTATS_MIB_INBCASTPKTS */ + [26] = RTNL_LINK_IP6_OUTBCASTPKTS, /* IPSTATS_MIB_OUTBCASTPKTS */ + [27] = RTNL_LINK_IP6_INMCASTOCTETS, /* IPSTATS_MIB_INMCASTOCTETS */ + [28] = RTNL_LINK_IP6_OUTMCASTOCTETS, /* IPSTATS_MIB_OUTMCASTOCTETS */ + [29] = RTNL_LINK_IP6_INBCASTOCTETS, /* IPSTATS_MIB_INBCASTOCTETS */ + [30] = RTNL_LINK_IP6_OUTBCASTOCTETS, /* IPSTATS_MIB_OUTBCASTOCTETS */ + [31] = RTNL_LINK_IP6_CSUMERRORS, /* IPSTATS_MIB_CSUMERRORS */ + [32] = RTNL_LINK_IP6_NOECTPKTS, /* IPSTATS_MIB_NOECTPKTS */ + [33] = RTNL_LINK_IP6_ECT1PKTS, /* IPSTATS_MIB_ECT1PKTS */ + [34] = RTNL_LINK_IP6_ECT0PKTS, /* IPSTATS_MIB_ECT0PKTS */ + [35] = RTNL_LINK_IP6_CEPKTS, /* IPSTATS_MIB_CEPKTS */ + [36] = RTNL_LINK_REASM_OVERLAPS, /* IPSTATS_MIB_REASM_OVERLAPS */ +}; + +const uint8_t *const _nltst_map_stat_id_from_IPSTATS_MIB_v2 = map_stat_id_from_IPSTATS_MIB_v2; + +static int inet6_parse_protinfo(struct rtnl_link *link, struct nlattr *attr, + void *data) +{ + struct inet6_data *i6 = data; + struct nlattr *tb[IFLA_INET6_MAX+1]; + int err; + + err = nla_parse_nested(tb, IFLA_INET6_MAX, attr, inet6_policy); + if (err < 0) + return err; + if (tb[IFLA_INET6_CONF] && nla_len(tb[IFLA_INET6_CONF]) % 4) + return -EINVAL; + if (tb[IFLA_INET6_STATS] && nla_len(tb[IFLA_INET6_STATS]) % 8) + return -EINVAL; + if (tb[IFLA_INET6_ICMP6STATS] && nla_len(tb[IFLA_INET6_ICMP6STATS]) % 8) + return -EINVAL; + + if (tb[IFLA_INET6_FLAGS]) + i6->i6_flags = nla_get_u32(tb[IFLA_INET6_FLAGS]); + + if (tb[IFLA_INET6_CACHEINFO]) + nla_memcpy(&i6->i6_cacheinfo, tb[IFLA_INET6_CACHEINFO], + sizeof(i6->i6_cacheinfo)); + + if (tb[IFLA_INET6_CONF]) { + i6->i6_conf_len = _NL_MIN(ARRAY_SIZE(i6->i6_conf), + nla_len(tb[IFLA_INET6_CONF]) / + sizeof(i6->i6_conf[0])); + nla_memcpy(&i6->i6_conf, tb[IFLA_INET6_CONF], + sizeof(i6->i6_conf[0]) * i6->i6_conf_len); + } + + if (tb[IFLA_INET6_TOKEN]) + nla_memcpy(&i6->i6_token, tb[IFLA_INET6_TOKEN], + sizeof(struct in6_addr)); + + if (tb[IFLA_INET6_ADDR_GEN_MODE]) + i6->i6_addr_gen_mode = nla_get_u8 (tb[IFLA_INET6_ADDR_GEN_MODE]); + + /* + * Due to 32bit data alignment, these addresses must be copied to an + * aligned location prior to access. + */ + if (tb[IFLA_INET6_STATS]) { + unsigned char *cnt = nla_data(tb[IFLA_INET6_STATS]); + uint64_t stat; + int i; + int len = nla_len(tb[IFLA_INET6_STATS]) / 8; + const uint8_t *map_stat_id = map_stat_id_from_IPSTATS_MIB_v2; + + if (len < 32 || + (tb[IFLA_INET6_ICMP6STATS] && nla_len(tb[IFLA_INET6_ICMP6STATS]) < 6)) { + /* kernel commit 14a196807482e6fc74f15fc03176d5c08880588f reordered the values. + * The later commit 6a5dc9e598fe90160fee7de098fa319665f5253e added values + * IPSTATS_MIB_CSUMERRORS/ICMP6_MIB_CSUMERRORS. If the netlink is shorter + * then this, assume that the kernel uses the previous meaning of the + * enumeration. */ + map_stat_id = map_stat_id_from_IPSTATS_MIB_v1; + } + + len = _NL_MIN(__IPSTATS_MIB_MAX, len); + for (i = 1; i < len; i++) { + memcpy(&stat, &cnt[i * sizeof(stat)], sizeof(stat)); + rtnl_link_set_stat(link, map_stat_id[i], stat); + } + } + + if (tb[IFLA_INET6_ICMP6STATS]) { +#define _NL_ICMP6_MIB_MAX 6 + unsigned char *cnt = nla_data(tb[IFLA_INET6_ICMP6STATS]); + uint64_t stat; + int i; + int len = _NL_MIN(_NL_ICMP6_MIB_MAX, nla_len(tb[IFLA_INET6_ICMP6STATS]) / 8); + + _NL_STATIC_ASSERT (__ICMP6_MIB_MAX >= _NL_ICMP6_MIB_MAX); + _NL_STATIC_ASSERT (RTNL_LINK_ICMP6_CSUMERRORS - RTNL_LINK_ICMP6_INMSGS + 1 == 5); + + for (i = 1; i < len; i++) { + memcpy(&stat, &cnt[i * sizeof(stat)], sizeof(stat)); + rtnl_link_set_stat(link, RTNL_LINK_ICMP6_INMSGS + i - 1, + stat); + } + } + + return 0; +} + +static int inet6_fill_af(struct rtnl_link *link, struct nl_msg *msg, void *data) +{ + struct inet6_data *id = data; + + if (id->i6_addr_gen_mode != I6_ADDR_GEN_MODE_UNKNOWN) + NLA_PUT_U8(msg, IFLA_INET6_ADDR_GEN_MODE, id->i6_addr_gen_mode); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +/* These live in include/net/if_inet6.h and should be moved to include/linux */ +#define IF_RA_OTHERCONF 0x80 +#define IF_RA_MANAGED 0x40 +#define IF_RA_RCVD 0x20 +#define IF_RS_SENT 0x10 +#define IF_READY 0x80000000 + +static const struct trans_tbl inet6_flags[] = { + __ADD(IF_RA_OTHERCONF, ra_otherconf), + __ADD(IF_RA_MANAGED, ra_managed), + __ADD(IF_RA_RCVD, ra_rcvd), + __ADD(IF_RS_SENT, rs_sent), + __ADD(IF_READY, ready), +}; + +char *rtnl_link_inet6_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, inet6_flags, + ARRAY_SIZE(inet6_flags)); +} + +int rtnl_link_inet6_str2flags(const char *name) +{ + return __str2flags(name, inet6_flags, ARRAY_SIZE(inet6_flags)); +} + +static const struct trans_tbl inet6_devconf[] = { + __ADD(DEVCONF_FORWARDING, forwarding), + __ADD(DEVCONF_HOPLIMIT, hoplimit), + __ADD(DEVCONF_MTU6, mtu6), + __ADD(DEVCONF_ACCEPT_RA, accept_ra), + __ADD(DEVCONF_ACCEPT_REDIRECTS, accept_redirects), + __ADD(DEVCONF_AUTOCONF, autoconf), + __ADD(DEVCONF_DAD_TRANSMITS, dad_transmits), + __ADD(DEVCONF_RTR_SOLICITS, rtr_solicits), + __ADD(DEVCONF_RTR_SOLICIT_INTERVAL, rtr_solicit_interval), + __ADD(DEVCONF_RTR_SOLICIT_DELAY, rtr_solicit_delay), + __ADD(DEVCONF_USE_TEMPADDR, use_tempaddr), + __ADD(DEVCONF_TEMP_VALID_LFT, temp_valid_lft), + __ADD(DEVCONF_TEMP_PREFERED_LFT, temp_prefered_lft), + __ADD(DEVCONF_REGEN_MAX_RETRY, regen_max_retry), + __ADD(DEVCONF_MAX_DESYNC_FACTOR, max_desync_factor), + __ADD(DEVCONF_MAX_ADDRESSES, max_addresses), + __ADD(DEVCONF_FORCE_MLD_VERSION, force_mld_version), + __ADD(DEVCONF_ACCEPT_RA_DEFRTR, accept_ra_defrtr), + __ADD(DEVCONF_ACCEPT_RA_PINFO, accept_ra_pinfo), + __ADD(DEVCONF_ACCEPT_RA_RTR_PREF, accept_ra_rtr_pref), + __ADD(DEVCONF_RTR_PROBE_INTERVAL, rtr_probe_interval), + __ADD(DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN, accept_ra_rt_info), + __ADD(DEVCONF_PROXY_NDP, proxy_ndp), + __ADD(DEVCONF_OPTIMISTIC_DAD, optimistic_dad), + __ADD(DEVCONF_ACCEPT_SOURCE_ROUTE, accept_source_route), + __ADD(DEVCONF_MC_FORWARDING, mc_forwarding), + __ADD(DEVCONF_DISABLE_IPV6, disable_ipv6), + __ADD(DEVCONF_ACCEPT_DAD, accept_dad), + __ADD(DEVCONF_FORCE_TLLAO, force_tllao), +}; + +static char *inet6_devconf2str(int type, char *buf, size_t len) +{ + return __type2str(type, buf, len, inet6_devconf, + ARRAY_SIZE(inet6_devconf)); +} + +static const struct trans_tbl inet6_addr_gen_mode[] = { + __ADD(IN6_ADDR_GEN_MODE_EUI64, eui64), + __ADD(IN6_ADDR_GEN_MODE_NONE, none), + __ADD(IN6_ADDR_GEN_MODE_STABLE_PRIVACY, stable_privacy), +}; + +const char *rtnl_link_inet6_addrgenmode2str(uint8_t mode, char *buf, size_t len) +{ + return __type2str(mode, buf, len, inet6_addr_gen_mode, + ARRAY_SIZE(inet6_addr_gen_mode)); +} + +uint8_t rtnl_link_inet6_str2addrgenmode(const char *mode) +{ + return (uint8_t) __str2type(mode, inet6_addr_gen_mode, + ARRAY_SIZE(inet6_addr_gen_mode)); +} + +static void inet6_dump_details(struct rtnl_link *link, + struct nl_dump_params *p, void *data) +{ + struct inet6_data *i6 = data; + struct nl_addr *addr; + int i, n = 0; + char buf[64]; + + nl_dump_line(p, " ipv6 max-reasm-len %s", + nl_size2str(i6->i6_cacheinfo.max_reasm_len, buf, sizeof(buf))); + + nl_dump(p, " <%s>\n", + rtnl_link_inet6_flags2str(i6->i6_flags, buf, sizeof(buf))); + + nl_dump_line(p, " create-stamp %.2fs reachable-time %s", + (double) i6->i6_cacheinfo.tstamp / 100., + nl_msec2str(i6->i6_cacheinfo.reachable_time, buf, sizeof(buf))); + + nl_dump(p, " retrans-time %s\n", + nl_msec2str(i6->i6_cacheinfo.retrans_time, buf, sizeof(buf))); + + addr = nl_addr_build(AF_INET6, &i6->i6_token, sizeof(i6->i6_token)); + nl_dump(p, " token %s\n", + nl_addr2str(addr, buf, sizeof(buf))); + nl_addr_put(addr); + + nl_dump(p, " link-local address mode %s\n", + rtnl_link_inet6_addrgenmode2str(i6->i6_addr_gen_mode, + buf, sizeof(buf))); + + nl_dump_line(p, " devconf:\n"); + nl_dump_line(p, " "); + + for (i = 0; i < (int) i6->i6_conf_len; i++) { + char buf2[64]; + uint32_t value = i6->i6_conf[i]; + int x, offset; + + switch (i) { + case DEVCONF_TEMP_VALID_LFT: + case DEVCONF_TEMP_PREFERED_LFT: + nl_msec2str((uint64_t) value * 1000., buf2, sizeof(buf2)); + break; + + case DEVCONF_RTR_PROBE_INTERVAL: + case DEVCONF_RTR_SOLICIT_INTERVAL: + case DEVCONF_RTR_SOLICIT_DELAY: + nl_msec2str(value, buf2, sizeof(buf2)); + break; + + default: + snprintf(buf2, sizeof(buf2), "%u", value); + break; + } + + inet6_devconf2str(i, buf, sizeof(buf)); + + offset = 23 - strlen(buf2); + if (offset < 0) + offset = 0; + + for (x = strlen(buf); x < offset; x++) + buf[x] = ' '; + + _nl_strncpy_trunc(&buf[offset], buf2, sizeof(buf) - offset); + + nl_dump_line(p, "%s", buf); + + if (++n == 3) { + nl_dump(p, "\n"); + nl_dump_line(p, " "); + n = 0; + } else + nl_dump(p, " "); + } + + if (n != 0) + nl_dump(p, "\n"); +} + +static void inet6_dump_stats(struct rtnl_link *link, + struct nl_dump_params *p, void *data) +{ + double octets; + char *octetsUnit; + + nl_dump(p, " IPv6: InPkts InOctets " + " InDiscards InDelivers\n"); + nl_dump(p, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INPKTS]); + + octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INOCTETS], + &octetsUnit); + if (octets) + nl_dump(p, "%14.2f %3s ", octets, octetsUnit); + else + nl_dump(p, "%16u B ", 0); + + nl_dump(p, "%18" PRIu64 " %18" PRIu64 "\n", + link->l_stats[RTNL_LINK_IP6_INDISCARDS], + link->l_stats[RTNL_LINK_IP6_INDELIVERS]); + + nl_dump(p, " OutPkts OutOctets " + " OutDiscards OutForwards\n"); + + nl_dump(p, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTPKTS]); + + octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTOCTETS], + &octetsUnit); + if (octets) + nl_dump(p, "%14.2f %3s ", octets, octetsUnit); + else + nl_dump(p, "%16u B ", 0); + + nl_dump(p, "%18" PRIu64 " %18" PRIu64 "\n", + link->l_stats[RTNL_LINK_IP6_OUTDISCARDS], + link->l_stats[RTNL_LINK_IP6_OUTFORWDATAGRAMS]); + + nl_dump(p, " InMcastPkts InMcastOctets " + " InBcastPkts InBcastOctests\n"); + + nl_dump(p, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INMCASTPKTS]); + + octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INMCASTOCTETS], + &octetsUnit); + if (octets) + nl_dump(p, "%14.2f %3s ", octets, octetsUnit); + else + nl_dump(p, "%16u B ", 0); + + nl_dump(p, "%18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INBCASTPKTS]); + octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INBCASTOCTETS], + &octetsUnit); + if (octets) + nl_dump(p, "%14.2f %3s\n", octets, octetsUnit); + else + nl_dump(p, "%16u B\n", 0); + + nl_dump(p, " OutMcastPkts OutMcastOctets " + " OutBcastPkts OutBcastOctests\n"); + + nl_dump(p, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTMCASTPKTS]); + + octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTMCASTOCTETS], + &octetsUnit); + if (octets) + nl_dump(p, "%14.2f %3s ", octets, octetsUnit); + else + nl_dump(p, "%16u B ", 0); + + nl_dump(p, "%18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTBCASTPKTS]); + octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTBCASTOCTETS], + &octetsUnit); + if (octets) + nl_dump(p, "%14.2f %3s\n", octets, octetsUnit); + else + nl_dump(p, "%16u B\n", 0); + + nl_dump(p, " ReasmOKs ReasmFails " + " ReasmReqds ReasmTimeout\n"); + nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", + link->l_stats[RTNL_LINK_IP6_REASMOKS], + link->l_stats[RTNL_LINK_IP6_REASMFAILS], + link->l_stats[RTNL_LINK_IP6_REASMREQDS], + link->l_stats[RTNL_LINK_IP6_REASMTIMEOUT]); + + nl_dump(p, " FragOKs FragFails " + " FragCreates\n"); + nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", + link->l_stats[RTNL_LINK_IP6_FRAGOKS], + link->l_stats[RTNL_LINK_IP6_FRAGFAILS], + link->l_stats[RTNL_LINK_IP6_FRAGCREATES]); + + nl_dump(p, " InHdrErrors InTooBigErrors " + " InNoRoutes InAddrErrors\n"); + nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", + link->l_stats[RTNL_LINK_IP6_INHDRERRORS], + link->l_stats[RTNL_LINK_IP6_INTOOBIGERRORS], + link->l_stats[RTNL_LINK_IP6_INNOROUTES], + link->l_stats[RTNL_LINK_IP6_INADDRERRORS]); + + nl_dump(p, " InUnknownProtos InTruncatedPkts " + " OutNoRoutes InCsumErrors\n"); + nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", + link->l_stats[RTNL_LINK_IP6_INUNKNOWNPROTOS], + link->l_stats[RTNL_LINK_IP6_INTRUNCATEDPKTS], + link->l_stats[RTNL_LINK_IP6_OUTNOROUTES], + link->l_stats[RTNL_LINK_IP6_CSUMERRORS]); + + nl_dump(p, " InNoECTPkts InECT1Pkts " + " InECT0Pkts InCEPkts\n"); + nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", + link->l_stats[RTNL_LINK_IP6_NOECTPKTS], + link->l_stats[RTNL_LINK_IP6_ECT1PKTS], + link->l_stats[RTNL_LINK_IP6_ECT0PKTS], + link->l_stats[RTNL_LINK_IP6_CEPKTS]); + + nl_dump(p, " ICMPv6: InMsgs InErrors " + " OutMsgs OutErrors InCsumErrors\n"); + nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", + link->l_stats[RTNL_LINK_ICMP6_INMSGS], + link->l_stats[RTNL_LINK_ICMP6_INERRORS], + link->l_stats[RTNL_LINK_ICMP6_OUTMSGS], + link->l_stats[RTNL_LINK_ICMP6_OUTERRORS], + link->l_stats[RTNL_LINK_ICMP6_CSUMERRORS]); +} + +static const struct nla_policy protinfo_policy = { + .type = NLA_NESTED, +}; + +static struct rtnl_link_af_ops inet6_ops = { + .ao_family = AF_INET6, + .ao_alloc = &inet6_alloc, + .ao_clone = &inet6_clone, + .ao_free = &inet6_free, + .ao_parse_protinfo = &inet6_parse_protinfo, + .ao_parse_af = &inet6_parse_protinfo, + .ao_fill_af = &inet6_fill_af, + .ao_dump[NL_DUMP_DETAILS] = &inet6_dump_details, + .ao_dump[NL_DUMP_STATS] = &inet6_dump_stats, + .ao_protinfo_policy = &protinfo_policy, +}; + +/** + * Return IPv6 specific flags + * @arg link Link object + * @arg out_flags Flags on success + * + * Returns the link's IPv6 flags. + * + * @return 0 on success + * @return -NLE_NOATTR configuration setting not available + */ +int rtnl_link_inet6_get_flags(struct rtnl_link *link, uint32_t* out_flags) +{ + struct inet6_data *id = NULL; + + if (!(id = rtnl_link_af_data(link, &inet6_ops))) + return -NLE_NOATTR; + + *out_flags = id->i6_flags; + return 0; +} + +/** + * Set IPv6 specific flags + * @arg link Link object + * @arg flags Flags to set + * + * Sets the link's IPv6 specific flags. Overwrites currently set flags. + * + * @return 0 on success + * @return -NLE_NOMEM could not allocate inet6 data + */ +int rtnl_link_inet6_set_flags(struct rtnl_link *link, uint32_t flags) +{ + struct inet6_data *id; + + if (!(id = rtnl_link_af_alloc(link, &inet6_ops))) + return -NLE_NOMEM; + + id->i6_flags = flags; + return 0; +} + +/** + * Get IPv6 tokenized interface identifier + * @arg link Link object + * @arg token Tokenized interface identifier on success + * + * Returns the link's IPv6 tokenized interface identifier. + * + * @return 0 on success + * @return -NLE_NOMEM failure to allocate struct nl_addr result + * @return -NLE_NOATTR configuration setting not available + * @return -NLE_NOADDR tokenized interface identifier is not set + */ +int rtnl_link_inet6_get_token(struct rtnl_link *link, struct nl_addr **addr) +{ + struct inet6_data *id; + + if (!(id = rtnl_link_af_data(link, &inet6_ops))) + return -NLE_NOATTR; + + *addr = nl_addr_build(AF_INET6, &id->i6_token, sizeof(id->i6_token)); + if (!*addr) + return -NLE_NOMEM; + if (nl_addr_iszero(*addr)) { + nl_addr_put(*addr); + *addr = NULL; + return -NLE_NOADDR; + } + + return 0; +} + +/** + * Set IPv6 tokenized interface identifier + * @arg link Link object + * @arg token Tokenized interface identifier + * + * Sets the link's IPv6 tokenized interface identifier. + * + * @return 0 on success + * @return -NLE_NOMEM could not allocate inet6 data + * @return -NLE_INVAL addr is not a valid inet6 address + */ +int rtnl_link_inet6_set_token(struct rtnl_link *link, struct nl_addr *addr) +{ + struct inet6_data *id; + + if ((nl_addr_get_family(addr) != AF_INET6) || + (nl_addr_get_len(addr) != sizeof(id->i6_token))) + return -NLE_INVAL; + + if (!(id = rtnl_link_af_alloc(link, &inet6_ops))) + return -NLE_NOMEM; + + memcpy(&id->i6_token, nl_addr_get_binary_addr(addr), + sizeof(id->i6_token)); + return 0; +} + +/** + * Get IPv6 link-local address generation mode + * @arg link Link object + * @arg mode Generation mode on success + * + * Returns the link's IPv6 link-local address generation mode. + * + * @return 0 on success + * @return -NLE_NOATTR configuration setting not available + * @return -NLE_INVAL generation mode unknown. If the link was received via + * netlink, it means that address generation mode is not + * supported by the kernel. + */ +int rtnl_link_inet6_get_addr_gen_mode(struct rtnl_link *link, uint8_t *mode) +{ + struct inet6_data *id; + + if (!(id = rtnl_link_af_data(link, &inet6_ops))) + return -NLE_NOATTR; + + if (id->i6_addr_gen_mode == I6_ADDR_GEN_MODE_UNKNOWN) + return -NLE_INVAL; + + *mode = id->i6_addr_gen_mode; + return 0; +} + +/** + * Set IPv6 link-local address generation mode + * @arg link Link object + * @arg mode Generation mode + * + * Sets the link's IPv6 link-local address generation mode. + * + * @return 0 on success + * @return -NLE_NOMEM could not allocate inet6 data + */ +int rtnl_link_inet6_set_addr_gen_mode(struct rtnl_link *link, uint8_t mode) +{ + struct inet6_data *id; + + if (!(id = rtnl_link_af_alloc(link, &inet6_ops))) + return -NLE_NOMEM; + + id->i6_addr_gen_mode = mode; + return 0; +} + +/** + * Get value of a ipv6 link configuration setting + * @arg link Link object + * @arg cfgid Configuration identifier + * @arg res Result pointer + * + * Stores the value of the specified configuration setting in the provided + * result pointer. + * + * @return 0 on success or a negative error code. + * @return -NLE_RANGE cfgid is out of range or not provided by kernel. + * @return -NLE_NOATTR configuration setting not available + */ +int rtnl_link_inet6_get_conf(struct rtnl_link *link, unsigned int cfgid, + uint32_t *res) +{ + struct inet6_data *id; + + if (!(id = rtnl_link_af_data(link, &inet6_ops))) + return -NLE_NOATTR; + + if (cfgid >= id->i6_conf_len) + return -NLE_RANGE; + + *res = id->i6_conf[cfgid]; + + return 0; +} + + +static void _nl_init inet6_init(void) +{ + rtnl_link_af_register(&inet6_ops); +} + +static void _nl_exit inet6_exit(void) +{ + rtnl_link_af_unregister(&inet6_ops); +} diff --git a/libnl/lib/route/link/ip6gre.c b/libnl/lib/route/link/ip6gre.c new file mode 100644 index 0000000..51d4609 --- /dev/null +++ b/libnl/lib/route/link/ip6gre.c @@ -0,0 +1,890 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +/** + * @ingroup link + * @defgroup ip6gre IP6GRE + * ip6gre link module + * + * @details + * \b Link Type Name: "ip6gre" + * + * @route_doc{link_ip6gre, IP6GRE Documentation} + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" + +#define IP6GRE_ATTR_LINK (1 << 0) +#define IP6GRE_ATTR_IFLAGS (1 << 1) +#define IP6GRE_ATTR_OFLAGS (1 << 2) +#define IP6GRE_ATTR_IKEY (1 << 3) +#define IP6GRE_ATTR_OKEY (1 << 4) +#define IP6GRE_ATTR_LOCAL (1 << 5) +#define IP6GRE_ATTR_REMOTE (1 << 6) +#define IP6GRE_ATTR_TTL (1 << 7) +#define IP6GRE_ATTR_ENCAPLIMIT (1 << 8) +#define IP6GRE_ATTR_FLOWINFO (1 << 9) +#define IP6GRE_ATTR_FLAGS (1 << 10) +#define IP6GRE_ATTR_FWMARK (1 << 11) + +struct ip6gre_info +{ + uint8_t ttl; + uint8_t encaplimit; + uint16_t iflags; + uint16_t oflags; + uint32_t ikey; + uint32_t okey; + uint32_t link; + uint32_t flowinfo; + uint32_t flags; + struct in6_addr local; + struct in6_addr remote; + uint32_t fwmark; + uint32_t ip6gre_mask; +}; + +static struct nla_policy ip6gre_policy[IFLA_GRE_MAX + 1] = { + [IFLA_GRE_LINK] = { .type = NLA_U32 }, + [IFLA_GRE_IFLAGS] = { .type = NLA_U16 }, + [IFLA_GRE_OFLAGS] = { .type = NLA_U16 }, + [IFLA_GRE_IKEY] = { .type = NLA_U32 }, + [IFLA_GRE_OKEY] = { .type = NLA_U32 }, + [IFLA_GRE_LOCAL] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_GRE_REMOTE] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_GRE_TTL] = { .type = NLA_U8 }, + [IFLA_GRE_ENCAP_LIMIT] = { .type = NLA_U8 }, + [IFLA_GRE_FLOWINFO] = { .type = NLA_U32 }, + [IFLA_GRE_FLAGS] = { .type = NLA_U32 }, + [IFLA_GRE_FWMARK] = { .type = NLA_U32 }, +}; + +static int ip6gre_alloc(struct rtnl_link *link) +{ + struct ip6gre_info *ip6gre; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*ip6gre)); + else { + ip6gre = calloc(1, sizeof(*ip6gre)); + if (!ip6gre) + return -NLE_NOMEM; + + link->l_info = ip6gre; + } + + return 0; +} + +static int ip6gre_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_GRE_MAX + 1]; + struct ip6gre_info *ip6gre; + int err; + + NL_DBG(3, "Parsing IP6GRE link info\n"); + + err = nla_parse_nested(tb, IFLA_GRE_MAX, data, ip6gre_policy); + if (err < 0) + goto errout; + + err = ip6gre_alloc(link); + if (err < 0) + goto errout; + + ip6gre = link->l_info; + + if (tb[IFLA_GRE_LINK]) { + ip6gre->link = nla_get_u32(tb[IFLA_GRE_LINK]); + ip6gre->ip6gre_mask |= IP6GRE_ATTR_LINK; + } + + if (tb[IFLA_GRE_IFLAGS]) { + ip6gre->iflags = nla_get_u16(tb[IFLA_GRE_IFLAGS]); + ip6gre->ip6gre_mask |= IP6GRE_ATTR_IFLAGS; + } + + if (tb[IFLA_GRE_OFLAGS]) { + ip6gre->oflags = nla_get_u16(tb[IFLA_GRE_OFLAGS]); + ip6gre->ip6gre_mask |= IP6GRE_ATTR_OFLAGS; + } + + if (tb[IFLA_GRE_IKEY]) { + ip6gre->ikey = nla_get_u32(tb[IFLA_GRE_IKEY]); + ip6gre->ip6gre_mask |= IP6GRE_ATTR_IKEY; + } + + if (tb[IFLA_GRE_OKEY]) { + ip6gre->okey = nla_get_u32(tb[IFLA_GRE_OKEY]); + ip6gre->ip6gre_mask |= IP6GRE_ATTR_OKEY; + } + + if (tb[IFLA_GRE_LOCAL]) { + nla_memcpy(&ip6gre->local, tb[IFLA_GRE_LOCAL], sizeof(struct in6_addr)); + ip6gre->ip6gre_mask |= IP6GRE_ATTR_LOCAL; + } + + if (tb[IFLA_GRE_REMOTE]) { + nla_memcpy(&ip6gre->remote, tb[IFLA_GRE_REMOTE], sizeof(struct in6_addr)); + ip6gre->ip6gre_mask |= IP6GRE_ATTR_REMOTE; + } + + if (tb[IFLA_GRE_TTL]) { + ip6gre->ttl = nla_get_u8(tb[IFLA_GRE_TTL]); + ip6gre->ip6gre_mask |= IP6GRE_ATTR_TTL; + } + + if (tb[IFLA_GRE_ENCAP_LIMIT]) { + ip6gre->encaplimit = nla_get_u8(tb[IFLA_GRE_ENCAP_LIMIT]); + ip6gre->ip6gre_mask |= IP6GRE_ATTR_ENCAPLIMIT; + } + + if (tb[IFLA_GRE_FLOWINFO]) { + ip6gre->flowinfo = nla_get_u32(tb[IFLA_GRE_FLOWINFO]); + ip6gre->ip6gre_mask |= IP6GRE_ATTR_FLOWINFO; + } + + if (tb[IFLA_GRE_FLAGS]) { + ip6gre->flags = nla_get_u32(tb[IFLA_GRE_FLAGS]); + ip6gre->ip6gre_mask |= IP6GRE_ATTR_FLAGS; + } + + if (tb[IFLA_GRE_FWMARK]) { + ip6gre->fwmark = nla_get_u32(tb[IFLA_GRE_FWMARK]); + ip6gre->ip6gre_mask |= IP6GRE_ATTR_FWMARK; + } + + err = 0; + + errout: + return err; +} + +static int ip6gre_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct ip6gre_info *ip6gre = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_GRE_LINK, ip6gre->link); + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_IFLAGS) + NLA_PUT_U16(msg, IFLA_GRE_IFLAGS, ip6gre->iflags); + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_OFLAGS) + NLA_PUT_U16(msg, IFLA_GRE_OFLAGS, ip6gre->oflags); + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_IKEY) + NLA_PUT_U32(msg, IFLA_GRE_IKEY, ip6gre->ikey); + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_OKEY) + NLA_PUT_U32(msg, IFLA_GRE_OKEY, ip6gre->okey); + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_LOCAL) + NLA_PUT(msg, IFLA_GRE_LOCAL, sizeof(struct in6_addr), &ip6gre->local); + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_REMOTE) + NLA_PUT(msg, IFLA_GRE_REMOTE, sizeof(struct in6_addr), &ip6gre->remote); + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_TTL) + NLA_PUT_U8(msg, IFLA_GRE_TTL, ip6gre->ttl); + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_ENCAPLIMIT) + NLA_PUT_U8(msg, IFLA_GRE_ENCAP_LIMIT, ip6gre->encaplimit); + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_FLOWINFO) + NLA_PUT_U32(msg, IFLA_GRE_FLOWINFO, ip6gre->flowinfo); + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_FLAGS) + NLA_PUT_U32(msg, IFLA_GRE_FLAGS, ip6gre->flags); + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_FWMARK) + NLA_PUT_U32(msg, IFLA_GRE_FWMARK, ip6gre->fwmark); + + nla_nest_end(msg, data); + + nla_put_failure: + + return 0; +} + +static void ip6gre_free(struct rtnl_link *link) +{ + struct ip6gre_info *ip6gre = link->l_info; + + free(ip6gre); + link->l_info = NULL; +} + +static void ip6gre_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + nl_dump(p, "ip6gre : %s", link->l_name); +} + +static void ip6gre_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct ip6gre_info *ip6gre = link->l_info; + char *name; + char addr[INET6_ADDRSTRLEN]; + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_LINK) { + nl_dump(p, " link "); + name = rtnl_link_get_name(link); + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", ip6gre->link); + } + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_IFLAGS) { + nl_dump(p, " iflags "); + nl_dump_line(p, "%x\n", ip6gre->iflags); + } + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_OFLAGS) { + nl_dump(p, " oflags "); + nl_dump_line(p, "%x\n", ip6gre->oflags); + } + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_IKEY) { + nl_dump(p, " ikey "); + nl_dump_line(p, "%x\n",ip6gre->ikey); + } + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_OKEY) { + nl_dump(p, " okey "); + nl_dump_line(p, "%x\n", ip6gre->okey); + } + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_LOCAL) { + nl_dump(p, " local "); + nl_dump_line(p, "%s\n", + _nl_inet_ntop(AF_INET6, &ip6gre->local, addr)); + } + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_REMOTE) { + nl_dump(p, " remote "); + nl_dump_line(p, "%s\n", + _nl_inet_ntop(AF_INET6, &ip6gre->remote, addr)); + } + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_TTL) { + nl_dump(p, " ttl "); + nl_dump_line(p, "%u\n", ip6gre->ttl); + } + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_ENCAPLIMIT) { + nl_dump(p, " encaplimit "); + nl_dump_line(p, "%u\n", ip6gre->encaplimit); + } + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_FLOWINFO) { + nl_dump(p, " flowinfo "); + nl_dump_line(p, "%x\n", ip6gre->flowinfo); + } + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_FLAGS) { + nl_dump(p, " flags "); + nl_dump_line(p, "%x\n", ip6gre->flags); + } + + if (ip6gre->ip6gre_mask & IP6GRE_ATTR_FWMARK) { + nl_dump(p, " fwmark "); + nl_dump_line(p, "%x\n", ip6gre->fwmark); + } +} + +static int ip6gre_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct ip6gre_info *ip6gre_dst, *ip6gre_src = src->l_info; + int err; + + dst->l_info = NULL; + + err = rtnl_link_set_type(dst, "ip6gre"); + if (err < 0) + return err; + + ip6gre_dst = dst->l_info; + + if (!ip6gre_dst || !ip6gre_src) + BUG(); + + memcpy(ip6gre_dst, ip6gre_src, sizeof(struct ip6gre_info)); + + return 0; +} + +static struct rtnl_link_info_ops ip6gre_info_ops = { + .io_name = "ip6gre", + .io_alloc = ip6gre_alloc, + .io_parse = ip6gre_parse, + .io_dump = { + [NL_DUMP_LINE] = ip6gre_dump_line, + [NL_DUMP_DETAILS] = ip6gre_dump_details, + }, + .io_clone = ip6gre_clone, + .io_put_attrs = ip6gre_put_attrs, + .io_free = ip6gre_free, +}; + +#define IS_IP6GRE_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &ip6gre_info_ops) { \ + APPBUG("Link is not a ip6gre link. set type \"ip6gre\" first.");\ + return -NLE_OPNOTSUPP; \ + } + +#define HAS_IP6GRE_ATTR_ASSERT(link,attr) \ + if (!((link)->ip6gre_mask & (attr))) \ + return -NLE_NOATTR; + +struct rtnl_link *rtnl_link_ip6gre_alloc(void) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_alloc(); + if (!link) + return NULL; + + err = rtnl_link_set_type(link, "ip6gre"); + if (err < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a IP6GRE link + * @arg link Link object + * + * @return True if link is a IP6GRE link, otherwise 0 is returned. + */ +int rtnl_link_is_ip6gre(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "ip6gre"); +} + +/** + * Create a new IP6GRE tunnel device + * @arg sock netlink socket + * @arg name name of the tunnel deviceL + * + * Creates a new ip6gre tunnel device in the kernel + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_add(struct nl_sock *sk, const char *name) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_ip6gre_alloc(); + if (!link) + return -NLE_NOMEM; + + if(name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + rtnl_link_put(link); + + return err; +} + +/** + * Set IP6GRE tunnel interface index + * @arg link Link object + * @arg index interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_set_link(struct rtnl_link *link, uint32_t index) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + ip6gre->link = index; + ip6gre->ip6gre_mask |= IP6GRE_ATTR_LINK; + + return 0; +} + +/** + * Get IP6GRE tunnel interface index + * @arg link Link object + * @arg index addr to fill in with the interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_get_link(struct rtnl_link *link, uint32_t *index) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + HAS_IP6GRE_ATTR_ASSERT(ip6gre, IP6GRE_ATTR_LINK); + + *index = ip6gre->link; + + return 0; +} + +/** + * Set IP6GRE tunnel set iflags + * @arg link Link object + * @arg iflags ip6gre iflags + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_set_iflags(struct rtnl_link *link, uint16_t iflags) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + ip6gre->iflags = iflags; + ip6gre->ip6gre_mask |= IP6GRE_ATTR_IFLAGS; + + return 0; +} + +/** + * Get IP6GRE tunnel iflags + * @arg link Link object + * @arg iflags addr to fill in with the iflags + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_get_iflags(struct rtnl_link *link, uint16_t *iflags) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + HAS_IP6GRE_ATTR_ASSERT(ip6gre, IP6GRE_ATTR_IFLAGS); + + *iflags = ip6gre->iflags; + + return 0; +} + +/** + * Set IP6GRE tunnel set oflags + * @arg link Link object + * @arg oflags ip6gre oflags + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_set_oflags(struct rtnl_link *link, uint16_t oflags) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + ip6gre->oflags = oflags; + ip6gre->ip6gre_mask |= IP6GRE_ATTR_OFLAGS; + + return 0; +} + +/** + * Get IP6GRE tunnel oflags + * @arg link Link object + * @arg oflags addr to fill in with the oflags + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_get_oflags(struct rtnl_link *link, uint16_t *oflags) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + HAS_IP6GRE_ATTR_ASSERT(ip6gre, IP6GRE_ATTR_OFLAGS); + + *oflags = ip6gre->oflags; + + return 0; +} + +/** + * Set IP6GRE tunnel set ikey + * @arg link Link object + * @arg ikey ip6gre ikey + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_set_ikey(struct rtnl_link *link, uint32_t ikey) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + ip6gre->ikey = ikey; + ip6gre->ip6gre_mask |= IP6GRE_ATTR_IKEY; + + return 0; +} + +/** + * Get IP6GRE tunnel ikey + * @arg link Link object + * @arg ikey addr to fill in with the ikey + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_get_ikey(struct rtnl_link *link, uint32_t *ikey) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + HAS_IP6GRE_ATTR_ASSERT(ip6gre, IP6GRE_ATTR_IKEY); + + *ikey = ip6gre->ikey; + + return 0; +} + +/** + * Set IP6GRE tunnel set okey + * @arg link Link object + * @arg okey ip6gre okey + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_set_okey(struct rtnl_link *link, uint32_t okey) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + ip6gre->okey = okey; + ip6gre->ip6gre_mask |= IP6GRE_ATTR_OKEY; + + return 0; +} + +/** + * Get IP6GRE tunnel okey + * @arg link Link object + * @arg okey addr to fill in with the okey + * + * @return okey value + */ +int rtnl_link_ip6gre_get_okey(struct rtnl_link *link, uint32_t *okey) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + HAS_IP6GRE_ATTR_ASSERT(ip6gre, IP6GRE_ATTR_OKEY); + + *okey = ip6gre->okey; + + return 0; +} + +/** + * Set IP6GRE tunnel local address + * @arg link Link object + * @arg local local address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_set_local(struct rtnl_link *link, struct in6_addr *local) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + memcpy(&ip6gre->local, local, sizeof(struct in6_addr)); + ip6gre->ip6gre_mask |= IP6GRE_ATTR_LOCAL; + + return 0; +} + +/** + * Get IP6GRE tunnel local address + * @arg link Link object + * @arg local addr to fill in with local address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_get_local(struct rtnl_link *link, struct in6_addr *local) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + HAS_IP6GRE_ATTR_ASSERT(ip6gre, IP6GRE_ATTR_LOCAL); + + memcpy(local, &ip6gre->local, sizeof(struct in6_addr)); + + return 0; +} + +/** + * Set IP6GRE tunnel remote address + * @arg link Link object + * @arg remote remote address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_set_remote(struct rtnl_link *link, struct in6_addr *remote) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + memcpy(&ip6gre->remote, remote, sizeof(struct in6_addr)); + ip6gre->ip6gre_mask |= IP6GRE_ATTR_REMOTE; + + return 0; +} + +/** + * Get IP6GRE tunnel remote address + * @arg link Link object + * @arg remote addr to fill in with remote address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_get_remote(struct rtnl_link *link, struct in6_addr *remote) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + HAS_IP6GRE_ATTR_ASSERT(ip6gre, IP6GRE_ATTR_REMOTE); + + memcpy(remote, &ip6gre->remote, sizeof(struct in6_addr)); + + return 0; +} + +/** + * Set IP6GRE tunnel ttl + * @arg link Link object + * @arg ttl tunnel ttl + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_set_ttl(struct rtnl_link *link, uint8_t ttl) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + ip6gre->ttl = ttl; + ip6gre->ip6gre_mask |= IP6GRE_ATTR_TTL; + + return 0; +} + +/** + * Set IP6GRE tunnel ttl + * @arg link Link object + * @arg ttl addr to fill in with the ttl + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_get_ttl(struct rtnl_link *link, uint8_t *ttl) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + HAS_IP6GRE_ATTR_ASSERT(ip6gre, IP6GRE_ATTR_TTL); + + *ttl = ip6gre->ttl; + + return 0; +} + +/** + * Set IP6GRE tunnel encap limit + * @arg link Link object + * @arg encaplimit tunnel encap limit value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_set_encaplimit(struct rtnl_link *link, uint8_t encaplimit) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + ip6gre->encaplimit = encaplimit; + ip6gre->ip6gre_mask |= IP6GRE_ATTR_ENCAPLIMIT; + + return 0; +} + +/** + * Get IP6GRE tunnel encap limit + * @arg link Link object + * @arg encaplimit addr to fill in with the encaplimit + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_get_encaplimit(struct rtnl_link *link, uint8_t *encaplimit) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + HAS_IP6GRE_ATTR_ASSERT(ip6gre, IP6GRE_ATTR_ENCAPLIMIT); + + *encaplimit = ip6gre->encaplimit; + + return 0; +} + +/** + * Set IP6GRE tunnel flowinfo + * @arg link Link object + * @arg flowinfo flowinfo value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_set_flowinfo(struct rtnl_link *link, uint32_t flowinfo) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + ip6gre->flowinfo = flowinfo; + ip6gre->ip6gre_mask |= IP6GRE_ATTR_FLOWINFO; + + return 0; +} + +/** + * Get IP6GRE flowinfo + * @arg link Link object + * @arg flowinfo addr to fill in with the flowinfo + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_get_flowinfo(struct rtnl_link *link, uint32_t *flowinfo) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + HAS_IP6GRE_ATTR_ASSERT(ip6gre, IP6GRE_ATTR_FLOWINFO); + + *flowinfo = ip6gre->flowinfo; + + return 0; +} + +/** + * Set IP6GRE tunnel flags + * @arg link Link object + * @arg flags tunnel flags + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_set_flags(struct rtnl_link *link, uint32_t flags) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + ip6gre->flags = flags; + ip6gre->ip6gre_mask |= IP6GRE_ATTR_FLAGS; + + return 0; +} + +/** + * Get IP6GRE flags + * @arg link Link object + * @arg flags addr to fill in with the tunnel flags + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_get_flags(struct rtnl_link *link, uint32_t *flags) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + HAS_IP6GRE_ATTR_ASSERT(ip6gre, IP6GRE_ATTR_FLAGS); + + *flags = ip6gre->flags; + + return 0; +} + +/** + * Set IP6GRE tunnel fwmark + * @arg link Link object + * @arg fwmark fwmark + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_set_fwmark(struct rtnl_link *link, uint32_t fwmark) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + ip6gre->fwmark = fwmark; + ip6gre->ip6gre_mask |= IP6GRE_ATTR_FWMARK; + + return 0; +} + +/** + * Get IP6GRE tunnel fwmark + * @arg link Link object + * @arg fwmark addr to fill in with the fwmark + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6gre_get_fwmark(struct rtnl_link *link, uint32_t *fwmark) +{ + struct ip6gre_info *ip6gre = link->l_info; + + IS_IP6GRE_LINK_ASSERT(link); + + HAS_IP6GRE_ATTR_ASSERT(ip6gre, IP6GRE_ATTR_FWMARK); + + *fwmark = ip6gre->fwmark; + + return 0; +} + +static void _nl_init ip6gre_init(void) +{ + rtnl_link_register_info(&ip6gre_info_ops); +} + +static void _nl_exit ip6gre_exit(void) +{ + rtnl_link_unregister_info(&ip6gre_info_ops); +} diff --git a/libnl/lib/route/link/ip6tnl.c b/libnl/lib/route/link/ip6tnl.c new file mode 100644 index 0000000..9b3abfd --- /dev/null +++ b/libnl/lib/route/link/ip6tnl.c @@ -0,0 +1,746 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +/** + * @ingroup link + * @defgroup ip6tnl IP6TNL + * ip6tnl link module + * + * @details + * \b Link Type Name: "ip6tnl" + * + * @route_doc{link_ip6tnl, IP6TNL Documentation} + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" + +#define IP6_TNL_ATTR_LINK (1 << 0) +#define IP6_TNL_ATTR_LOCAL (1 << 1) +#define IP6_TNL_ATTR_REMOTE (1 << 2) +#define IP6_TNL_ATTR_TTL (1 << 3) +#define IP6_TNL_ATTR_TOS (1 << 4) +#define IP6_TNL_ATTR_ENCAPLIMIT (1 << 5) +#define IP6_TNL_ATTR_FLAGS (1 << 6) +#define IP6_TNL_ATTR_PROTO (1 << 7) +#define IP6_TNL_ATTR_FLOWINFO (1 << 8) +#define IP6_TNL_ATTR_FWMARK (1 << 9) + +struct ip6_tnl_info +{ + uint8_t ttl; + uint8_t tos; + uint8_t encap_limit; + uint8_t proto; + uint32_t flags; + uint32_t link; + uint32_t flowinfo; + struct in6_addr local; + struct in6_addr remote; + uint32_t fwmark; + uint32_t ip6_tnl_mask; +}; + +static struct nla_policy ip6_tnl_policy[IFLA_IPTUN_MAX + 1] = { + [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, + [IFLA_IPTUN_LOCAL] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_IPTUN_REMOTE] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_IPTUN_TTL] = { .type = NLA_U8 }, + [IFLA_IPTUN_TOS] = { .type = NLA_U8 }, + [IFLA_IPTUN_ENCAP_LIMIT] = { .type = NLA_U8 }, + [IFLA_IPTUN_FLOWINFO] = { .type = NLA_U32 }, + [IFLA_IPTUN_FLAGS] = { .type = NLA_U32 }, + [IFLA_IPTUN_PROTO] = { .type = NLA_U8 }, + [IFLA_IPTUN_FWMARK] = { .type = NLA_U32 }, +}; + +static int ip6_tnl_alloc(struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*ip6_tnl)); + else { + ip6_tnl = calloc(1, sizeof(*ip6_tnl)); + if (!ip6_tnl) + return -NLE_NOMEM; + + link->l_info = ip6_tnl; + } + + return 0; +} + +static int ip6_tnl_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_IPTUN_MAX + 1]; + struct ip6_tnl_info *ip6_tnl; + int err; + + NL_DBG(3, "Parsing IP6_TNL link info\n"); + + err = nla_parse_nested(tb, IFLA_IPTUN_MAX, data, ip6_tnl_policy); + if (err < 0) + goto errout; + + err = ip6_tnl_alloc(link); + if (err < 0) + goto errout; + + ip6_tnl = link->l_info; + + if (tb[IFLA_IPTUN_LINK]) { + ip6_tnl->link = nla_get_u32(tb[IFLA_IPTUN_LINK]); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_LINK; + } + + if (tb[IFLA_IPTUN_LOCAL]) { + nla_memcpy(&ip6_tnl->local, tb[IFLA_IPTUN_LOCAL], sizeof(struct in6_addr)); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_LOCAL; + } + + if (tb[IFLA_IPTUN_REMOTE]) { + nla_memcpy(&ip6_tnl->remote, tb[IFLA_IPTUN_REMOTE], sizeof(struct in6_addr)); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_REMOTE; + } + + if (tb[IFLA_IPTUN_TTL]) { + ip6_tnl->ttl = nla_get_u8(tb[IFLA_IPTUN_TTL]); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_TTL; + } + + if (tb[IFLA_IPTUN_TOS]) { + ip6_tnl->tos = nla_get_u8(tb[IFLA_IPTUN_TOS]); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_TOS; + } + + if (tb[IFLA_IPTUN_ENCAP_LIMIT]) { + ip6_tnl->encap_limit = nla_get_u8(tb[IFLA_IPTUN_ENCAP_LIMIT]); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_ENCAPLIMIT; + } + + if (tb[IFLA_IPTUN_FLAGS]) { + ip6_tnl->flags = nla_get_u32(tb[IFLA_IPTUN_FLAGS]); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_FLAGS; + } + + if (tb[IFLA_IPTUN_FLOWINFO]) { + ip6_tnl->flowinfo = nla_get_u32(tb[IFLA_IPTUN_FLOWINFO]); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_FLOWINFO; + } + + if (tb[IFLA_IPTUN_PROTO]) { + ip6_tnl->proto = nla_get_u8(tb[IFLA_IPTUN_PROTO]); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_PROTO; + } + + if (tb[IFLA_IPTUN_FWMARK]) { + ip6_tnl->fwmark = nla_get_u32(tb[IFLA_IPTUN_FWMARK]); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_FWMARK; + } + + err = 0; + +errout: + return err; +} + +static int ip6_tnl_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_IPTUN_LINK, ip6_tnl->link); + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_LOCAL) + NLA_PUT(msg, IFLA_IPTUN_LOCAL, sizeof(struct in6_addr), &ip6_tnl->local); + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_REMOTE) + NLA_PUT(msg, IFLA_IPTUN_REMOTE, sizeof(struct in6_addr), &ip6_tnl->remote); + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_TTL) + NLA_PUT_U8(msg, IFLA_IPTUN_TTL, ip6_tnl->ttl); + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_TOS) + NLA_PUT_U8(msg, IFLA_IPTUN_TOS, ip6_tnl->tos); + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_ENCAPLIMIT) + NLA_PUT_U8(msg, IFLA_IPTUN_ENCAP_LIMIT, ip6_tnl->encap_limit); + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_FLAGS) + NLA_PUT_U32(msg, IFLA_IPTUN_FLAGS, ip6_tnl->flags); + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_FLOWINFO) + NLA_PUT_U32(msg, IFLA_IPTUN_FLOWINFO, ip6_tnl->flowinfo); + + /* kernel crashes if this attribure is missing temporary fix */ + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_PROTO) + NLA_PUT_U8(msg, IFLA_IPTUN_PROTO, ip6_tnl->proto); + else + NLA_PUT_U8(msg, IFLA_IPTUN_PROTO, 0); + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_FWMARK) + NLA_PUT_U32(msg, IFLA_IPTUN_FWMARK, ip6_tnl->fwmark); + + nla_nest_end(msg, data); + +nla_put_failure: + return 0; +} + +static void ip6_tnl_free(struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + free(ip6_tnl); + link->l_info = NULL; +} + +static void ip6_tnl_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + nl_dump(p, "ip6_tnl : %s", link->l_name); +} + +static void ip6_tnl_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + char *name, addr[INET6_ADDRSTRLEN]; + struct rtnl_link *parent; + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_LINK) { + nl_dump(p, " link "); + + name = NULL; + parent = link_lookup(link->ce_cache, ip6_tnl->link); + if (parent) + name = rtnl_link_get_name(parent); + + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", ip6_tnl->link); + } + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_LOCAL) { + nl_dump(p, " local "); + nl_dump_line(p, "%s\n", + _nl_inet_ntop(AF_INET6, &ip6_tnl->local, addr)); + } + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_REMOTE) { + nl_dump(p, " remote "); + nl_dump_line(p, "%s\n", + _nl_inet_ntop(AF_INET6, &ip6_tnl->remote, addr)); + } + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_TTL) { + nl_dump(p, " ttl "); + nl_dump_line(p, "%d\n", ip6_tnl->ttl); + } + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_TOS) { + nl_dump(p, " tos "); + nl_dump_line(p, "%d\n", ip6_tnl->tos); + } + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_ENCAPLIMIT) { + nl_dump(p, " encaplimit "); + nl_dump_line(p, "%d\n", ip6_tnl->encap_limit); + } + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_FLAGS) { + nl_dump(p, " flags "); + nl_dump_line(p, " (%x)\n", ip6_tnl->flags); + } + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_FLOWINFO) { + nl_dump(p, " flowinfo "); + nl_dump_line(p, " (%x)\n", ip6_tnl->flowinfo); + } + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_PROTO) { + nl_dump(p, " proto "); + nl_dump_line(p, " (%x)\n", ip6_tnl->proto); + } + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_FWMARK) { + nl_dump(p, " fwmark "); + nl_dump_line(p, "%x\n", ip6_tnl->fwmark); + } +} + +static int ip6_tnl_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct ip6_tnl_info *ip6_tnl_dst, *ip6_tnl_src = src->l_info; + int err; + + dst->l_info = NULL; + + err = rtnl_link_set_type(dst, "ip6tnl"); + if (err < 0) + return err; + + ip6_tnl_dst = dst->l_info; + + if (!ip6_tnl_dst || !ip6_tnl_src) + BUG(); + + memcpy(ip6_tnl_dst, ip6_tnl_src, sizeof(struct ip6_tnl_info)); + + return 0; +} + +static struct rtnl_link_info_ops ip6_tnl_info_ops = { + .io_name = "ip6tnl", + .io_alloc = ip6_tnl_alloc, + .io_parse = ip6_tnl_parse, + .io_dump = { + [NL_DUMP_LINE] = ip6_tnl_dump_line, + [NL_DUMP_DETAILS] = ip6_tnl_dump_details, + }, + .io_clone = ip6_tnl_clone, + .io_put_attrs = ip6_tnl_put_attrs, + .io_free = ip6_tnl_free, +}; + +#define IS_IP6_TNL_LINK_ASSERT(link)\ + if ((link)->l_info_ops != &ip6_tnl_info_ops) {\ + APPBUG("Link is not a ip6_tnl link. set type \"ip6tnl\" first.");\ + return -NLE_OPNOTSUPP;\ + } + +struct rtnl_link *rtnl_link_ip6_tnl_alloc(void) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_alloc(); + if (!link) + return NULL; + + err = rtnl_link_set_type(link, "ip6tnl"); + if (err < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a IP6_TNL link + * @arg link Link object + * + * @return True if link is a IP6_TNL link, otherwise false is returned. + */ +int rtnl_link_is_ip6_tnl(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "ip6tnl"); +} + +/** + * Create a new ip6_tnl tunnel device + * @arg sock netlink socket + * @arg name name of the tunnel device + * + * Creates a new ip6_tnl tunnel device in the kernel + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_add(struct nl_sock *sk, const char *name) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_ip6_tnl_alloc(); + if (!link) + return -NLE_NOMEM; + + if(name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + rtnl_link_put(link); + + return err; +} + +/** + * Set IP6_TNL tunnel interface index + * @arg link Link object + * @arg index interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_set_link(struct rtnl_link *link, uint32_t index) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + ip6_tnl->link = index; + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_LINK; + + return 0; +} + +/** + * Get IP6_TNL tunnel interface index + * @arg link Link object + * + * @return interface index value + */ +uint32_t rtnl_link_ip6_tnl_get_link(struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + return ip6_tnl->link; +} + +/** + * Set IP6_TNL tunnel local address + * @arg link Link object + * @arg addr local address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_set_local(struct rtnl_link *link, struct in6_addr *addr) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + memcpy(&ip6_tnl->local, addr, sizeof(struct in6_addr)); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_LOCAL; + + return 0; +} + +/** + * Get IP6_TNL tunnel local address + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_get_local(struct rtnl_link *link, struct in6_addr *addr) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + memcpy(addr, &ip6_tnl->local, sizeof(struct in6_addr)); + + return 0; +} + +/** + * Set IP6_TNL tunnel remote address + * @arg link Link object + * @arg remote remote address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_set_remote(struct rtnl_link *link, struct in6_addr *addr) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + memcpy(&ip6_tnl->remote, addr, sizeof(struct in6_addr)); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_REMOTE; + + return 0; +} + +/** + * Get IP6_TNL tunnel remote address + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_get_remote(struct rtnl_link *link, struct in6_addr *addr) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + memcpy(addr, &ip6_tnl->remote, sizeof(struct in6_addr)); + + return 0; +} + +/** + * Set IP6_TNL tunnel ttl + * @arg link Link object + * @arg ttl tunnel ttl + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_set_ttl(struct rtnl_link *link, uint8_t ttl) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + ip6_tnl->ttl = ttl; + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_TTL; + + return 0; +} + +/** + * Get IP6_TNL tunnel ttl + * @arg link Link object + * + * @return ttl value + */ +uint8_t rtnl_link_ip6_tnl_get_ttl(struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + return ip6_tnl->ttl; +} + +/** + * Set IP6_TNL tunnel tos + * @arg link Link object + * @arg tos tunnel tos + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_set_tos(struct rtnl_link *link, uint8_t tos) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + ip6_tnl->tos = tos; + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_TOS; + + return 0; +} + +/** + * Get IP6_TNL tunnel tos + * @arg link Link object + * + * @return tos value + */ +uint8_t rtnl_link_ip6_tnl_get_tos(struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + return ip6_tnl->tos; +} + +/** + * Set IP6_TNL tunnel encap limit + * @arg link Link object + * @arg encap_limit encaplimit value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_set_encaplimit(struct rtnl_link *link, uint8_t encap_limit) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + ip6_tnl->encap_limit = encap_limit; + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_ENCAPLIMIT; + + return 0; +} + +/** + * Get IP6_TNL encaplimit + * @arg link Link object + * + * @return encaplimit value + */ +uint8_t rtnl_link_ip6_tnl_get_encaplimit(struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + return ip6_tnl->encap_limit; +} + +/** + * Set IP6_TNL tunnel flowinfo + * @arg link Link object + * @arg flowinfo flowinfo value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_set_flowinfo(struct rtnl_link *link, uint32_t flowinfo) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + ip6_tnl->flowinfo = flowinfo; + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_FLOWINFO; + + return 0; +} + +/** + * Get IP6_TNL flowinfo + * @arg link Link object + * + * @return flowinfo value + */ +uint32_t rtnl_link_ip6_tnl_get_flowinfo(struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + return ip6_tnl->flowinfo; +} + +/** + * Set IP6_TNL tunnel flags + * @arg link Link object + * @arg flags tunnel flags + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_set_flags(struct rtnl_link *link, uint32_t flags) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + ip6_tnl->flags = flags; + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_FLAGS; + + return 0; +} + +/** + * Get IP6_TNL path flags + * @arg link Link object + * + * @return flags value + */ +uint32_t rtnl_link_ip6_tnl_get_flags(struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + return ip6_tnl->flags; +} + +/** + * Set IP6_TNL tunnel proto + * @arg link Link object + * @arg proto tunnel proto + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_set_proto(struct rtnl_link *link, uint8_t proto) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + ip6_tnl->proto = proto; + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_PROTO; + + return 0; +} + +/** + * Get IP6_TNL proto + * @arg link Link object + * + * @return proto value + */ +uint8_t rtnl_link_ip6_tnl_get_proto(struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + return ip6_tnl->proto; +} + +/** + * Set IP6_TNL tunnel fwmark + * @arg link Link object + * @arg fwmark fwmark + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_set_fwmark(struct rtnl_link *link, uint32_t fwmark) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + ip6_tnl->fwmark = fwmark; + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_FWMARK; + + return 0; +} + +/** + * Get IP6_TNL tunnel fwmark + * @arg link Link object + * @arg fwmark addr to fill in with the fwmark + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_get_fwmark(struct rtnl_link *link, uint32_t *fwmark) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + if (!(ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_FWMARK)) + return -NLE_NOATTR; + + *fwmark = ip6_tnl->fwmark; + + return 0; +} + +static void _nl_init ip6_tnl_init(void) +{ + rtnl_link_register_info(&ip6_tnl_info_ops); +} + +static void _nl_exit ip6_tnl_exit(void) +{ + rtnl_link_unregister_info(&ip6_tnl_info_ops); +} diff --git a/libnl/lib/route/link/ip6vti.c b/libnl/lib/route/link/ip6vti.c new file mode 100644 index 0000000..b6c7a1b --- /dev/null +++ b/libnl/lib/route/link/ip6vti.c @@ -0,0 +1,558 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +/** + * @ingroup link + * @defgroup ip6vti IP6VTI + * ip6vti link module + * + * @details + * \b Link Type Name: "vti6" + * + * @route_doc{link_ip6vti, IP6VTI Documentation} + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" + +#define IP6VTI_ATTR_LINK (1 << 0) +#define IP6VTI_ATTR_IKEY (1 << 1) +#define IP6VTI_ATTR_OKEY (1 << 2) +#define IP6VTI_ATTR_LOCAL (1 << 3) +#define IP6VTI_ATTR_REMOTE (1 << 4) +#define IP6VTI_ATTR_FWMARK (1 << 5) + +struct ip6vti_info +{ + uint32_t link; + uint32_t ikey; + uint32_t okey; + struct in6_addr local; + struct in6_addr remote; + uint32_t fwmark; + uint32_t ip6vti_mask; +}; + +static struct nla_policy ip6vti_policy[IFLA_VTI_MAX + 1] = { + [IFLA_VTI_LINK] = { .type = NLA_U32 }, + [IFLA_VTI_IKEY] = { .type = NLA_U32 }, + [IFLA_VTI_OKEY] = { .type = NLA_U32 }, + [IFLA_VTI_LOCAL] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_VTI_REMOTE] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_VTI_FWMARK] = { .type = NLA_U32 }, +}; + +static int ip6vti_alloc(struct rtnl_link *link) +{ + struct ip6vti_info *ip6vti; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*ip6vti)); + else { + ip6vti = calloc(1, sizeof(*ip6vti)); + if (!ip6vti) + return -NLE_NOMEM; + + link->l_info = ip6vti; + } + + return 0; +} + +static int ip6vti_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_VTI_MAX + 1]; + struct ip6vti_info *ip6vti; + int err; + + NL_DBG(3, "Parsing IP6VTI link info\n"); + + err = nla_parse_nested(tb, IFLA_VTI_MAX, data, ip6vti_policy); + if (err < 0) + goto errout; + + err = ip6vti_alloc(link); + if (err < 0) + goto errout; + + ip6vti = link->l_info; + + if (tb[IFLA_VTI_LINK]) { + ip6vti->link = nla_get_u32(tb[IFLA_VTI_LINK]); + ip6vti->ip6vti_mask |= IP6VTI_ATTR_LINK; + } + + if (tb[IFLA_VTI_IKEY]) { + ip6vti->ikey = nla_get_u32(tb[IFLA_VTI_IKEY]); + ip6vti->ip6vti_mask |= IP6VTI_ATTR_IKEY; + } + + if (tb[IFLA_VTI_OKEY]) { + ip6vti->okey = nla_get_u32(tb[IFLA_VTI_OKEY]); + ip6vti->ip6vti_mask |= IP6VTI_ATTR_OKEY; + } + + if (tb[IFLA_VTI_LOCAL]) { + nla_memcpy(&ip6vti->local, tb[IFLA_VTI_LOCAL], sizeof(struct in6_addr)); + ip6vti->ip6vti_mask |= IP6VTI_ATTR_LOCAL; + } + + if (tb[IFLA_VTI_REMOTE]) { + nla_memcpy(&ip6vti->remote, tb[IFLA_VTI_REMOTE], sizeof(struct in6_addr)); + ip6vti->ip6vti_mask |= IP6VTI_ATTR_REMOTE; + } + + if (tb[IFLA_VTI_FWMARK]) { + ip6vti->fwmark = nla_get_u32(tb[IFLA_VTI_FWMARK]); + ip6vti->ip6vti_mask |= IP6VTI_ATTR_FWMARK; + } + + err = 0; + + errout: + return err; +} + +static int ip6vti_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct ip6vti_info *ip6vti = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + + if (ip6vti->ip6vti_mask & IP6VTI_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_VTI_LINK, ip6vti->link); + + if (ip6vti->ip6vti_mask & IP6VTI_ATTR_IKEY) + NLA_PUT_U32(msg, IFLA_VTI_IKEY, ip6vti->ikey); + + if (ip6vti->ip6vti_mask & IP6VTI_ATTR_OKEY) + NLA_PUT_U32(msg, IFLA_VTI_OKEY, ip6vti->okey); + + if (ip6vti->ip6vti_mask & IP6VTI_ATTR_LOCAL) + NLA_PUT(msg, IFLA_VTI_LOCAL, sizeof(struct in6_addr), &ip6vti->local); + + if (ip6vti->ip6vti_mask & IP6VTI_ATTR_REMOTE) + NLA_PUT(msg, IFLA_VTI_REMOTE, sizeof(struct in6_addr), &ip6vti->remote); + + if (ip6vti->ip6vti_mask & IP6VTI_ATTR_FWMARK) + NLA_PUT_U32(msg, IFLA_VTI_FWMARK, ip6vti->fwmark); + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static void ip6vti_free(struct rtnl_link *link) +{ + struct ip6vti_info *ip6vti = link->l_info; + + free(ip6vti); + link->l_info = NULL; +} + +static void ip6vti_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + nl_dump(p, "ip6vti : %s", link->l_name); +} + +static void ip6vti_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct ip6vti_info *ip6vti = link->l_info; + char *name; + char addr[INET6_ADDRSTRLEN]; + + if (ip6vti->ip6vti_mask & IP6VTI_ATTR_LINK) { + nl_dump(p, " link "); + name = rtnl_link_get_name(link); + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", ip6vti->link); + } + + if (ip6vti->ip6vti_mask & IP6VTI_ATTR_IKEY) { + nl_dump(p, " ikey "); + nl_dump_line(p, "%x\n",ip6vti->ikey); + } + + if (ip6vti->ip6vti_mask & IP6VTI_ATTR_OKEY) { + nl_dump(p, " okey "); + nl_dump_line(p, "%x\n", ip6vti->okey); + } + + if (ip6vti->ip6vti_mask & IP6VTI_ATTR_LOCAL) { + nl_dump(p, " local "); + nl_dump_line(p, "%s\n", + _nl_inet_ntop(AF_INET6, &ip6vti->local, addr)); + } + + if (ip6vti->ip6vti_mask & IP6VTI_ATTR_REMOTE) { + nl_dump(p, " remote "); + nl_dump_line(p, "%s\n", + _nl_inet_ntop(AF_INET6, &ip6vti->remote, addr)); + } + + if (ip6vti->ip6vti_mask & IP6VTI_ATTR_FWMARK) { + nl_dump(p, " fwmark "); + nl_dump_line(p, "%x\n", ip6vti->fwmark); + } +} + +static int ip6vti_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct ip6vti_info *ip6vti_dst, *ip6vti_src = src->l_info; + int err; + + dst->l_info = NULL; + + err = rtnl_link_set_type(dst, "vti6"); + if (err < 0) + return err; + + ip6vti_dst = dst->l_info; + + if (!ip6vti_dst || !ip6vti_src) + BUG(); + + memcpy(ip6vti_dst, ip6vti_src, sizeof(struct ip6vti_info)); + + return 0; +} + +static struct rtnl_link_info_ops ip6vti_info_ops = { + .io_name = "vti6", + .io_alloc = ip6vti_alloc, + .io_parse = ip6vti_parse, + .io_dump = { + [NL_DUMP_LINE] = ip6vti_dump_line, + [NL_DUMP_DETAILS] = ip6vti_dump_details, + }, + .io_clone = ip6vti_clone, + .io_put_attrs = ip6vti_put_attrs, + .io_free = ip6vti_free, +}; + +#define IS_IP6VTI_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &ip6vti_info_ops) { \ + APPBUG("Link is not a ip6vti link. set type \"vti6\" first."); \ + return -NLE_OPNOTSUPP; \ + } + +#define HAS_IP6VTI_ATTR_ASSERT(ip6vti,attr) \ + if (!((ip6vti)->ip6vti_mask & (attr))) \ + return -NLE_NOATTR; + +struct rtnl_link *rtnl_link_ip6vti_alloc(void) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_alloc(); + if (!link) + return NULL; + + err = rtnl_link_set_type(link, "vti6"); + if (err < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a IP6VTI link + * @arg link Link object + * + * @return True if link is a IP6VTI link, otherwise 0 is returned. + */ +int rtnl_link_is_ip6vti(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "vti6"); +} +/** + * Create a new vti6 tunnel device + * @arg sock netlink socket + * @arg name name of the tunnel deviceL + * + * Creates a new vti6 tunnel device in the kernel + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6vti_add(struct nl_sock *sk, const char *name) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_ip6vti_alloc(); + if (!link) + return -NLE_NOMEM; + + if(name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + rtnl_link_put(link); + + return err; +} +/** + * Set IP6VTI tunnel interface index + * @arg link Link object + * @arg index interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6vti_set_link(struct rtnl_link *link, uint32_t index) +{ + struct ip6vti_info *ip6vti = link->l_info; + + IS_IP6VTI_LINK_ASSERT(link); + + ip6vti->link = index; + ip6vti->ip6vti_mask |= IP6VTI_ATTR_LINK; + + return 0; +} + +/** + * Get IP6VTI tunnel interface index + * @arg link Link object + * @arg index addr to fill in with the interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6vti_get_link(struct rtnl_link *link, uint32_t *index) +{ + struct ip6vti_info *ip6vti = link->l_info; + + IS_IP6VTI_LINK_ASSERT(link); + + HAS_IP6VTI_ATTR_ASSERT(ip6vti, IP6VTI_ATTR_LINK); + + *index = ip6vti->link; + + return 0; +} + +/** + * Set IP6VTI tunnel set ikey + * @arg link Link object + * @arg ikey gre ikey + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6vti_set_ikey(struct rtnl_link *link, uint32_t ikey) +{ + struct ip6vti_info *ip6vti = link->l_info; + + IS_IP6VTI_LINK_ASSERT(link); + + ip6vti->ikey = ikey; + ip6vti->ip6vti_mask |= IP6VTI_ATTR_IKEY; + + return 0; +} + +/** + * Get IP6VTI tunnel ikey + * @arg link Link object + * @arg ikey addr to fill in with the ikey + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6vti_get_ikey(struct rtnl_link *link, uint32_t *ikey) +{ + struct ip6vti_info *ip6vti = link->l_info; + + IS_IP6VTI_LINK_ASSERT(link); + + HAS_IP6VTI_ATTR_ASSERT(ip6vti, IP6VTI_ATTR_IKEY); + + *ikey = ip6vti->ikey; + + return 0; +} + +/** + * Set IP6VTI tunnel set okey + * @arg link Link object + * @arg okey gre okey + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6vti_set_okey(struct rtnl_link *link, uint32_t okey) +{ + struct ip6vti_info *ip6vti = link->l_info; + + IS_IP6VTI_LINK_ASSERT(link); + + ip6vti->okey = okey; + ip6vti->ip6vti_mask |= IP6VTI_ATTR_OKEY; + + return 0; +} + +/** + * Get IP6VTI tunnel okey + * @arg link Link object + * @arg okey addr to fill in with the okey + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6vti_get_okey(struct rtnl_link *link, uint32_t *okey) +{ + struct ip6vti_info *ip6vti = link->l_info; + + IS_IP6VTI_LINK_ASSERT(link); + + HAS_IP6VTI_ATTR_ASSERT(ip6vti, IP6VTI_ATTR_OKEY); + + *okey = ip6vti->okey; + + return 0; +} + +/** + * Set IP6VTI tunnel local address + * @arg link Link object + * @arg local local address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6vti_set_local(struct rtnl_link *link, struct in6_addr *local) +{ + struct ip6vti_info *ip6vti = link->l_info; + + IS_IP6VTI_LINK_ASSERT(link); + + memcpy(&ip6vti->local, local, sizeof(struct in6_addr)); + ip6vti->ip6vti_mask |= IP6VTI_ATTR_LOCAL; + + return 0; +} + +/** + * Get IP6VTI tunnel local address + * @arg link Link object + * @arg local addr to fill in with remote address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6vti_get_local(struct rtnl_link *link, struct in6_addr *local) +{ + struct ip6vti_info *ip6vti = link->l_info; + + IS_IP6VTI_LINK_ASSERT(link); + + HAS_IP6VTI_ATTR_ASSERT(ip6vti, IP6VTI_ATTR_LOCAL); + + memcpy(local, &ip6vti->local, sizeof(struct in6_addr)); + + return 0; +} + +/** + * Set IP6VTI tunnel remote address + * @arg link Link object + * @arg remote remote address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6vti_set_remote(struct rtnl_link *link, struct in6_addr *remote) +{ + struct ip6vti_info *ip6vti = link->l_info; + + IS_IP6VTI_LINK_ASSERT(link); + + memcpy(&ip6vti->remote, remote, sizeof(struct in6_addr)); + ip6vti->ip6vti_mask |= IP6VTI_ATTR_REMOTE; + + return 0; +} + +/** + * Get IP6VTI tunnel remote address + * @arg link Link object + * @arg remote addr to fill in with remote address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6vti_get_remote(struct rtnl_link *link, struct in6_addr *remote) +{ + struct ip6vti_info *ip6vti = link->l_info; + + IS_IP6VTI_LINK_ASSERT(link); + + HAS_IP6VTI_ATTR_ASSERT(ip6vti, IP6VTI_ATTR_REMOTE); + + memcpy(remote, &ip6vti->remote, sizeof(struct in6_addr)); + + return 0; +} + +/** + * Set IP6VTI tunnel fwmark + * @arg link Link object + * @arg fwmark fwmark + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6vti_set_fwmark(struct rtnl_link *link, uint32_t fwmark) +{ + struct ip6vti_info *ip6vti = link->l_info; + + IS_IP6VTI_LINK_ASSERT(link); + + ip6vti->fwmark = fwmark; + ip6vti->ip6vti_mask |= IP6VTI_ATTR_FWMARK; + + return 0; +} + +/** + * Get IP6VTI tunnel fwmark + * @arg link Link object + * @arg fwmark addr to fill in with the fwmark + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6vti_get_fwmark(struct rtnl_link *link, uint32_t *fwmark) +{ + struct ip6vti_info *ip6vti = link->l_info; + + IS_IP6VTI_LINK_ASSERT(link); + + HAS_IP6VTI_ATTR_ASSERT(ip6vti, IP6VTI_ATTR_FWMARK); + + *fwmark = ip6vti->fwmark; + + return 0; +} + +static void _nl_init ip6vti_init(void) +{ + rtnl_link_register_info(&ip6vti_info_ops); +} + +static void _nl_exit ip6vti_exit(void) +{ + rtnl_link_unregister_info(&ip6vti_info_ops); +} diff --git a/libnl/lib/route/link/ipgre.c b/libnl/lib/route/link/ipgre.c new file mode 100644 index 0000000..bd3cc40 --- /dev/null +++ b/libnl/lib/route/link/ipgre.c @@ -0,0 +1,896 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +/** + * @ingroup link + * @defgroup ipgre IPGRE + * ipgre link module + * + * @details + * \b Link Type Name: "ipgre" + * + * @route_doc{link_ipgre, IPGRE Documentation} + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" + +#define IPGRE_ATTR_LINK (1 << 0) +#define IPGRE_ATTR_IFLAGS (1 << 1) +#define IPGRE_ATTR_OFLAGS (1 << 2) +#define IPGRE_ATTR_IKEY (1 << 3) +#define IPGRE_ATTR_OKEY (1 << 4) +#define IPGRE_ATTR_LOCAL (1 << 5) +#define IPGRE_ATTR_REMOTE (1 << 6) +#define IPGRE_ATTR_TTL (1 << 7) +#define IPGRE_ATTR_TOS (1 << 8) +#define IPGRE_ATTR_PMTUDISC (1 << 9) +#define IPGRE_ATTR_FWMARK (1 << 10) + +struct ipgre_info +{ + uint8_t ttl; + uint8_t tos; + uint8_t pmtudisc; + uint16_t iflags; + uint16_t oflags; + uint32_t ikey; + uint32_t okey; + uint32_t link; + uint32_t local; + uint32_t remote; + uint32_t fwmark; + uint32_t ipgre_mask; +}; + +static struct nla_policy ipgre_policy[IFLA_GRE_MAX + 1] = { + [IFLA_GRE_LINK] = { .type = NLA_U32 }, + [IFLA_GRE_IFLAGS] = { .type = NLA_U16 }, + [IFLA_GRE_OFLAGS] = { .type = NLA_U16 }, + [IFLA_GRE_IKEY] = { .type = NLA_U32 }, + [IFLA_GRE_OKEY] = { .type = NLA_U32 }, + [IFLA_GRE_LOCAL] = { .type = NLA_U32 }, + [IFLA_GRE_REMOTE] = { .type = NLA_U32 }, + [IFLA_GRE_TTL] = { .type = NLA_U8 }, + [IFLA_GRE_TOS] = { .type = NLA_U8 }, + [IFLA_GRE_PMTUDISC] = { .type = NLA_U8 }, + [IFLA_GRE_FWMARK] = { .type = NLA_U32 }, +}; + +static int ipgre_alloc(struct rtnl_link *link) +{ + struct ipgre_info *ipgre; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*ipgre)); + else { + ipgre = calloc(1, sizeof(*ipgre)); + if (!ipgre) + return -NLE_NOMEM; + + link->l_info = ipgre; + } + + return 0; +} + +static int ipgre_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_GRE_MAX + 1]; + struct ipgre_info *ipgre; + int err; + + NL_DBG(3, "Parsing IPGRE link info\n"); + + err = nla_parse_nested(tb, IFLA_GRE_MAX, data, ipgre_policy); + if (err < 0) + goto errout; + + err = ipgre_alloc(link); + if (err < 0) + goto errout; + + ipgre = link->l_info; + + if (tb[IFLA_GRE_LINK]) { + ipgre->link = nla_get_u32(tb[IFLA_GRE_LINK]); + ipgre->ipgre_mask |= IPGRE_ATTR_LINK; + } + + if (tb[IFLA_GRE_IFLAGS]) { + ipgre->iflags = nla_get_u16(tb[IFLA_GRE_IFLAGS]); + ipgre->ipgre_mask |= IPGRE_ATTR_IFLAGS; + } + + if (tb[IFLA_GRE_OFLAGS]) { + ipgre->oflags = nla_get_u16(tb[IFLA_GRE_OFLAGS]); + ipgre->ipgre_mask |= IPGRE_ATTR_OFLAGS; + } + + if (tb[IFLA_GRE_IKEY]) { + ipgre->ikey = nla_get_u32(tb[IFLA_GRE_IKEY]); + ipgre->ipgre_mask |= IPGRE_ATTR_IKEY; + } + + if (tb[IFLA_GRE_OKEY]) { + ipgre->okey = nla_get_u32(tb[IFLA_GRE_OKEY]); + ipgre->ipgre_mask |= IPGRE_ATTR_OKEY; + } + + if (tb[IFLA_GRE_LOCAL]) { + ipgre->local = nla_get_u32(tb[IFLA_GRE_LOCAL]); + ipgre->ipgre_mask |= IPGRE_ATTR_LOCAL; + } + + if (tb[IFLA_GRE_REMOTE]) { + ipgre->remote = nla_get_u32(tb[IFLA_GRE_REMOTE]); + ipgre->ipgre_mask |= IPGRE_ATTR_REMOTE; + } + + if (tb[IFLA_GRE_TTL]) { + ipgre->ttl = nla_get_u8(tb[IFLA_GRE_TTL]); + ipgre->ipgre_mask |= IPGRE_ATTR_TTL; + } + + if (tb[IFLA_GRE_TOS]) { + ipgre->tos = nla_get_u8(tb[IFLA_GRE_TOS]); + ipgre->ipgre_mask |= IPGRE_ATTR_TOS; + } + + if (tb[IFLA_GRE_PMTUDISC]) { + ipgre->pmtudisc = nla_get_u8(tb[IFLA_GRE_PMTUDISC]); + ipgre->ipgre_mask |= IPGRE_ATTR_PMTUDISC; + } + + if (tb[IFLA_GRE_FWMARK]) { + ipgre->fwmark = nla_get_u32(tb[IFLA_GRE_FWMARK]); + ipgre->ipgre_mask |= IPGRE_ATTR_FWMARK; + } + + err = 0; + +errout: + return err; +} + +static int ipgre_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + + if (ipgre->ipgre_mask & IPGRE_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_GRE_LINK, ipgre->link); + + if (ipgre->ipgre_mask & IFLA_GRE_IFLAGS) + NLA_PUT_U16(msg, IFLA_GRE_IFLAGS, ipgre->iflags); + + if (ipgre->ipgre_mask & IFLA_GRE_OFLAGS) + NLA_PUT_U16(msg, IFLA_GRE_OFLAGS, ipgre->oflags); + + if (ipgre->ipgre_mask & IPGRE_ATTR_IKEY) + NLA_PUT_U32(msg, IFLA_GRE_IKEY, ipgre->ikey); + + if (ipgre->ipgre_mask & IPGRE_ATTR_OKEY) + NLA_PUT_U32(msg, IFLA_GRE_OKEY, ipgre->okey); + + if (ipgre->ipgre_mask & IPGRE_ATTR_LOCAL) + NLA_PUT_U32(msg, IFLA_GRE_LOCAL, ipgre->local); + + if (ipgre->ipgre_mask & IPGRE_ATTR_REMOTE) + NLA_PUT_U32(msg, IFLA_GRE_REMOTE, ipgre->remote); + + if (ipgre->ipgre_mask & IPGRE_ATTR_TTL) + NLA_PUT_U8(msg, IFLA_GRE_TTL, ipgre->ttl); + + if (ipgre->ipgre_mask & IPGRE_ATTR_TOS) + NLA_PUT_U8(msg, IFLA_GRE_TOS, ipgre->tos); + + if (ipgre->ipgre_mask & IPGRE_ATTR_PMTUDISC) + NLA_PUT_U8(msg, IFLA_GRE_PMTUDISC, ipgre->pmtudisc); + + if (ipgre->ipgre_mask & IPGRE_ATTR_FWMARK) + NLA_PUT_U32(msg, IFLA_GRE_FWMARK, ipgre->fwmark); + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static void ipgre_free(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + free(ipgre); + link->l_info = NULL; +} + +static void ipgre_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + nl_dump(p, "ipgre : %s", link->l_name); +} + +static void ipgre_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct ipgre_info *ipgre = link->l_info; + char *name, addr[INET_ADDRSTRLEN]; + struct rtnl_link *parent; + + if (ipgre->ipgre_mask & IPGRE_ATTR_LINK) { + nl_dump(p, " link "); + + name = NULL; + parent = link_lookup(link->ce_cache, ipgre->link); + if (parent) + name = rtnl_link_get_name(parent); + + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", ipgre->link); + } + + if (ipgre->ipgre_mask & IPGRE_ATTR_IFLAGS) { + nl_dump(p, " iflags "); + nl_dump_line(p, "%x\n", ipgre->iflags); + } + + if (ipgre->ipgre_mask & IPGRE_ATTR_OFLAGS) { + nl_dump(p, " oflags "); + nl_dump_line(p, "%x\n", ipgre->oflags); + } + + if (ipgre->ipgre_mask & IPGRE_ATTR_IKEY) { + nl_dump(p, " ikey "); + nl_dump_line(p, "%x\n",ipgre->ikey); + } + + if (ipgre->ipgre_mask & IPGRE_ATTR_OKEY) { + nl_dump(p, " okey "); + nl_dump_line(p, "%x\n", ipgre->okey); + } + + if (ipgre->ipgre_mask & IPGRE_ATTR_LOCAL) { + nl_dump(p, " local "); + if(inet_ntop(AF_INET, &ipgre->local, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ntohs(ipgre->local)); + } + + if (ipgre->ipgre_mask & IPGRE_ATTR_REMOTE) { + nl_dump(p, " remote "); + if(inet_ntop(AF_INET, &ipgre->remote, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ntohs(ipgre->remote)); + } + + if (ipgre->ipgre_mask & IPGRE_ATTR_TTL) { + nl_dump(p, " ttl "); + nl_dump_line(p, "%u\n", ipgre->ttl); + } + + if (ipgre->ipgre_mask & IPGRE_ATTR_TOS) { + nl_dump(p, " tos "); + nl_dump_line(p, "%u\n", ipgre->tos); + } + + if (ipgre->ipgre_mask & IPGRE_ATTR_PMTUDISC) { + nl_dump(p, " pmtudisc "); + nl_dump_line(p, "enabled (%#x)\n", ipgre->pmtudisc); + } + + if (ipgre->ipgre_mask & IPGRE_ATTR_FWMARK) { + nl_dump(p, " fwmark "); + nl_dump_line(p, "%x\n", ipgre->fwmark); + } +} + +static int ipgre_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct ipgre_info *ipgre_dst, *ipgre_src = src->l_info; + int err; + + dst->l_info = NULL; + + err = rtnl_link_set_type(dst, "gre"); + if (err < 0) + return err; + + ipgre_dst = dst->l_info; + + if (!ipgre_dst || !ipgre_src) + BUG(); + + memcpy(ipgre_dst, ipgre_src, sizeof(struct ipgre_info)); + + return 0; +} + +static int ipgretap_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct ipgre_info *ipgre_dst, *ipgre_src = src->l_info; + int err; + + dst->l_info = NULL; + + err = rtnl_link_set_type(dst, "gretap"); + if (err < 0) + return err; + + ipgre_dst = dst->l_info; + + if (!ipgre_dst || !ipgre_src) + BUG(); + + memcpy(ipgre_dst, ipgre_src, sizeof(struct ipgre_info)); + + return 0; +} + +static struct rtnl_link_info_ops ipgre_info_ops = { + .io_name = "gre", + .io_alloc = ipgre_alloc, + .io_parse = ipgre_parse, + .io_dump = { + [NL_DUMP_LINE] = ipgre_dump_line, + [NL_DUMP_DETAILS] = ipgre_dump_details, + }, + .io_clone = ipgre_clone, + .io_put_attrs = ipgre_put_attrs, + .io_free = ipgre_free, +}; + +static struct rtnl_link_info_ops ipgretap_info_ops = { + .io_name = "gretap", + .io_alloc = ipgre_alloc, + .io_parse = ipgre_parse, + .io_dump = { + [NL_DUMP_LINE] = ipgre_dump_line, + [NL_DUMP_DETAILS] = ipgre_dump_details, + }, + .io_clone = ipgretap_clone, + .io_put_attrs = ipgre_put_attrs, + .io_free = ipgre_free, +}; + +#define IS_IPGRE_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &ipgre_info_ops && \ + (link)->l_info_ops != &ipgretap_info_ops) { \ + APPBUG("Link is not a ipgre link. set type \"gre/gretap\" first.");\ + return -NLE_OPNOTSUPP; \ + } + +struct rtnl_link *rtnl_link_ipgre_alloc(void) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_alloc(); + if (!link) + return NULL; + + err = rtnl_link_set_type(link, "gre"); + if (err < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a IPGRE link + * @arg link Link object + * + * @return True if link is a IPGRE link, otherwise 0 is returned. + */ +int rtnl_link_is_ipgre(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "gre"); +} + +/** + * Create a new IPGRE tunnel device + * @arg sock netlink socket + * @arg name name of the tunnel deviceL + * + * Creates a new ipip tunnel device in the kernel + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_add(struct nl_sock *sk, const char *name) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_ipgre_alloc(); + if (!link) + return -NLE_NOMEM; + + if(name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + rtnl_link_put(link); + + return err; +} + +struct rtnl_link *rtnl_link_ipgretap_alloc(void) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_alloc(); + if (!link) + return NULL; + + err = rtnl_link_set_type(link, "gretap"); + if (err < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a IPGRETAP link + * @arg link Link object + * + * @return True if link is a IPGRETAP link, otherwise 0 is returned. + */ +int rtnl_link_is_ipgretap(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "gretap"); +} +/** + * Create a new IPGRETAP tunnel device + * @arg sock netlink socket + * @arg name name of the tunnel deviceL + * + * Creates a new IPGRETAP tunnel device in the kernel + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgretap_add(struct nl_sock *sk, const char *name) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_ipgretap_alloc(); + if (!link) + return -NLE_NOMEM; + + if(name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + rtnl_link_put(link); + + return err; +} + +/** + * Set IPGRE tunnel interface index + * @arg link Link object + * @arg index interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_link(struct rtnl_link *link, uint32_t index) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->link = index; + ipgre->ipgre_mask |= IPGRE_ATTR_LINK; + + return 0; +} + +/** + * Get IPGRE tunnel interface index + * @arg link Link object + * + * @return interface index + */ +uint32_t rtnl_link_ipgre_get_link(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->link; +} + +/** + * Set IPGRE tunnel set iflags + * @arg link Link object + * @arg iflags gre iflags + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_iflags(struct rtnl_link *link, uint16_t iflags) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->iflags = iflags; + ipgre->ipgre_mask |= IPGRE_ATTR_IFLAGS; + + return 0; +} + +/** + * Get IPGRE tunnel iflags + * @arg link Link object + * + * @return iflags + */ +uint16_t rtnl_link_ipgre_get_iflags(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->iflags; +} + +/** + * Set IPGRE tunnel set oflags + * @arg link Link object + * @arg iflags gre oflags + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_oflags(struct rtnl_link *link, uint16_t oflags) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->oflags = oflags; + ipgre->ipgre_mask |= IPGRE_ATTR_OFLAGS; + + return 0; +} + +/** + * Get IPGRE tunnel oflags + * @arg link Link object + * + * @return oflags + */ +uint16_t rtnl_link_ipgre_get_oflags(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->oflags; +} + +/** + * Set IPGRE tunnel set ikey + * @arg link Link object + * @arg ikey gre ikey + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_ikey(struct rtnl_link *link, uint32_t ikey) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->ikey = ikey; + ipgre->ipgre_mask |= IPGRE_ATTR_IKEY; + + return 0; +} + +/** + * Get IPGRE tunnel ikey + * @arg link Link object + * + * @return ikey + */ +uint32_t rtnl_link_ipgre_get_ikey(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->ikey; +} + +/** + * Set IPGRE tunnel set okey + * @arg link Link object + * @arg okey gre okey + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_okey(struct rtnl_link *link, uint32_t okey) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->okey = okey; + ipgre->ipgre_mask |= IPGRE_ATTR_OKEY; + + return 0; +} + +/** + * Get IPGRE tunnel okey + * @arg link Link object + * + * @return okey value + */ +uint32_t rtnl_link_ipgre_get_okey(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->okey; +} + +/** + * Set IPGRE tunnel local address + * @arg link Link object + * @arg addr local address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_local(struct rtnl_link *link, uint32_t addr) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->local = addr; + ipgre->ipgre_mask |= IPGRE_ATTR_LOCAL; + + return 0; +} + +/** + * Get IPGRE tunnel local address + * @arg link Link object + * + * @return local address + */ +uint32_t rtnl_link_ipgre_get_local(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->local; +} + +/** + * Set IPGRE tunnel remote address + * @arg link Link object + * @arg remote remote address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_remote(struct rtnl_link *link, uint32_t remote) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->remote = remote; + ipgre->ipgre_mask |= IPGRE_ATTR_REMOTE; + + return 0; +} + +/** + * Get IPGRE tunnel remote address + * @arg link Link object + * + * @return remote address on success or a negative error code + */ +uint32_t rtnl_link_ipgre_get_remote(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->remote; +} + +/** + * Set IPGRE tunnel ttl + * @arg link Link object + * @arg ttl tunnel ttl + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_ttl(struct rtnl_link *link, uint8_t ttl) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->ttl = ttl; + ipgre->ipgre_mask |= IPGRE_ATTR_TTL; + + return 0; +} + +/** + * Set IPGRE tunnel ttl + * @arg link Link object + * + * @return ttl value + */ +uint8_t rtnl_link_ipgre_get_ttl(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->ttl; +} + +/** + * Set IPGRE tunnel tos + * @arg link Link object + * @arg tos tunnel tos + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_tos(struct rtnl_link *link, uint8_t tos) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->tos = tos; + ipgre->ipgre_mask |= IPGRE_ATTR_TOS; + + return 0; +} + +/** + * Get IPGRE tunnel tos + * @arg link Link object + * + * @return tos value + */ +uint8_t rtnl_link_ipgre_get_tos(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->tos; +} + +/** + * Set IPGRE tunnel path MTU discovery + * @arg link Link object + * @arg pmtudisc path MTU discovery + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->pmtudisc = pmtudisc; + ipgre->ipgre_mask |= IPGRE_ATTR_PMTUDISC; + + return 0; +} + +/** + * Get IPGRE path MTU discovery + * @arg link Link object + * + * @return pmtudisc value + */ +uint8_t rtnl_link_ipgre_get_pmtudisc(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->pmtudisc; +} + +/* Function prototype for ABI-preserving wrapper (not in public header) to avoid + * GCC warning about missing prototype. */ +uint8_t rtnl_link_get_pmtudisc(struct rtnl_link *link); + +uint8_t rtnl_link_get_pmtudisc(struct rtnl_link *link) +{ + /* rtnl_link_ipgre_get_pmtudisc() was wrongly named. Keep this + * to preserve ABI. */ + return rtnl_link_ipgre_get_pmtudisc (link); +} + +/** + * Set IPGRE tunnel fwmark + * @arg link Link object + * @arg fwmark fwmark + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_fwmark(struct rtnl_link *link, uint32_t fwmark) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->fwmark = fwmark; + ipgre->ipgre_mask |= IPGRE_ATTR_FWMARK; + + return 0; +} + +/** + * Get IPGRE tunnel fwmark + * @arg link Link object + * @arg fwmark addr to fill in with the fwmark + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_get_fwmark(struct rtnl_link *link, uint32_t *fwmark) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + if (!(ipgre->ipgre_mask & IPGRE_ATTR_FWMARK)) + return -NLE_NOATTR; + + *fwmark = ipgre->fwmark; + + return 0; +} + +static void _nl_init ipgre_init(void) +{ + rtnl_link_register_info(&ipgre_info_ops); + rtnl_link_register_info(&ipgretap_info_ops); +} + +static void _nl_exit ipgre_exit(void) +{ + rtnl_link_unregister_info(&ipgre_info_ops); + rtnl_link_unregister_info(&ipgretap_info_ops); +} diff --git a/libnl/lib/route/link/ipip.c b/libnl/lib/route/link/ipip.c new file mode 100644 index 0000000..74db890 --- /dev/null +++ b/libnl/lib/route/link/ipip.c @@ -0,0 +1,593 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +/** + * @ingroup link + * @defgroup ipip IPIP + * ipip link module + * + * @details + * \b Link Type Name: "ipip" + * + * @route_doc{link_ipip, IPIP Documentation} + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" + +#define IPIP_ATTR_LINK (1 << 0) +#define IPIP_ATTR_LOCAL (1 << 1) +#define IPIP_ATTR_REMOTE (1 << 2) +#define IPIP_ATTR_TTL (1 << 3) +#define IPIP_ATTR_TOS (1 << 4) +#define IPIP_ATTR_PMTUDISC (1 << 5) +#define IPIP_ATTR_FWMARK (1 << 6) + +struct ipip_info +{ + uint8_t ttl; + uint8_t tos; + uint8_t pmtudisc; + uint32_t link; + uint32_t local; + uint32_t remote; + uint32_t fwmark; + uint32_t ipip_mask; +}; + +static struct nla_policy ipip_policy[IFLA_IPTUN_MAX + 1] = { + [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, + [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, + [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 }, + [IFLA_IPTUN_TTL] = { .type = NLA_U8 }, + [IFLA_IPTUN_TOS] = { .type = NLA_U8 }, + [IFLA_IPTUN_PMTUDISC] = { .type = NLA_U8 }, + [IFLA_IPTUN_FWMARK] = { .type = NLA_U32 }, +}; + +static int ipip_alloc(struct rtnl_link *link) +{ + struct ipip_info *ipip; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*ipip)); + else { + ipip = calloc(1, sizeof(*ipip)); + if (!ipip) + return -NLE_NOMEM; + + link->l_info = ipip; + } + + return 0; +} + +static int ipip_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_IPTUN_MAX + 1]; + struct ipip_info *ipip; + int err; + + NL_DBG(3, "Parsing IPIP link info\n"); + + err = nla_parse_nested(tb, IFLA_IPTUN_MAX, data, ipip_policy); + if (err < 0) + goto errout; + + err = ipip_alloc(link); + if (err < 0) + goto errout; + + ipip = link->l_info; + + if (tb[IFLA_IPTUN_LINK]) { + ipip->link = nla_get_u32(tb[IFLA_IPTUN_LINK]); + ipip->ipip_mask |= IPIP_ATTR_LINK; + } + + if (tb[IFLA_IPTUN_LOCAL]) { + ipip->local = nla_get_u32(tb[IFLA_IPTUN_LOCAL]); + ipip->ipip_mask |= IPIP_ATTR_LOCAL; + } + + if (tb[IFLA_IPTUN_REMOTE]) { + ipip->remote = nla_get_u32(tb[IFLA_IPTUN_REMOTE]); + ipip->ipip_mask |= IPIP_ATTR_REMOTE; + } + + if (tb[IFLA_IPTUN_TTL]) { + ipip->ttl = nla_get_u8(tb[IFLA_IPTUN_TTL]); + ipip->ipip_mask |= IPIP_ATTR_TTL; + } + + if (tb[IFLA_IPTUN_TOS]) { + ipip->tos = nla_get_u8(tb[IFLA_IPTUN_TOS]); + ipip->ipip_mask |= IPIP_ATTR_TOS; + } + + if (tb[IFLA_IPTUN_PMTUDISC]) { + ipip->pmtudisc = nla_get_u8(tb[IFLA_IPTUN_PMTUDISC]); + ipip->ipip_mask |= IPIP_ATTR_PMTUDISC; + } + + if (tb[IFLA_IPTUN_FWMARK]) { + ipip->fwmark = nla_get_u32(tb[IFLA_IPTUN_FWMARK]); + ipip->ipip_mask |= IPIP_ATTR_FWMARK; + } + + err = 0; + +errout: + return err; +} + +static int ipip_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct ipip_info *ipip = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + + if (ipip->ipip_mask & IPIP_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_IPTUN_LINK, ipip->link); + + if (ipip->ipip_mask & IPIP_ATTR_LOCAL) + NLA_PUT_U32(msg, IFLA_IPTUN_LOCAL, ipip->local); + + if (ipip->ipip_mask & IPIP_ATTR_REMOTE) + NLA_PUT_U32(msg, IFLA_IPTUN_REMOTE, ipip->remote); + + if (ipip->ipip_mask & IPIP_ATTR_TTL) + NLA_PUT_U8(msg, IFLA_IPTUN_TTL, ipip->ttl); + + if (ipip->ipip_mask & IPIP_ATTR_TOS) + NLA_PUT_U8(msg, IFLA_IPTUN_TOS, ipip->tos); + + if (ipip->ipip_mask & IPIP_ATTR_PMTUDISC) + NLA_PUT_U8(msg, IFLA_IPTUN_PMTUDISC, ipip->pmtudisc); + + if (ipip->ipip_mask & IPIP_ATTR_FWMARK) + NLA_PUT_U32(msg, IFLA_IPTUN_FWMARK, ipip->fwmark); + + nla_nest_end(msg, data); + +nla_put_failure: + return 0; +} + +static void ipip_free(struct rtnl_link *link) +{ + struct ipip_info *ipip = link->l_info; + + free(ipip); + link->l_info = NULL; +} + +static void ipip_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + nl_dump(p, "ipip : %s", link->l_name); +} + +static void ipip_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct ipip_info *ipip = link->l_info; + char *name, addr[INET_ADDRSTRLEN]; + struct rtnl_link *parent; + + if (ipip->ipip_mask & IPIP_ATTR_LINK) { + nl_dump(p, " link "); + + name = NULL; + parent = link_lookup(link->ce_cache, ipip->link); + if (parent) + name = rtnl_link_get_name(parent); + + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", ipip->link); + } + + if (ipip->ipip_mask & IPIP_ATTR_LOCAL) { + nl_dump(p, " local "); + if(inet_ntop(AF_INET, &ipip->local, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ntohs(ipip->local)); + } + + if (ipip->ipip_mask & IPIP_ATTR_REMOTE) { + nl_dump(p, " remote "); + if(inet_ntop(AF_INET, &ipip->remote, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ntohs(ipip->remote)); + } + + if (ipip->ipip_mask & IPIP_ATTR_TTL) { + nl_dump(p, " ttl "); + nl_dump_line(p, "%u\n", ipip->ttl); + } + + if (ipip->ipip_mask & IPIP_ATTR_TOS) { + nl_dump(p, " tos "); + nl_dump_line(p, "%u\n", ipip->tos); + } + + if (ipip->ipip_mask & IPIP_ATTR_PMTUDISC) { + nl_dump(p, " pmtudisc "); + nl_dump_line(p, "enabled (%#x)\n", ipip->pmtudisc); + } + + if (ipip->ipip_mask & IPIP_ATTR_FWMARK) { + nl_dump(p, " fwmark "); + nl_dump_line(p, "%x\n", ipip->fwmark); + } +} + +static int ipip_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct ipip_info *ipip_dst, *ipip_src = src->l_info; + int err; + + dst->l_info = NULL; + + err = rtnl_link_set_type(dst, "ipip"); + if (err < 0) + return err; + + ipip_dst = dst->l_info; + + if (!ipip_dst || !ipip_src) + BUG(); + + memcpy(ipip_dst, ipip_src, sizeof(struct ipip_info)); + + return 0; +} + +static struct rtnl_link_info_ops ipip_info_ops = { + .io_name = "ipip", + .io_alloc = ipip_alloc, + .io_parse = ipip_parse, + .io_dump = { + [NL_DUMP_LINE] = ipip_dump_line, + [NL_DUMP_DETAILS] = ipip_dump_details, + }, + .io_clone = ipip_clone, + .io_put_attrs = ipip_put_attrs, + .io_free = ipip_free, +}; + +#define IS_IPIP_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &ipip_info_ops) { \ + APPBUG("Link is not a ipip link. set type \"ipip\" first."); \ + return -NLE_OPNOTSUPP; \ + } + +struct rtnl_link *rtnl_link_ipip_alloc(void) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_alloc(); + if (!link) + return NULL; + + err = rtnl_link_set_type(link, "ipip"); + if (err < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a IPIP link + * @arg link Link object + * + * @return True if link is a IPIP link, otherwise false is returned. + */ +int rtnl_link_is_ipip(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "ipip"); +} + +/** + * Create a new ipip tunnel device + * @arg sock netlink socket + * @arg name name of the tunnel deviceL + * + * Creates a new ipip tunnel device in the kernel + * @return 0 on success or a negative error code + */ +int rtnl_link_ipip_add(struct nl_sock *sk, const char *name) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_ipip_alloc(); + if (!link) + return -NLE_NOMEM; + + if(name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + rtnl_link_put(link); + + return err; +} + +/** + * Set IPIP tunnel interface index + * @arg link Link object + * @arg index interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipip_set_link(struct rtnl_link *link, uint32_t index) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + ipip->link = index; + ipip->ipip_mask |= IPIP_ATTR_LINK; + + return 0; +} + +/** + * Get IPIP tunnel interface index + * @arg link Link object + * + * @return interface index value + */ +uint32_t rtnl_link_ipip_get_link(struct rtnl_link *link) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + return ipip->link; +} + +/** + * Set IPIP tunnel local address + * @arg link Link object + * @arg addr local address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipip_set_local(struct rtnl_link *link, uint32_t addr) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + ipip->local = addr; + ipip->ipip_mask |= IPIP_ATTR_LOCAL; + + return 0; +} + +/** + * Get IPIP tunnel local address + * @arg link Link object + * + * @return local address value + */ +uint32_t rtnl_link_ipip_get_local(struct rtnl_link *link) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + return ipip->local; +} + +/** + * Set IPIP tunnel remote address + * @arg link Link object + * @arg remote remote address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipip_set_remote(struct rtnl_link *link, uint32_t addr) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + ipip->remote = addr; + ipip->ipip_mask |= IPIP_ATTR_REMOTE; + + return 0; +} + +/** + * Get IPIP tunnel remote address + * @arg link Link object + * + * @return remote address + */ +uint32_t rtnl_link_ipip_get_remote(struct rtnl_link *link) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + return ipip->remote; +} + +/** + * Set IPIP tunnel ttl + * @arg link Link object + * @arg ttl tunnel ttl + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipip_set_ttl(struct rtnl_link *link, uint8_t ttl) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + ipip->ttl = ttl; + ipip->ipip_mask |= IPIP_ATTR_TTL; + + return 0; +} + +/** + * Get IPIP tunnel ttl + * @arg link Link object + * + * @return ttl value + */ +uint8_t rtnl_link_ipip_get_ttl(struct rtnl_link *link) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + return ipip->ttl; +} + +/** + * Set IPIP tunnel tos + * @arg link Link object + * @arg tos tunnel tos + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipip_set_tos(struct rtnl_link *link, uint8_t tos) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + ipip->tos = tos; + ipip->ipip_mask |= IPIP_ATTR_TOS; + + return 0; +} + +/** + * Get IPIP tunnel tos + * @arg link Link object + * + * @return tos value + */ +uint8_t rtnl_link_ipip_get_tos(struct rtnl_link *link) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + return ipip->tos; +} + +/** + * Set IPIP tunnel path MTU discovery + * @arg link Link object + * @arg pmtudisc path MTU discovery + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipip_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + ipip->pmtudisc = pmtudisc; + ipip->ipip_mask |= IPIP_ATTR_PMTUDISC; + + return 0; +} + +/** + * Get IPIP path MTU discovery + * @arg link Link object + * + * @return pmtudisc value + */ +uint8_t rtnl_link_ipip_get_pmtudisc(struct rtnl_link *link) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + return ipip->pmtudisc; +} + +/** + * Set IPIP tunnel fwmark + * @arg link Link object + * @arg fwmark fwmark + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipip_set_fwmark(struct rtnl_link *link, uint32_t fwmark) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + ipip->fwmark = fwmark; + ipip->ipip_mask |= IPIP_ATTR_FWMARK; + + return 0; +} + +/** + * Get IPIP tunnel fwmark + * @arg link Link object + * @arg fwmark addr to fill in with the fwmark + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipip_get_fwmark(struct rtnl_link *link, uint32_t *fwmark) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + if (!(ipip->ipip_mask & IPIP_ATTR_FWMARK)) + return -NLE_NOATTR; + + *fwmark = ipip->fwmark; + + return 0; +} + +static void _nl_init ipip_init(void) +{ + rtnl_link_register_info(&ipip_info_ops); +} + +static void _nl_exit ipip_exit(void) +{ + rtnl_link_unregister_info(&ipip_info_ops); +} diff --git a/libnl/lib/route/link/ipvlan.c b/libnl/lib/route/link/ipvlan.c new file mode 100644 index 0000000..807942b --- /dev/null +++ b/libnl/lib/route/link/ipvlan.c @@ -0,0 +1,273 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2015 Cong Wang + */ + +/** + * @ingroup link + * @defgroup ipvlan IPVLAN + * IP-based Virtual LAN link module + * + * @details + * \b Link Type Name: "ipvlan" + * + * @route_doc{link_ipvlan, IPVLAN Documentation} + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" + +/** @cond SKIP */ +#define IPVLAN_HAS_MODE (1<<0) + +struct ipvlan_info +{ + uint16_t ipi_mode; + uint32_t ipi_mask; +}; + +/** @endcond */ + +static struct nla_policy ipvlan_policy[IFLA_IPVLAN_MAX+1] = { + [IFLA_IPVLAN_MODE] = { .type = NLA_U16 }, +}; + +static int ipvlan_alloc(struct rtnl_link *link) +{ + struct ipvlan_info *ipi; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*ipi)); + else { + if ((ipi = calloc(1, sizeof(*ipi))) == NULL) + return -NLE_NOMEM; + + link->l_info = ipi; + } + + return 0; +} + +static int ipvlan_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_IPVLAN_MAX+1]; + struct ipvlan_info *ipi; + int err; + + NL_DBG(3, "Parsing IPVLAN link info\n"); + + if ((err = nla_parse_nested(tb, IFLA_IPVLAN_MAX, data, ipvlan_policy)) < 0) + goto errout; + + if ((err = ipvlan_alloc(link)) < 0) + goto errout; + + ipi = link->l_info; + + if (tb[IFLA_IPVLAN_MODE]) { + ipi->ipi_mode = nla_get_u16(tb[IFLA_IPVLAN_MODE]); + ipi->ipi_mask |= IPVLAN_HAS_MODE; + } + + err = 0; +errout: + return err; +} + +static void ipvlan_free(struct rtnl_link *link) +{ + free(link->l_info); + link->l_info = NULL; +} + +static void ipvlan_dump(struct rtnl_link *link, struct nl_dump_params *p) +{ + char buf[64]; + struct ipvlan_info *ipi = link->l_info; + + if (ipi->ipi_mask & IPVLAN_HAS_MODE) { + rtnl_link_ipvlan_mode2str(ipi->ipi_mode, buf, sizeof(buf)); + nl_dump(p, "ipvlan-mode %s", buf); + } +} + +static int ipvlan_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct ipvlan_info *vdst, *vsrc = src->l_info; + int err; + + dst->l_info = NULL; + if ((err = rtnl_link_set_type(dst, "ipvlan")) < 0) + return err; + vdst = dst->l_info; + + if (!vdst || !vsrc) + return -NLE_NOMEM; + + memcpy(vdst, vsrc, sizeof(struct ipvlan_info)); + + return 0; +} + +static int ipvlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct ipvlan_info *ipi = link->l_info; + struct nlattr *data; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + return -NLE_MSGSIZE; + + if (ipi->ipi_mask & IPVLAN_HAS_MODE) + NLA_PUT_U16(msg, IFLA_IPVLAN_MODE, ipi->ipi_mode); + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static struct rtnl_link_info_ops ipvlan_info_ops = { + .io_name = "ipvlan", + .io_alloc = ipvlan_alloc, + .io_parse = ipvlan_parse, + .io_dump = { + [NL_DUMP_LINE] = ipvlan_dump, + [NL_DUMP_DETAILS] = ipvlan_dump, + }, + .io_clone = ipvlan_clone, + .io_put_attrs = ipvlan_put_attrs, + .io_free = ipvlan_free, +}; + +/** @cond SKIP */ +#define IS_IPVLAN_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &ipvlan_info_ops) { \ + APPBUG("Link is not a ipvlan link. set type \"ipvlan\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +/** + * @name IPVLAN Object + * @{ + */ + +/** + * Allocate link object of type IPVLAN + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_ipvlan_alloc(void) +{ + struct rtnl_link *link; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if (rtnl_link_set_type(link, "ipvlan") < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a IPVLAN link + * @arg link Link object + * + * @return True if link is a IPVLAN link, otherwise false is returned. + */ +int rtnl_link_is_ipvlan(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "ipvlan"); +} + +/** + * Set IPVLAN MODE + * @arg link Link object + * @arg mode IPVLAN mode + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipvlan_set_mode(struct rtnl_link *link, uint16_t mode) +{ + struct ipvlan_info *ipi = link->l_info; + + IS_IPVLAN_LINK_ASSERT(link); + + ipi->ipi_mode = mode; + ipi->ipi_mask |= IPVLAN_HAS_MODE; + + return 0; +} + +/** + * Get IPVLAN Mode + * @arg link Link object + * @arg out_mode on success, return the mode + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_ipvlan_get_mode(struct rtnl_link *link, uint16_t *out_mode) +{ + struct ipvlan_info *ipi = link->l_info; + + IS_IPVLAN_LINK_ASSERT(link); + + if (!(ipi->ipi_mask & IPVLAN_HAS_MODE)) + return -NLE_INVAL; + *out_mode = ipi->ipi_mode; + return 0; +} + +/** @} */ + +static const struct trans_tbl ipvlan_modes[] = { + __ADD(IPVLAN_MODE_L2, l2), + __ADD(IPVLAN_MODE_L3, l3), +}; + +/** + * @name Mode Translation + * @{ + */ + +char *rtnl_link_ipvlan_mode2str(int mode, char *buf, size_t len) +{ + return __type2str(mode, buf, len, ipvlan_modes, ARRAY_SIZE(ipvlan_modes)); +} + +int rtnl_link_ipvlan_str2mode(const char *name) +{ + return __str2type(name, ipvlan_modes, ARRAY_SIZE(ipvlan_modes)); +} + +/** @} */ + +static void _nl_init ipvlan_init(void) +{ + rtnl_link_register_info(&ipvlan_info_ops); +} + +static void _nl_exit ipvlan_exit(void) +{ + rtnl_link_unregister_info(&ipvlan_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/ipvti.c b/libnl/lib/route/link/ipvti.c new file mode 100644 index 0000000..fd57895 --- /dev/null +++ b/libnl/lib/route/link/ipvti.c @@ -0,0 +1,542 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +/** + * @ingroup link + * @defgroup ipvti IPVTI + * ipvti link module + * + * @details + * \b Link Type Name: "ipvti" + * + * @route_doc{link_ipvti, IPVTI Documentation} + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" + +#define IPVTI_ATTR_LINK (1 << 0) +#define IPVTI_ATTR_IKEY (1 << 1) +#define IPVTI_ATTR_OKEY (1 << 2) +#define IPVTI_ATTR_LOCAL (1 << 3) +#define IPVTI_ATTR_REMOTE (1 << 4) +#define IPVTI_ATTR_FWMARK (1 << 5) + +struct ipvti_info +{ + uint32_t link; + uint32_t ikey; + uint32_t okey; + uint32_t local; + uint32_t remote; + uint32_t fwmark; + uint32_t ipvti_mask; +}; + +static struct nla_policy ipvti_policy[IFLA_VTI_MAX + 1] = { + [IFLA_VTI_LINK] = { .type = NLA_U32 }, + [IFLA_VTI_IKEY] = { .type = NLA_U32 }, + [IFLA_VTI_OKEY] = { .type = NLA_U32 }, + [IFLA_VTI_LOCAL] = { .type = NLA_U32 }, + [IFLA_VTI_REMOTE] = { .type = NLA_U32 }, + [IFLA_VTI_FWMARK] = { .type = NLA_U32 }, +}; + +static int ipvti_alloc(struct rtnl_link *link) +{ + struct ipvti_info *ipvti; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*ipvti)); + else { + ipvti = calloc(1, sizeof(*ipvti)); + if (!ipvti) + return -NLE_NOMEM; + + link->l_info = ipvti; + } + + return 0; +} + +static int ipvti_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_VTI_MAX + 1]; + struct ipvti_info *ipvti; + int err; + + NL_DBG(3, "Parsing IPVTI link info\n"); + + err = nla_parse_nested(tb, IFLA_VTI_MAX, data, ipvti_policy); + if (err < 0) + goto errout; + + err = ipvti_alloc(link); + if (err < 0) + goto errout; + + ipvti = link->l_info; + + if (tb[IFLA_VTI_LINK]) { + ipvti->link = nla_get_u32(tb[IFLA_VTI_LINK]); + ipvti->ipvti_mask |= IPVTI_ATTR_LINK; + } + + if (tb[IFLA_VTI_IKEY]) { + ipvti->ikey = nla_get_u32(tb[IFLA_VTI_IKEY]); + ipvti->ipvti_mask |= IPVTI_ATTR_IKEY; + } + + if (tb[IFLA_VTI_OKEY]) { + ipvti->okey = nla_get_u32(tb[IFLA_VTI_OKEY]); + ipvti->ipvti_mask |= IPVTI_ATTR_OKEY; + } + + if (tb[IFLA_VTI_LOCAL]) { + ipvti->local = nla_get_u32(tb[IFLA_VTI_LOCAL]); + ipvti->ipvti_mask |= IPVTI_ATTR_LOCAL; + } + + if (tb[IFLA_VTI_REMOTE]) { + ipvti->remote = nla_get_u32(tb[IFLA_VTI_REMOTE]); + ipvti->ipvti_mask |= IPVTI_ATTR_REMOTE; + } + + if (tb[IFLA_VTI_FWMARK]) { + ipvti->fwmark = nla_get_u32(tb[IFLA_VTI_FWMARK]); + ipvti->ipvti_mask |= IPVTI_ATTR_FWMARK; + } + + err = 0; + +errout: + return err; +} + +static int ipvti_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct ipvti_info *ipvti = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + + if (ipvti->ipvti_mask & IPVTI_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_VTI_LINK, ipvti->link); + + if (ipvti->ipvti_mask & IPVTI_ATTR_IKEY) + NLA_PUT_U32(msg, IFLA_VTI_IKEY, ipvti->ikey); + + if (ipvti->ipvti_mask & IFLA_VTI_IKEY) + NLA_PUT_U32(msg, IFLA_VTI_OKEY, ipvti->okey); + + if (ipvti->ipvti_mask & IPVTI_ATTR_LOCAL) + NLA_PUT_U32(msg, IFLA_VTI_LOCAL, ipvti->local); + + if (ipvti->ipvti_mask & IPVTI_ATTR_REMOTE) + NLA_PUT_U32(msg, IFLA_VTI_REMOTE, ipvti->remote); + + if (ipvti->ipvti_mask & IPVTI_ATTR_FWMARK) + NLA_PUT_U32(msg, IFLA_VTI_FWMARK, ipvti->fwmark); + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static void ipvti_free(struct rtnl_link *link) +{ + struct ipvti_info *ipvti = link->l_info; + + free(ipvti); + link->l_info = NULL; +} + +static void ipvti_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + nl_dump(p, "ipvti : %s", link->l_name); +} + +static void ipvti_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct ipvti_info *ipvti = link->l_info; + char *name, addr[INET_ADDRSTRLEN]; + struct rtnl_link *parent; + + if (ipvti->ipvti_mask & IPVTI_ATTR_LINK) { + nl_dump(p, " link "); + + name = NULL; + parent = link_lookup(link->ce_cache, ipvti->link); + if (parent) + name = rtnl_link_get_name(parent); + + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", ipvti->link); + } + + if (ipvti->ipvti_mask & IPVTI_ATTR_IKEY) { + nl_dump(p, " ikey "); + nl_dump_line(p, "%x\n",ipvti->ikey); + } + + if (ipvti->ipvti_mask & IPVTI_ATTR_OKEY) { + nl_dump(p, " okey "); + nl_dump_line(p, "%x\n", ipvti->okey); + } + + if (ipvti->ipvti_mask & IPVTI_ATTR_LOCAL) { + nl_dump(p, " local "); + if(inet_ntop(AF_INET, &ipvti->local, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ntohs(ipvti->local)); + } + + if (ipvti->ipvti_mask & IPVTI_ATTR_REMOTE) { + nl_dump(p, " remote "); + if(inet_ntop(AF_INET, &ipvti->remote, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ntohs(ipvti->remote)); + } + + if (ipvti->ipvti_mask & IPVTI_ATTR_FWMARK) { + nl_dump(p, " fwmark "); + nl_dump_line(p, "%x\n", ipvti->fwmark); + } +} + +static int ipvti_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct ipvti_info *ipvti_dst, *ipvti_src = src->l_info; + int err; + + dst->l_info = NULL; + + err = rtnl_link_set_type(dst, "vti"); + if (err < 0) + return err; + + ipvti_dst = dst->l_info; + + if (!ipvti_dst || !ipvti_src) + BUG(); + + memcpy(ipvti_dst, ipvti_src, sizeof(struct ipvti_info)); + + return 0; +} + +static struct rtnl_link_info_ops ipvti_info_ops = { + .io_name = "vti", + .io_alloc = ipvti_alloc, + .io_parse = ipvti_parse, + .io_dump = { + [NL_DUMP_LINE] = ipvti_dump_line, + [NL_DUMP_DETAILS] = ipvti_dump_details, + }, + .io_clone = ipvti_clone, + .io_put_attrs = ipvti_put_attrs, + .io_free = ipvti_free, +}; + +#define IS_IPVTI_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &ipvti_info_ops) { \ + APPBUG("Link is not a ipvti link. set type \vti\" first."); \ + return -NLE_OPNOTSUPP; \ + } + +struct rtnl_link *rtnl_link_ipvti_alloc(void) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_alloc(); + if (!link) + return NULL; + + err = rtnl_link_set_type(link, "vti"); + if (err < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a IPVTI link + * @arg link Link object + * + * @return True if link is a IPVTI link, otherwise 0 is returned. + */ +int rtnl_link_is_ipvti(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "vti"); +} +/** + * Create a new ipvti tunnel device + * @arg sock netlink socket + * @arg name name of the tunnel deviceL + * + * Creates a new ipvti tunnel device in the kernel + * @return 0 on success or a negative error code + */ +int rtnl_link_ipvti_add(struct nl_sock *sk, const char *name) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_ipvti_alloc(); + if (!link) + return -NLE_NOMEM; + + if(name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + rtnl_link_put(link); + + return err; +} +/** + * Set IPVTI tunnel interface index + * @arg link Link object + * @arg index interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipvti_set_link(struct rtnl_link *link, uint32_t index) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + ipvti->link = index; + ipvti->ipvti_mask |= IPVTI_ATTR_LINK; + + return 0; +} + +/** + * Get IPVTI tunnel interface index + * @arg link Link object + * + * @return interface index + */ +uint32_t rtnl_link_ipvti_get_link(struct rtnl_link *link) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + return ipvti->link; +} + +/** + * Set IPVTI tunnel set ikey + * @arg link Link object + * @arg ikey gre ikey + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipvti_set_ikey(struct rtnl_link *link, uint32_t ikey) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + ipvti->ikey = ikey; + ipvti->ipvti_mask |= IPVTI_ATTR_IKEY; + + return 0; +} + +/** + * Get IPVTI tunnel ikey + * @arg link Link object + * + * @return ikey + */ +uint32_t rtnl_link_ipvti_get_ikey(struct rtnl_link *link) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + return ipvti->ikey; +} + +/** + * Set IPVTI tunnel set okey + * @arg link Link object + * @arg okey gre okey + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipvti_set_okey(struct rtnl_link *link, uint32_t okey) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + ipvti->okey = okey; + ipvti->ipvti_mask |= IPVTI_ATTR_OKEY; + + return 0; +} + +/** + * Get IPVTI tunnel okey + * @arg link Link object + * + * @return okey value + */ +uint32_t rtnl_link_ipvti_get_okey(struct rtnl_link *link) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + return ipvti->okey; +} + +/** + * Set IPVTI tunnel local address + * @arg link Link object + * @arg addr local address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipvti_set_local(struct rtnl_link *link, uint32_t addr) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + ipvti->local = addr; + ipvti->ipvti_mask |= IPVTI_ATTR_LOCAL; + + return 0; +} + +/** + * Get IPVTI tunnel local address + * @arg link Link object + * + * @return local address + */ +uint32_t rtnl_link_ipvti_get_local(struct rtnl_link *link) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + return ipvti->local; +} + +/** + * Set IPVTI tunnel remote address + * @arg link Link object + * @arg remote remote address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipvti_set_remote(struct rtnl_link *link, uint32_t remote) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + ipvti->remote = remote; + ipvti->ipvti_mask |= IPVTI_ATTR_REMOTE; + + return 0; +} + +/** + * Get IPVTI tunnel remote address + * @arg link Link object + * + * @return remote address on success or a negative error code + */ +uint32_t rtnl_link_ipvti_get_remote(struct rtnl_link *link) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + return ipvti->remote; +} + +/** + * Set IPVTI tunnel fwmark + * @arg link Link object + * @arg fwmark fwmark + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipvti_set_fwmark(struct rtnl_link *link, uint32_t fwmark) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + ipvti->fwmark = fwmark; + ipvti->ipvti_mask |= IPVTI_ATTR_FWMARK; + + return 0; +} + +/** + * Get IPVTI tunnel fwmark + * @arg link Link object + * @arg fwmark addr to fill in with the fwmark + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipvti_get_fwmark(struct rtnl_link *link, uint32_t *fwmark) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + if (!(ipvti->ipvti_mask & IPVTI_ATTR_FWMARK)) + return -NLE_NOATTR; + + *fwmark = ipvti->fwmark; + + return 0; +} + +static void _nl_init ipvti_init(void) +{ + rtnl_link_register_info(&ipvti_info_ops); +} + +static void _nl_exit ipvti_exit(void) +{ + rtnl_link_unregister_info(&ipvti_info_ops); +} diff --git a/libnl/lib/route/link/link-api.h b/libnl/lib/route/link/link-api.h new file mode 100644 index 0000000..0e54057 --- /dev/null +++ b/libnl/lib/route/link/link-api.h @@ -0,0 +1,191 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_LINK_API_H_ +#define NETLINK_LINK_API_H_ + +#include +#include + +#include "nl-priv-dynamic-core/nl-core.h" + +/** + * @ingroup link_api + * + * Available operations to modules implementing a link info type. + */ +struct rtnl_link_info_ops +{ + /** Name of link info type, must match name on kernel side */ + char * io_name; + + /** Reference count, DO NOT MODIFY */ + int io_refcnt; + + /** Called to assign an info type to a link. + * Has to allocate enough resources to hold attributes. Can + * use link->l_info to store a pointer. */ + int (*io_alloc)(struct rtnl_link *); + + /** Called to parse the link info attribute. + * Must parse the attribute and assign all values to the link. + */ + int (*io_parse)(struct rtnl_link *, + struct nlattr *, + struct nlattr *); + + /** Called when the link object is dumped. + * Must dump the info type specific attributes. */ + void (*io_dump[NL_DUMP_MAX+1])(struct rtnl_link *, + struct nl_dump_params *); + + /** Called when a link object is cloned. + * Must clone all info type specific attributes. */ + int (*io_clone)(struct rtnl_link *, struct rtnl_link *); + + /** Called when construction a link netlink message. + * Must append all info type specific attributes to the message. */ + int (*io_put_attrs)(struct nl_msg *, struct rtnl_link *); + + /** Called to release all resources previously allocated + * in either io_alloc() or io_parse(). */ + void (*io_free)(struct rtnl_link *); + + /** Called to compare link info parameters between two links. */ + int (*io_compare)(struct rtnl_link *, struct rtnl_link *, + int flags); + + struct nl_list_head io_list; +}; + +extern struct rtnl_link_info_ops *rtnl_link_info_ops_lookup(const char *); +extern void rtnl_link_info_ops_get(struct rtnl_link_info_ops *); +extern void rtnl_link_info_ops_put(struct rtnl_link_info_ops *); +extern int rtnl_link_register_info(struct rtnl_link_info_ops *); +extern int rtnl_link_unregister_info(struct rtnl_link_info_ops *); + + +/** + * @ingroup link_api + * + * Available operations to modules implementing a link address family. + */ +struct rtnl_link_af_ops +{ + /** The address family this operations set implements */ + const unsigned int ao_family; + + /** Number of users of this operations, DO NOT MODIFY. */ + int ao_refcnt; + + /** Validation policy for IFLA_PROTINFO attribute. This pointer + * can be set to a nla_policy structure describing the minimal + * requirements the attribute must meet. Failure of meeting these + * requirements will result in a parsing error. */ + const struct nla_policy *ao_protinfo_policy; + + /** Called after address family has been assigned to link. Must + * allocate data buffer to hold address family specific data and + * store it in link->l_af_data. */ + void * (*ao_alloc)(struct rtnl_link *); + + /** Called when the link is cloned, must allocate a clone of the + * address family specific buffer and return it. */ + void * (*ao_clone)(struct rtnl_link *, void *); + + /** Called when the link gets freed. Must free all allocated data */ + void (*ao_free)(struct rtnl_link *, void *); + + /** Called if a IFLA_PROTINFO attribute needs to be parsed. Typically + * stores the parsed data in the address family specific buffer. */ + int (*ao_parse_protinfo)(struct rtnl_link *, + struct nlattr *, void *); + + /** Called if a IFLA_AF_SPEC attribute needs to be parsed. Typically + * stores the parsed data in the address family specific buffer. */ + int (*ao_parse_af)(struct rtnl_link *, + struct nlattr *, void *); + + /** Called if a link message is sent to the kernel. Must append the + * link address family specific attributes to the message. */ + int (*ao_fill_af)(struct rtnl_link *, + struct nl_msg *msg, void *); + + /** Called if the full IFLA_AF_SPEC data needs to be parsed. Typically + * stores the parsed data in the address family specific buffer. */ + int (*ao_parse_af_full)(struct rtnl_link *, + struct nlattr *, void *); + + /** Called for GETLINK message to the kernel. Used to append + * link address family specific attributes to the request message. */ + int (*ao_get_af)(struct nl_msg *msg, + uint32_t *ext_filter_mask); + + /** Dump address family specific link attributes */ + void (*ao_dump[NL_DUMP_MAX+1])(struct rtnl_link *, + struct nl_dump_params *, + void *); + + /** Comparison function + * + * Will be called when two links are compared for their af data. It + * takes two link objects in question, an object specific bitmask + * defining which attributes should be compared and flags to control + * the behaviour + * + * The function must return a bitmask with the relevant bit set for + * each attribute that mismatches + */ + int (*ao_compare)(struct rtnl_link *, + struct rtnl_link *, int, uint32_t, int); + + /* RTM_NEWLINK override + * + * Called if a change link request is set to the kernel. If this returns + * anything other than zero, RTM_NEWLINK will be overriden with + * RTM_SETLINK when rtnl_link_build_change_request() is called. + */ + int (*ao_override_rtm)(struct rtnl_link *); + + /** Called if a link message is sent to the kernel. Must append the + * link protocol specific attributes to the message. (IFLA_PROTINFO) */ + int (*ao_fill_pi)(struct rtnl_link *, + struct nl_msg *msg, void *); + + /** PROTINFO type + * + * Called if a link message is sent to the kernel. If this is set, + * the default IFLA_PROTINFO is bitmasked with what is specified + * here. (eg. NLA_F_NESTED) + */ + const int ao_fill_pi_flags; + + /** IFLA_AF_SPEC nesting override + * + * Called if a link message is sent to the kernel. If this is set, + * the AF specific nest is not created. Instead, AF specific attributes + * are nested directly in the IFLA_AF_SPEC attribute. + */ + const int ao_fill_af_no_nest; +}; + +extern struct rtnl_link_af_ops *rtnl_link_af_ops_lookup(unsigned int); +extern void rtnl_link_af_ops_put(struct rtnl_link_af_ops *); +extern void * rtnl_link_af_alloc(struct rtnl_link *, + const struct rtnl_link_af_ops *); +extern void * rtnl_link_af_data(const struct rtnl_link *, + const struct rtnl_link_af_ops *); +extern int rtnl_link_af_register(struct rtnl_link_af_ops *); +extern int rtnl_link_af_unregister(struct rtnl_link_af_ops *); +extern int rtnl_link_af_data_compare(struct rtnl_link *a, + struct rtnl_link *b, + int family); +extern int rtnl_link_info_data_compare(struct rtnl_link *a, + struct rtnl_link *b, + int flags); + +extern struct rtnl_link *link_lookup(struct nl_cache *cache, int ifindex); + +#endif diff --git a/libnl/lib/route/link/macsec.c b/libnl/lib/route/link/macsec.c new file mode 100644 index 0000000..7f4f568 --- /dev/null +++ b/libnl/lib/route/link/macsec.c @@ -0,0 +1,895 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Sabrina Dubroca + */ + +/** + * @ingroup link + * @defgroup macsec MACsec + * MACsec link module + * + * @details + * \b Link Type Name: "macsec" + * + * @route_doc{link_macsec, MACsec Documentation} + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" +#include "nl-priv-dynamic-core/nl-core.h" + +/** @cond SKIP */ +#define MACSEC_ATTR_SCI (1 << 0) +#define MACSEC_ATTR_ICV_LEN (1 << 1) +#define MACSEC_ATTR_CIPHER_SUITE (1 << 2) +#define MACSEC_ATTR_WINDOW (1 << 3) +#define MACSEC_ATTR_ENCODING_SA (1 << 4) +#define MACSEC_ATTR_ENCRYPT (1 << 5) +#define MACSEC_ATTR_PROTECT (1 << 6) +#define MACSEC_ATTR_INC_SCI (1 << 7) +#define MACSEC_ATTR_ES (1 << 8) +#define MACSEC_ATTR_SCB (1 << 9) +#define MACSEC_ATTR_REPLAY_PROTECT (1 << 10) +#define MACSEC_ATTR_VALIDATION (1 << 11) +#define MACSEC_ATTR_PORT (1 << 12) +#define MACSEC_ATTR_OFFLOAD (1 << 13) + +struct macsec_info { + int ifindex; + uint64_t sci; + uint16_t port; + uint64_t cipher_suite; + uint16_t icv_len; + uint32_t window; + enum macsec_validation_type validate; + uint8_t encoding_sa; + + uint8_t send_sci, end_station, scb, replay_protect, protect, encrypt, offload; + + uint32_t ce_mask; +}; + +#define DEFAULT_ICV_LEN 16 + +/** @endcond */ + +static struct nla_policy macsec_policy[IFLA_MACSEC_MAX+1] = { + [IFLA_MACSEC_SCI] = { .type = NLA_U64 }, + [IFLA_MACSEC_ICV_LEN] = { .type = NLA_U8 }, + [IFLA_MACSEC_CIPHER_SUITE] = { .type = NLA_U64 }, + [IFLA_MACSEC_WINDOW] = { .type = NLA_U32 }, + [IFLA_MACSEC_ENCODING_SA] = { .type = NLA_U8 }, + [IFLA_MACSEC_ENCRYPT] = { .type = NLA_U8 }, + [IFLA_MACSEC_PROTECT] = { .type = NLA_U8 }, + [IFLA_MACSEC_INC_SCI] = { .type = NLA_U8 }, + [IFLA_MACSEC_ES] = { .type = NLA_U8 }, + [IFLA_MACSEC_SCB] = { .type = NLA_U8 }, + [IFLA_MACSEC_REPLAY_PROTECT] = { .type = NLA_U8 }, + [IFLA_MACSEC_VALIDATION] = { .type = NLA_U8 }, + [IFLA_MACSEC_OFFLOAD] = { .type = NLA_U8 }, +}; + +/** + * @name MACsec Object + * @{ + */ + +/** + * Allocate link object of type MACsec + * + * @return Allocated link object or NULL. + */ +static int macsec_alloc(struct rtnl_link *link) +{ + struct macsec_info *info; + + if (!link->l_info) { + link->l_info = malloc(sizeof(struct macsec_info)); + if (!link->l_info) + return -NLE_NOMEM; + } + + memset(link->l_info, 0, sizeof(struct macsec_info)); + info = link->l_info; + + info->cipher_suite = MACSEC_DEFAULT_CIPHER_ID; + info->icv_len = DEFAULT_ICV_LEN; + info->ce_mask = MACSEC_ATTR_CIPHER_SUITE | MACSEC_ATTR_ICV_LEN; + + return 0; +} + +static int macsec_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_MACSEC_MAX+1]; + struct macsec_info *info; + int err; + + NL_DBG(3, "Parsing MACsec link info\n"); + + if ((err = nla_parse_nested(tb, IFLA_MACSEC_MAX, data, macsec_policy)) < 0) + goto errout; + + if ((err = macsec_alloc(link)) < 0) + goto errout; + + info = link->l_info; + + if (tb[IFLA_MACSEC_SCI]) { + info->sci = nla_get_u64(tb[IFLA_MACSEC_SCI]); + info->ce_mask |= MACSEC_ATTR_SCI; + } + + if (tb[IFLA_MACSEC_PROTECT]) { + info->protect = nla_get_u8(tb[IFLA_MACSEC_PROTECT]); + info->ce_mask |= MACSEC_ATTR_PROTECT; + } + + if (tb[IFLA_MACSEC_CIPHER_SUITE]) { + info->cipher_suite = nla_get_u64(tb[IFLA_MACSEC_CIPHER_SUITE]); + info->ce_mask |= MACSEC_ATTR_CIPHER_SUITE; + } + + if (tb[IFLA_MACSEC_ICV_LEN]) { + info->icv_len = nla_get_u8(tb[IFLA_MACSEC_ICV_LEN]); + info->ce_mask |= MACSEC_ATTR_ICV_LEN; + } + + if (tb[IFLA_MACSEC_ENCODING_SA]) { + info->encoding_sa = nla_get_u8(tb[IFLA_MACSEC_ENCODING_SA]); + info->ce_mask |= MACSEC_ATTR_ENCODING_SA; + } + + if (tb[IFLA_MACSEC_VALIDATION]) { + info->validate = nla_get_u8(tb[IFLA_MACSEC_VALIDATION]); + info->ce_mask |= MACSEC_ATTR_VALIDATION; + } + + if (tb[IFLA_MACSEC_ENCRYPT]) { + info->encrypt = nla_get_u8(tb[IFLA_MACSEC_ENCRYPT]); + info->ce_mask |= MACSEC_ATTR_ENCRYPT; + } + + if (tb[IFLA_MACSEC_OFFLOAD]) { + info->offload = nla_get_u8(tb[IFLA_MACSEC_OFFLOAD]); + info->ce_mask |= MACSEC_ATTR_OFFLOAD; + } + + if (tb[IFLA_MACSEC_INC_SCI]) { + info->send_sci = nla_get_u8(tb[IFLA_MACSEC_INC_SCI]); + info->ce_mask |= MACSEC_ATTR_INC_SCI; + } + + if (tb[IFLA_MACSEC_ES]) { + info->end_station = nla_get_u8(tb[IFLA_MACSEC_ES]); + info->ce_mask |= MACSEC_ATTR_ES; + } + + if (tb[IFLA_MACSEC_SCB]) { + info->scb = nla_get_u8(tb[IFLA_MACSEC_SCB]); + info->ce_mask |= MACSEC_ATTR_SCB; + } + + if (tb[IFLA_MACSEC_REPLAY_PROTECT]) { + info->replay_protect = nla_get_u8(tb[IFLA_MACSEC_REPLAY_PROTECT]); + info->ce_mask |= MACSEC_ATTR_REPLAY_PROTECT; + } + + if (tb[IFLA_MACSEC_WINDOW]) { + info->window = nla_get_u32(tb[IFLA_MACSEC_WINDOW]); + info->ce_mask |= MACSEC_ATTR_WINDOW; + } + + err = 0; +errout: + return err; +} + +static void macsec_free(struct rtnl_link *link) +{ + free(link->l_info); + link->l_info = NULL; +} + +static const char *values_on_off[] = { "off", "on" }; + +static const char *VALIDATE_STR[] = { + [MACSEC_VALIDATE_DISABLED] = "disabled", + [MACSEC_VALIDATE_CHECK] = "check", + [MACSEC_VALIDATE_STRICT] = "strict", +}; + +static char *replay_protect_str(char *buf, uint8_t replay_protect, uint8_t window) +{ + if (replay_protect == 1) { + sprintf(buf, "replay_protect on window %d", window); + } else if (replay_protect == 0) { + sprintf(buf, "replay_protect off"); + } else { + buf[0] = '\0'; + } + + return buf; +} + +/** @cond SKIP */ +#define PRINT_FLAG(buf, i, field, c) ({ if (i->field == 1) *buf++ = c; }) +/** @endcond */ +static char *flags_str(char *buf, unsigned char len, struct macsec_info *info) +{ + char *tmp = buf; + memset(tmp, 0, len); + + PRINT_FLAG(tmp, info, protect, 'P'); + PRINT_FLAG(tmp, info, encrypt, 'E'); + PRINT_FLAG(tmp, info, send_sci, 'S'); + PRINT_FLAG(tmp, info, end_station, 'e'); + PRINT_FLAG(tmp, info, scb, 's'); + PRINT_FLAG(tmp, info, replay_protect, 'R'); + + *tmp++ = ' '; + *tmp++ = 'v'; + switch (info->validate) { + case MACSEC_VALIDATE_DISABLED: + *tmp++ = 'd'; + break; + case MACSEC_VALIDATE_CHECK: + *tmp++ = 'c'; + break; + case MACSEC_VALIDATE_STRICT: + *tmp++ = 's'; + break; + default: + break; + } + + sprintf(tmp, " %d", info->encoding_sa); + + return buf; +} + +static void macsec_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct macsec_info *info = link->l_info; + char tmp[128]; + + nl_dump(p, "sci %016llx <%s>", (long long unsigned)ntohll(info->sci), + flags_str(tmp, sizeof(tmp), info)); +} + +static void macsec_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct macsec_info *info = link->l_info; + char tmp[128]; + + nl_dump(p, + " sci %016llx protect %s encoding_sa %d encrypt %s send_sci %s validate %s %s\n", + (long long unsigned)ntohll(info->sci), + values_on_off[info->protect], info->encoding_sa, + values_on_off[info->encrypt], values_on_off[info->send_sci], + VALIDATE_STR[info->validate], + replay_protect_str(tmp, info->replay_protect, info->window)); + nl_dump(p, " cipher suite: %016llx, icv_len %d\n", + (long long unsigned)info->cipher_suite, info->icv_len); +} + +static int macsec_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct macsec_info *copy, *info = src->l_info; + int err; + + dst->l_info = NULL; + if ((err = rtnl_link_set_type(dst, "macsec")) < 0) + return err; + copy = dst->l_info; + + if (!info || !copy) + return -NLE_NOMEM; + + memcpy(copy, info, sizeof(struct macsec_info)); + + return 0; +} + +static int macsec_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct macsec_info *info = link->l_info; + struct nlattr *data; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + return -NLE_MSGSIZE; + + if (info->ce_mask & MACSEC_ATTR_SCI) + NLA_PUT_U64(msg, IFLA_MACSEC_SCI, info->sci); + else if (info->ce_mask & MACSEC_ATTR_PORT) + NLA_PUT_U16(msg, IFLA_MACSEC_PORT, htons(info->port)); + + if ((info->ce_mask & MACSEC_ATTR_ENCRYPT)) + NLA_PUT_U8(msg, IFLA_MACSEC_ENCRYPT, info->encrypt); + + if ((info->ce_mask & MACSEC_ATTR_OFFLOAD)) + NLA_PUT_U8(msg, IFLA_MACSEC_OFFLOAD, info->offload); + + if (info->cipher_suite != MACSEC_DEFAULT_CIPHER_ID || info->icv_len != DEFAULT_ICV_LEN) { + NLA_PUT_U64(msg, IFLA_MACSEC_CIPHER_SUITE, info->cipher_suite); + NLA_PUT_U8(msg, IFLA_MACSEC_ICV_LEN, info->icv_len); + } + + if ((info->ce_mask & MACSEC_ATTR_INC_SCI)) + NLA_PUT_U8(msg, IFLA_MACSEC_INC_SCI, info->send_sci); + + if ((info->ce_mask & MACSEC_ATTR_ES)) + NLA_PUT_U8(msg, IFLA_MACSEC_ES, info->end_station); + + if ((info->ce_mask & MACSEC_ATTR_SCB)) + NLA_PUT_U8(msg, IFLA_MACSEC_SCB, info->scb); + + if ((info->ce_mask & MACSEC_ATTR_PROTECT)) + NLA_PUT_U8(msg, IFLA_MACSEC_PROTECT, info->protect); + + if ((info->ce_mask & MACSEC_ATTR_REPLAY_PROTECT)) { + if (info->replay_protect && !(info->ce_mask & MACSEC_ATTR_WINDOW)) + return -NLE_INVAL; + + NLA_PUT_U8(msg, IFLA_MACSEC_REPLAY_PROTECT, info->replay_protect); + NLA_PUT_U32(msg, IFLA_MACSEC_WINDOW, info->window); + } + + if ((info->ce_mask & MACSEC_ATTR_VALIDATION)) + NLA_PUT_U8(msg, IFLA_MACSEC_VALIDATION, info->validate); + + if ((info->ce_mask & MACSEC_ATTR_ENCODING_SA)) + NLA_PUT_U8(msg, IFLA_MACSEC_ENCODING_SA, info->encoding_sa); + + nla_nest_end(msg, data); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int macsec_compare(struct rtnl_link *link_a, struct rtnl_link *link_b, + int flags) +{ + struct macsec_info *a = link_a->l_info; + struct macsec_info *b = link_b->l_info; + int diff = 0; + uint32_t attrs = flags & LOOSE_COMPARISON ? b->ce_mask : + ~((uint32_t)0u); + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + if (a->ce_mask & MACSEC_ATTR_SCI && b->ce_mask & MACSEC_ATTR_SCI) + diff |= _DIFF(MACSEC_ATTR_SCI, a->sci != b->sci); + else if (a->ce_mask & MACSEC_ATTR_PORT && b->ce_mask & MACSEC_ATTR_PORT) + diff |= _DIFF(MACSEC_ATTR_PORT, a->port != b->port); + + if (a->ce_mask & MACSEC_ATTR_CIPHER_SUITE && + b->ce_mask & MACSEC_ATTR_CIPHER_SUITE) { + diff |= _DIFF(MACSEC_ATTR_ICV_LEN, a->icv_len != b->icv_len); + diff |= _DIFF(MACSEC_ATTR_CIPHER_SUITE, + a->cipher_suite != b->cipher_suite); + } + + if (a->ce_mask & MACSEC_ATTR_REPLAY_PROTECT && + b->ce_mask & MACSEC_ATTR_REPLAY_PROTECT) { + int d = _DIFF(MACSEC_ATTR_REPLAY_PROTECT, + a->replay_protect != b->replay_protect); + if (a->replay_protect && b->replay_protect) { + d |= _DIFF(MACSEC_ATTR_WINDOW, a->window != b->window); + } + diff |= d; + } + + diff |= _DIFF(MACSEC_ATTR_ENCODING_SA, + a->encoding_sa != b->encoding_sa); + diff |= _DIFF(MACSEC_ATTR_ENCRYPT, a->encrypt != b->encrypt); + diff |= _DIFF(MACSEC_ATTR_PROTECT, a->protect != b->protect); + diff |= _DIFF(MACSEC_ATTR_INC_SCI, a->send_sci != b->send_sci); + diff |= _DIFF(MACSEC_ATTR_ES, a->end_station != b->end_station); + diff |= _DIFF(MACSEC_ATTR_SCB, a->scb != b->scb); + diff |= _DIFF(MACSEC_ATTR_VALIDATION, a->validate != b->validate); +#undef _DIFF + + return diff; +} + + +static struct rtnl_link_info_ops macsec_info_ops = { + .io_name = "macsec", + .io_alloc = macsec_alloc, + .io_parse = macsec_parse, + .io_dump = { + [NL_DUMP_LINE] = macsec_dump_line, + [NL_DUMP_DETAILS] = macsec_dump_details, + }, + .io_clone = macsec_clone, + .io_put_attrs = macsec_put_attrs, + .io_free = macsec_free, + .io_compare = macsec_compare, +}; + +static void _nl_init macsec_init(void) +{ + rtnl_link_register_info(&macsec_info_ops); +} + +static void _nl_exit macsec_exit(void) +{ + rtnl_link_unregister_info(&macsec_info_ops); +} + +/** @cond SKIP */ +#define IS_MACSEC_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &macsec_info_ops) { \ + APPBUG("Link is not a MACsec link. set type \"macsec\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +struct rtnl_link *rtnl_link_macsec_alloc(void) +{ + struct rtnl_link *link = rtnl_link_alloc(); + + if (!link) + return NULL; + + if (rtnl_link_set_type(link, "macsec") < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Set SCI + * @arg link Link object + * @arg sci Secure Channel Identifier in network byte order + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macsec_set_sci(struct rtnl_link *link, uint64_t sci) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + info->sci = sci; + info->ce_mask |= MACSEC_ATTR_SCI; + + return 0; +} + +/** + * Get SCI + * @arg link Link object + * @arg sci On return points to the Secure Channel Identifier + * in network byte order + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macsec_get_sci(struct rtnl_link *link, uint64_t *sci) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_SCI)) + return -NLE_NOATTR; + + if (sci) + *sci = info->sci; + + return 0; +} + +/** + * Set port identifier + * @arg link Link object + * @arg port Port identifier in host byte order + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macsec_set_port(struct rtnl_link *link, uint16_t port) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + info->port = port; + info->ce_mask |= MACSEC_ATTR_PORT; + + return 0; +} + +/** + * Get port identifier + * @arg link Link object + * @arg port On return points to the port identifier in host byte order + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macsec_get_port(struct rtnl_link *link, uint16_t *port) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_PORT)) + return -NLE_NOATTR; + + if (port) + *port = info->port; + + return 0; +} + +int rtnl_link_macsec_set_cipher_suite(struct rtnl_link *link, uint64_t cipher_suite) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + info->cipher_suite = cipher_suite; + info->ce_mask |= MACSEC_ATTR_CIPHER_SUITE; + + return 0; +} + +int rtnl_link_macsec_get_cipher_suite(struct rtnl_link *link, uint64_t *cs) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_CIPHER_SUITE)) + return -NLE_NOATTR; + + if (cs) + *cs = info->cipher_suite; + + return 0; +} + +int rtnl_link_macsec_set_icv_len(struct rtnl_link *link, uint16_t icv_len) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (icv_len > MACSEC_STD_ICV_LEN) + return -NLE_INVAL; + + info->icv_len = icv_len; + info->ce_mask |= MACSEC_ATTR_ICV_LEN; + + return 0; +} + +int rtnl_link_macsec_get_icv_len(struct rtnl_link *link, uint16_t *icv_len) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_ICV_LEN)) + return -NLE_NOATTR; + + if (icv_len) + *icv_len = info->icv_len; + + return 0; +} + +int rtnl_link_macsec_set_protect(struct rtnl_link *link, uint8_t protect) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (protect > 1) + return -NLE_INVAL; + + info->protect = protect; + info->ce_mask |= MACSEC_ATTR_PROTECT; + + return 0; +} + +int rtnl_link_macsec_get_protect(struct rtnl_link *link, uint8_t *protect) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_PROTECT)) + return -NLE_NOATTR; + + if (protect) + *protect = info->protect; + + return 0; +} + +int rtnl_link_macsec_set_encrypt(struct rtnl_link *link, uint8_t encrypt) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (encrypt > 1) + return -NLE_INVAL; + + info->encrypt = encrypt; + info->ce_mask |= MACSEC_ATTR_ENCRYPT; + + return 0; +} + +int rtnl_link_macsec_get_encrypt(struct rtnl_link *link, uint8_t *encrypt) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_ENCRYPT)) + return -NLE_NOATTR; + + if (encrypt) + *encrypt = info->encrypt; + + return 0; +} + +int rtnl_link_macsec_set_offload(struct rtnl_link *link, uint8_t offload) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + info->offload = offload; + info->ce_mask |= MACSEC_ATTR_OFFLOAD; + + return 0; +} + +int rtnl_link_macsec_get_offload(struct rtnl_link *link, uint8_t *offload) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_OFFLOAD)) + return -NLE_NOATTR; + + if (offload) + *offload = info->offload; + + return 0; +} + +int rtnl_link_macsec_set_encoding_sa(struct rtnl_link *link, uint8_t encoding_sa) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (encoding_sa > 3) + return -NLE_INVAL; + + info->encoding_sa = encoding_sa; + info->ce_mask |= MACSEC_ATTR_ENCODING_SA; + + return 0; +} + +int rtnl_link_macsec_get_encoding_sa(struct rtnl_link *link, uint8_t *encoding_sa) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_ENCODING_SA)) + return -NLE_NOATTR; + + if (encoding_sa) + *encoding_sa = info->encoding_sa; + + return 0; +} + +int rtnl_link_macsec_set_validation_type(struct rtnl_link *link, enum macsec_validation_type validate) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (validate > MACSEC_VALIDATE_MAX) + return -NLE_INVAL; + + info->validate = validate; + info->ce_mask |= MACSEC_ATTR_VALIDATION; + + return 0; +} + +int rtnl_link_macsec_get_validation_type(struct rtnl_link *link, enum macsec_validation_type *validate) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_VALIDATION)) + return -NLE_NOATTR; + + if (validate) + *validate = info->validate; + + return 0; +} + +int rtnl_link_macsec_set_replay_protect(struct rtnl_link *link, uint8_t replay_protect) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (replay_protect > 1) + return -NLE_INVAL; + + info->replay_protect = replay_protect; + info->ce_mask |= MACSEC_ATTR_REPLAY_PROTECT; + + return 0; +} + +int rtnl_link_macsec_get_replay_protect(struct rtnl_link *link, uint8_t *replay_protect) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_REPLAY_PROTECT)) + return -NLE_NOATTR; + + if (replay_protect) + *replay_protect = info->replay_protect; + + return 0; +} + +int rtnl_link_macsec_set_window(struct rtnl_link *link, uint32_t window) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + info->window = window; + info->ce_mask |= MACSEC_ATTR_WINDOW; + + return 0; +} + +int rtnl_link_macsec_get_window(struct rtnl_link *link, uint32_t *window) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_WINDOW)) + return -NLE_NOATTR; + + if (window) + *window = info->window; + + return 0; +} + +int rtnl_link_macsec_set_send_sci(struct rtnl_link *link, uint8_t send_sci) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (send_sci > 1) + return -NLE_INVAL; + + info->send_sci = send_sci; + info->ce_mask |= MACSEC_ATTR_INC_SCI; + + return 0; +} + +int rtnl_link_macsec_get_send_sci(struct rtnl_link *link, uint8_t *send_sci) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_INC_SCI)) + return -NLE_NOATTR; + + if (send_sci) + *send_sci = info->send_sci; + + return 0; +} + +int rtnl_link_macsec_set_end_station(struct rtnl_link *link, uint8_t end_station) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (end_station > 1) + return -NLE_INVAL; + + info->end_station = end_station; + info->ce_mask |= MACSEC_ATTR_ES; + + return 0; +} + +int rtnl_link_macsec_get_end_station(struct rtnl_link *link, uint8_t *es) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_ES)) + return -NLE_NOATTR; + + if (es) + *es = info->end_station; + + return 0; +} + +int rtnl_link_macsec_set_scb(struct rtnl_link *link, uint8_t scb) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (scb > 1) + return -NLE_INVAL; + + info->scb = scb; + info->ce_mask |= MACSEC_ATTR_SCB; + + return 0; +} + +int rtnl_link_macsec_get_scb(struct rtnl_link *link, uint8_t *scb) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_SCB)) + return -NLE_NOATTR; + + if (scb) + *scb = info->scb; + + return 0; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/route/link/macvlan.c b/libnl/lib/route/link/macvlan.c new file mode 100644 index 0000000..fa7d0bb --- /dev/null +++ b/libnl/lib/route/link/macvlan.c @@ -0,0 +1,878 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Michael Braun + */ + +/** + * @ingroup link + * @defgroup macvlan MACVLAN/MACVTAP + * MAC-based Virtual LAN link module + * + * @details + * \b Link Type Name: "macvlan" + * + * @route_doc{link_macvlan, MACVLAN Documentation} + * @route_doc{link_macvtap, MACVTAP Documentation} + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" + +/** @cond SKIP */ +#define MACVLAN_HAS_MODE (1<<0) +#define MACVLAN_HAS_FLAGS (1<<1) +#define MACVLAN_HAS_MACADDR (1<<2) + +struct macvlan_info +{ + uint32_t mvi_mode; + uint16_t mvi_flags; // there currently is only one flag and kernel has no flags_mask yet + uint32_t mvi_mask; + uint32_t mvi_maccount; + uint32_t mvi_macmode; + struct nl_addr **mvi_macaddr; +}; + +/** @endcond */ + +static struct nla_policy macvlan_policy[IFLA_MACVLAN_MAX+1] = { + [IFLA_MACVLAN_MODE] = { .type = NLA_U32 }, + [IFLA_MACVLAN_FLAGS] = { .type = NLA_U16 }, + [IFLA_MACVLAN_MACADDR_MODE] = { .type = NLA_U32 }, + [IFLA_MACVLAN_MACADDR] = { .type = NLA_UNSPEC }, + [IFLA_MACVLAN_MACADDR_DATA] = { .type = NLA_NESTED }, + [IFLA_MACVLAN_MACADDR_COUNT] = { .type = NLA_U32 }, +}; + +static int macvlan_alloc(struct rtnl_link *link) +{ + struct macvlan_info *mvi; + uint32_t i; + + if (link->l_info) { + mvi = link->l_info; + for (i = 0; i < mvi->mvi_maccount; i++) + nl_addr_put(mvi->mvi_macaddr[i]); + free(mvi->mvi_macaddr); + memset(mvi, 0, sizeof(*mvi)); + } else { + if ((mvi = calloc(1, sizeof(*mvi))) == NULL) + return -NLE_NOMEM; + + link->l_info = mvi; + } + mvi->mvi_macmode = MACVLAN_MACADDR_SET; + + return 0; +} + +static int macvlan_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_MACVLAN_MAX+1]; + struct macvlan_info *mvi; + struct nlattr *nla; + int len; + int err; + + NL_DBG(3, "Parsing %s link info", link->l_info_ops->io_name); + + if ((err = nla_parse_nested(tb, IFLA_MACVLAN_MAX, data, macvlan_policy)) < 0) + goto errout; + + if ((err = macvlan_alloc(link)) < 0) + goto errout; + + mvi = link->l_info; + + if (tb[IFLA_MACVLAN_MODE]) { + mvi->mvi_mode = nla_get_u32(tb[IFLA_MACVLAN_MODE]); + mvi->mvi_mask |= MACVLAN_HAS_MODE; + } + + if (tb[IFLA_MACVLAN_FLAGS]) { + mvi->mvi_flags = nla_get_u16(tb[IFLA_MACVLAN_FLAGS]); + mvi->mvi_mask |= MACVLAN_HAS_FLAGS; + } + + if ( tb[IFLA_MACVLAN_MACADDR_COUNT] + && tb[IFLA_MACVLAN_MACADDR_DATA]) { + mvi->mvi_maccount = nla_get_u32(tb[IFLA_MACVLAN_MACADDR_COUNT]); + if (mvi->mvi_maccount > 0) { + uint32_t i; + + nla = nla_data(tb[IFLA_MACVLAN_MACADDR_DATA]); + len = nla_len(tb[IFLA_MACVLAN_MACADDR_DATA]); + + mvi->mvi_macaddr = calloc(mvi->mvi_maccount, + sizeof(*(mvi->mvi_macaddr))); + if (mvi->mvi_macaddr == NULL) { + err = -NLE_NOMEM; + goto errout; + } + + i = 0; + for (; nla_ok(nla, len); nla = nla_next(nla, &len)) { + if (i >= mvi->mvi_maccount) + break; + if (nla_type(nla) != IFLA_MACVLAN_MACADDR || + nla_len(nla) < ETH_ALEN) + continue; + mvi->mvi_macaddr[i] = nl_addr_alloc_attr(nla, AF_LLC); + i++; + } + } + mvi->mvi_mask |= MACVLAN_HAS_MACADDR; + } + + err = 0; +errout: + return err; +} + +static void macvlan_free(struct rtnl_link *link) +{ + struct macvlan_info *mvi; + uint32_t i; + + mvi = link->l_info; + if (!mvi) + return; + + for (i = 0; i < mvi->mvi_maccount; i++) + nl_addr_put(mvi->mvi_macaddr[i]); + free(mvi->mvi_macaddr); + free(mvi); + + link->l_info = NULL; +} + +static void macvlan_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + char buf[64]; + uint32_t i; + struct macvlan_info *mvi = link->l_info; + + if (mvi->mvi_mask & MACVLAN_HAS_MODE) { + rtnl_link_macvlan_mode2str(mvi->mvi_mode, buf, sizeof(buf)); + nl_dump(p, " %s-mode %s", link->l_info_ops->io_name, buf); + } + + if (mvi->mvi_mask & MACVLAN_HAS_FLAGS) { + rtnl_link_macvlan_flags2str(mvi->mvi_flags, buf, sizeof(buf)); + nl_dump(p, " %s-flags %s", link->l_info_ops->io_name, buf); + } + + if (mvi->mvi_mask & MACVLAN_HAS_MACADDR) { + nl_dump(p, " macvlan-count %u", (unsigned) mvi->mvi_maccount); + + if (mvi->mvi_maccount) + nl_dump(p, " macvlan-sourcemac"); + + for (i = 0; i < mvi->mvi_maccount; i++) { + nl_dump(p, " %s", nl_addr2str(mvi->mvi_macaddr[i], buf, + sizeof(buf))); + } + } + nl_dump(p, "\n"); +} + +static int macvlan_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct macvlan_info *vdst, *vsrc = src->l_info; + int err; + uint32_t i; + + dst->l_info = NULL; + if ((err = rtnl_link_set_type(dst, "macvlan")) < 0) + return err; + vdst = dst->l_info; + + if (!vdst || !vsrc) + return -NLE_NOMEM; + + memcpy(vdst, vsrc, sizeof(struct macvlan_info)); + + if ( vsrc->mvi_mask & MACVLAN_HAS_MACADDR + && vsrc->mvi_maccount > 0) { + vdst->mvi_macaddr = calloc(vdst->mvi_maccount, + sizeof(*(vdst->mvi_macaddr))); + for (i = 0; i < vdst->mvi_maccount; i++) + vdst->mvi_macaddr[i] = nl_addr_clone(vsrc->mvi_macaddr[i]); + } else + vdst->mvi_macaddr = NULL; + + return 0; +} + +static int macvlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct macvlan_info *mvi = link->l_info; + struct nlattr *data, *datamac = NULL; + int ret; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + return -NLE_MSGSIZE; + + ret = -NLE_NOMEM; + + if (mvi->mvi_mask & MACVLAN_HAS_MODE) + NLA_PUT_U32(msg, IFLA_MACVLAN_MODE, mvi->mvi_mode); + + if (mvi->mvi_mask & MACVLAN_HAS_FLAGS) + NLA_PUT_U16(msg, IFLA_MACVLAN_FLAGS, mvi->mvi_flags); + + if (mvi->mvi_mask & MACVLAN_HAS_MACADDR) { + uint32_t i; + + NLA_PUT_U32(msg, IFLA_MACVLAN_MACADDR_MODE, mvi->mvi_macmode); + datamac = nla_nest_start(msg, IFLA_MACVLAN_MACADDR_DATA); + if (!datamac) + goto nla_put_failure; + + for (i = 0; i < mvi->mvi_maccount; i++) { + NLA_PUT_ADDR(msg, IFLA_MACVLAN_MACADDR, + mvi->mvi_macaddr[i]); + } + } + + ret = 0; + +nla_put_failure: + if (datamac) + nla_nest_end(msg, datamac); + + nla_nest_end(msg, data); + + return ret; +} + +static struct rtnl_link_info_ops macvlan_info_ops = { + .io_name = "macvlan", + .io_alloc = macvlan_alloc, + .io_parse = macvlan_parse, + .io_dump = { + [NL_DUMP_DETAILS] = macvlan_dump_details, + }, + .io_clone = macvlan_clone, + .io_put_attrs = macvlan_put_attrs, + .io_free = macvlan_free, +}; + +static struct rtnl_link_info_ops macvtap_info_ops = { + .io_name = "macvtap", + .io_alloc = macvlan_alloc, + .io_parse = macvlan_parse, + .io_dump = { + [NL_DUMP_DETAILS] = macvlan_dump_details, + }, + .io_clone = macvlan_clone, + .io_put_attrs = macvlan_put_attrs, + .io_free = macvlan_free, +}; + +/** @cond SKIP */ +#define IS_MACVLAN_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &macvlan_info_ops) { \ + APPBUG("Link is not a macvlan link. set type \"macvlan\" first."); \ + return -NLE_OPNOTSUPP; \ + } + +#define IS_MACVTAP_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &macvtap_info_ops) { \ + APPBUG("Link is not a macvtap link. set type \"macvtap\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +/** + * @name MACVLAN Object + * @{ + */ + +/** + * Allocate link object of type MACVLAN + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_macvlan_alloc(void) +{ + struct rtnl_link *link; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if (rtnl_link_set_type(link, "macvlan") < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a MACVLAN link + * @arg link Link object + * + * @return True if link is a MACVLAN link, otherwise false is returned. + */ +int rtnl_link_is_macvlan(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "macvlan"); +} + +/** + * Set MACVLAN MODE + * @arg link Link object + * @arg mode MACVLAN mode + * + * @return 0 on success or a negative error code + */ +int rtnl_link_macvlan_set_mode(struct rtnl_link *link, uint32_t mode) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVLAN_LINK_ASSERT(link); + + mvi->mvi_mode = mode; + mvi->mvi_mask |= MACVLAN_HAS_MODE; + + if (mode != MACVLAN_MODE_SOURCE) { + uint32_t i; + + for (i = 0; i < mvi->mvi_maccount; i++) + nl_addr_put(mvi->mvi_macaddr[i]); + free(mvi->mvi_macaddr); + mvi->mvi_maccount = 0; + mvi->mvi_macaddr = NULL; + mvi->mvi_macmode = MACVLAN_MACADDR_SET; + mvi->mvi_mask &= ~MACVLAN_HAS_MACADDR; + } + + return 0; +} + +/** + * Get MACVLAN Mode + * @arg link Link object + * + * @return MACVLAN mode, 0 if not set or a negative error code. + */ +uint32_t rtnl_link_macvlan_get_mode(struct rtnl_link *link) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVLAN_LINK_ASSERT(link); + + if (mvi->mvi_mask & MACVLAN_HAS_MODE) + return mvi->mvi_mode; + else + return 0; +} + +/** + * Set MACVLAN MACMODE + * @arg link Link object + * @arg mode MACVLAN mac list modification mode + * + * Only for macvlan SOURCE mode. + * + * @return 0 on success or a negative error code + */ +int rtnl_link_macvlan_set_macmode(struct rtnl_link *link, uint32_t macmode) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVLAN_LINK_ASSERT(link); + + if (!(mvi->mvi_mask & MACVLAN_HAS_MODE) || + (mvi->mvi_mode != MACVLAN_MODE_SOURCE)) + return -NLE_INVAL; + + mvi->mvi_macmode = macmode; + mvi->mvi_mask |= MACVLAN_HAS_MACADDR; + + return 0; +} + +/** + * Get MACVLAN MACMODE + * @arg link Link object + * @arg out_macmode mac list modification mode + * + * Only for SOURCE mode. + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macvlan_get_macmode(struct rtnl_link *link, uint32_t *out_macmode) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVLAN_LINK_ASSERT(link); + + if (!(mvi->mvi_mask & MACVLAN_HAS_MODE) || + (mvi->mvi_mode != MACVLAN_MODE_SOURCE)) + return -NLE_INVAL; + + if (!(mvi->mvi_mask & MACVLAN_HAS_MACADDR)) + return -NLE_INVAL; + + *out_macmode = mvi->mvi_macmode; + + return 0; +} + +/** + * Set MACVLAN flags + * @arg link Link object + * @arg flags MACVLAN flags + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macvlan_set_flags(struct rtnl_link *link, uint16_t flags) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVLAN_LINK_ASSERT(link); + + mvi->mvi_flags |= flags; + mvi->mvi_mask |= MACVLAN_HAS_FLAGS; + + return 0; +} + +/** + * Unset MACVLAN flags + * @arg link Link object + * @arg flags MACVLAN flags + * + * Note: kernel currently only has a single flag and lacks flags_mask to + * indicate which flags shall be changed (it always all). + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macvlan_unset_flags(struct rtnl_link *link, uint16_t flags) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVLAN_LINK_ASSERT(link); + + mvi->mvi_flags &= ~flags; + mvi->mvi_mask |= MACVLAN_HAS_FLAGS; + + return 0; +} + +/** + * Get MACVLAN flags + * @arg link Link object + * + * @return MACVLAN flags, 0 if none set, or a negative error code. + */ +uint16_t rtnl_link_macvlan_get_flags(struct rtnl_link *link) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVLAN_LINK_ASSERT(link); + + return mvi->mvi_flags; +} + +/** + * Get number of MAC-Addr for MACVLAN device in source mode + * @arg link Link object + * @arg out_count number of mac addresses + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macvlan_count_macaddr(struct rtnl_link *link, uint32_t *out_count) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVLAN_LINK_ASSERT(link); + + if (!(mvi->mvi_mask & MACVLAN_HAS_MODE) || + (mvi->mvi_mode != MACVLAN_MODE_SOURCE)) + return -NLE_INVAL; + + if (!(mvi->mvi_mask & MACVLAN_HAS_MACADDR)) + return -NLE_INVAL; + + *out_count = mvi->mvi_maccount; + + return 0; +} + +/** + * Get configured remote MAC-Addr from MACVLAN device in source mode + * @arg link Link object + * @arg out_addr address object + * + * The returned nl_addr struct needs NOT to be released using nl_addr_put. + * It is only valid until the address is not removed from this link object + * or its mode is changed to non-source. + * + * @return 0 on success or negative error code + */ +int rtnl_link_macvlan_get_macaddr(struct rtnl_link *link, uint32_t idx, + const struct nl_addr **out_addr) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVLAN_LINK_ASSERT(link); + + if (!(mvi->mvi_mask & MACVLAN_HAS_MODE) || + (mvi->mvi_mode != MACVLAN_MODE_SOURCE)) + return -NLE_INVAL; + + if (!(mvi->mvi_mask & MACVLAN_HAS_MACADDR)) + return -NLE_INVAL; + + if (idx >= mvi->mvi_maccount) + return -NLE_INVAL; + + *out_addr = mvi->mvi_macaddr[idx]; + return 0; +} + +/** + * Add MAC-Addr to MACVLAN device in source mode + * @arg link Link object + * @arg addr MAC-Addr + * + * addr is not release but cloned by this method. + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macvlan_add_macaddr(struct rtnl_link *link, struct nl_addr *addr) +{ + struct macvlan_info *mvi = link->l_info; + struct nl_addr **mvi_macaddr; + size_t newsize; + + IS_MACVLAN_LINK_ASSERT(link); + + if (nl_addr_get_family(addr) != AF_LLC) + return -NLE_INVAL; + + if (!(mvi->mvi_mask & MACVLAN_HAS_MODE) || + (mvi->mvi_mode != MACVLAN_MODE_SOURCE)) + return -NLE_INVAL; + + if (!(mvi->mvi_mask & MACVLAN_HAS_MACADDR)) + return -NLE_INVAL; + + if (mvi->mvi_maccount >= UINT32_MAX) + return -NLE_INVAL; + + newsize = (mvi->mvi_maccount + 1) * sizeof(*(mvi->mvi_macaddr)); + mvi_macaddr = realloc(mvi->mvi_macaddr, newsize); + if (!mvi_macaddr) + return -NLE_NOMEM; + + mvi->mvi_macaddr = mvi_macaddr; + mvi->mvi_macaddr[mvi->mvi_maccount] = nl_addr_clone(addr); + mvi->mvi_maccount++; + + mvi->mvi_mask |= MACVLAN_HAS_MACADDR; + + return 0; +} + +/** + * Remove MAC-Addr from MACVLAN device in source mode + * @arg link Link object + * @arg addr MAC-Addr + * + * addr is not release by this method. + * + * @return a negative error code on failure, or the number + * of deleted addresses on success. + */ +int rtnl_link_macvlan_del_macaddr(struct rtnl_link *link, struct nl_addr *addr) +{ + struct macvlan_info *mvi = link->l_info; + uint32_t found, i; + + IS_MACVLAN_LINK_ASSERT(link); + + if (nl_addr_get_family(addr) != AF_LLC) + return -NLE_INVAL; + + if (!(mvi->mvi_mask & MACVLAN_HAS_MODE) || + (mvi->mvi_mode != MACVLAN_MODE_SOURCE)) + return -NLE_INVAL; + + if (!(mvi->mvi_mask & MACVLAN_HAS_MACADDR)) + return -NLE_INVAL; + + nl_addr_get(addr); + + found = 0; i = 0; + while (i + found < mvi->mvi_maccount) { + mvi->mvi_macaddr[i] = mvi->mvi_macaddr[i + found]; + if (found > 0) + mvi->mvi_macaddr[i + found] = NULL; + if (nl_addr_cmp(addr, mvi->mvi_macaddr[i]) == 0) { + nl_addr_put(mvi->mvi_macaddr[i]); + mvi->mvi_macaddr[i] = NULL; + found++; + } else + i++; + } + + nl_addr_put(addr); + + mvi->mvi_maccount -= found; + + return found > INT_MAX ? INT_MAX : (int) found; +} + +/** @} */ + + +/** + * @name MACVTAP Object + * @{ + */ + +/** + * Allocate link object of type MACVTAP + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_macvtap_alloc(void) +{ + struct rtnl_link *link; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if (rtnl_link_set_type(link, "macvtap") < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a MACVTAP link + * @arg link Link object + * + * @return True if link is a MACVTAP link, otherwise false is returned. + */ +int rtnl_link_is_macvtap(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "macvtap"); +} + +/** + * Set MACVTAP MODE + * @arg link Link object + * @arg mode MACVTAP mode + * + * @return 0 on success or a negative error code + */ +int rtnl_link_macvtap_set_mode(struct rtnl_link *link, uint32_t mode) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVTAP_LINK_ASSERT(link); + + mvi->mvi_mode = mode; + mvi->mvi_mask |= MACVLAN_HAS_MODE; + + return 0; +} + +/** + * Get MACVTAP Mode + * @arg link Link object + * + * @return MACVTAP mode, 0 if not set or a negative error code. + */ +uint32_t rtnl_link_macvtap_get_mode(struct rtnl_link *link) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVTAP_LINK_ASSERT(link); + + if (mvi->mvi_mask & MACVLAN_HAS_MODE) + return mvi->mvi_mode; + else + return 0; +} + +/** + * Set MACVTAP flags + * @arg link Link object + * @arg flags MACVTAP flags + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macvtap_set_flags(struct rtnl_link *link, uint16_t flags) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVTAP_LINK_ASSERT(link); + + mvi->mvi_flags |= flags; + mvi->mvi_mask |= MACVLAN_HAS_FLAGS; + + return 0; +} + +/** + * Unset MACVTAP flags + * @arg link Link object + * @arg flags MACVTAP flags + * + * Note: kernel currently only has a single flag and lacks flags_mask to + * indicate which flags shall be changed (it always all). + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macvtap_unset_flags(struct rtnl_link *link, uint16_t flags) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVTAP_LINK_ASSERT(link); + + mvi->mvi_flags &= ~flags; + mvi->mvi_mask |= MACVLAN_HAS_FLAGS; + + return 0; +} + +/** + * Get MACVTAP flags + * @arg link Link object + * + * @return MACVTAP flags, 0 if none set, or a negative error code. + */ +uint16_t rtnl_link_macvtap_get_flags(struct rtnl_link *link) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVTAP_LINK_ASSERT(link); + + return mvi->mvi_flags; +} + +/** @} */ + + +static const struct trans_tbl macvlan_flags[] = { + __ADD(MACVLAN_FLAG_NOPROMISC, nopromisc), +}; + +static const struct trans_tbl macvlan_modes[] = { + __ADD(MACVLAN_MODE_PRIVATE, private), + __ADD(MACVLAN_MODE_VEPA, vepa), + __ADD(MACVLAN_MODE_BRIDGE, bridge), + __ADD(MACVLAN_MODE_PASSTHRU, passthru), + __ADD(MACVLAN_MODE_SOURCE, source), +}; + +static const struct trans_tbl macvlan_macmodes[] = { + __ADD(MACVLAN_MACADDR_ADD, "add"), + __ADD(MACVLAN_MACADDR_DEL, "del"), + __ADD(MACVLAN_MACADDR_SET, "set"), + __ADD(MACVLAN_MACADDR_FLUSH, "flush"), +}; + +/** + * @name Flag Translation + * @{ + */ + +char *rtnl_link_macvlan_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, macvlan_flags, ARRAY_SIZE(macvlan_flags)); +} + +int rtnl_link_macvlan_str2flags(const char *name) +{ + return __str2flags(name, macvlan_flags, ARRAY_SIZE(macvlan_flags)); +} + +char *rtnl_link_macvtap_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, macvlan_flags, ARRAY_SIZE(macvlan_flags)); +} + +int rtnl_link_macvtap_str2flags(const char *name) +{ + return __str2flags(name, macvlan_flags, ARRAY_SIZE(macvlan_flags)); +} + +/** @} */ + +/** + * @name Mode Translation + * @{ + */ + +char *rtnl_link_macvlan_mode2str(int mode, char *buf, size_t len) +{ + return __type2str(mode, buf, len, macvlan_modes, ARRAY_SIZE(macvlan_modes)); +} + +int rtnl_link_macvlan_str2mode(const char *name) +{ + return __str2type(name, macvlan_modes, ARRAY_SIZE(macvlan_modes)); +} + +char *rtnl_link_macvlan_macmode2str(int mode, char *buf, size_t len) +{ + return __type2str(mode, buf, len, macvlan_macmodes, + ARRAY_SIZE(macvlan_macmodes)); +} + +int rtnl_link_macvlan_str2macmode(const char *name) +{ + return __str2type(name, macvlan_macmodes, ARRAY_SIZE(macvlan_macmodes)); +} + +char *rtnl_link_macvtap_mode2str(int mode, char *buf, size_t len) +{ + return __type2str(mode, buf, len, macvlan_modes, ARRAY_SIZE(macvlan_modes)); +} + +int rtnl_link_macvtap_str2mode(const char *name) +{ + return __str2type(name, macvlan_modes, ARRAY_SIZE(macvlan_modes)); +} + +/** @} */ + +static void _nl_init macvlan_init(void) +{ + rtnl_link_register_info(&macvlan_info_ops); + rtnl_link_register_info(&macvtap_info_ops); +} + +static void _nl_exit macvlan_exit(void) +{ + rtnl_link_unregister_info(&macvlan_info_ops); + rtnl_link_unregister_info(&macvtap_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/ppp.c b/libnl/lib/route/link/ppp.c new file mode 100644 index 0000000..34ca5f6 --- /dev/null +++ b/libnl/lib/route/link/ppp.c @@ -0,0 +1,219 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Jonas Johansson + */ + +/** + * @ingroup link + * @defgroup ppp PPP + * + * @details + * \b Link Type Name: "ppp" + * + * @route_doc{link_ppp, PPP Documentation} + * @{ + */ + +#include "nl-default.h" + +#include +#include + +#include "nl-route.h" +#include "link-api.h" + +/** @cond SKIP */ +#define PPP_ATTR_FD (1<<0) + +struct ppp_info +{ + int32_t pi_fd; + uint32_t ce_mask; +}; + +/** @endcond */ + +static struct nla_policy ppp_nl_policy[IFLA_PPP_MAX+1] = { + [IFLA_PPP_DEV_FD] = { .type = NLA_S32 }, +}; + +static int ppp_alloc(struct rtnl_link *link) +{ + struct ppp_info *info; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*info)); + else { + if ((info = calloc(1, sizeof(*info))) == NULL) + return -NLE_NOMEM; + + link->l_info = info; + } + + return 0; +} + +static int ppp_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_PPP_MAX+1]; + struct ppp_info *info; + int err; + + NL_DBG(3, "Parsing PPP link info\n"); + + if ((err = nla_parse_nested(tb, IFLA_PPP_MAX, data, ppp_nl_policy)) < 0) + goto errout; + + if ((err = ppp_alloc(link)) < 0) + goto errout; + + info = link->l_info; + + if (tb[IFLA_PPP_DEV_FD]) { + info->pi_fd = nla_get_s32(tb[IFLA_PPP_DEV_FD]); + info->ce_mask |= PPP_ATTR_FD; + } + + err = 0; +errout: + return err; +} + +static void ppp_free(struct rtnl_link *link) +{ + free(link->l_info); + link->l_info = NULL; +} + +static int ppp_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct ppp_info *vdst, *vsrc = src->l_info; + int err; + + dst->l_info = NULL; + if ((err = rtnl_link_set_type(dst, "ppp")) < 0) + return err; + vdst = dst->l_info; + + if (!vdst || !vsrc) + return -NLE_NOMEM; + + memcpy(vdst, vsrc, sizeof(struct ppp_info)); + + return 0; +} + +static int ppp_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct ppp_info *info = link->l_info; + struct nlattr *data; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + return -NLE_MSGSIZE; + + if (info->ce_mask & PPP_ATTR_FD) + NLA_PUT_S32(msg, IFLA_PPP_DEV_FD, info->pi_fd); + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static struct rtnl_link_info_ops ppp_info_ops = { + .io_name = "ppp", + .io_alloc = ppp_alloc, + .io_parse = ppp_parse, + .io_clone = ppp_clone, + .io_put_attrs = ppp_put_attrs, + .io_free = ppp_free, +}; + +/** @cond SKIP */ +#define IS_PPP_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &ppp_info_ops) { \ + APPBUG("Link is not a PPP link. set type \"ppp\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +/** + * @name PPP Object + * @{ + */ + +/** + * Allocate link object of type PPP + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_ppp_alloc(void) +{ + struct rtnl_link *link; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if (rtnl_link_set_type(link, "ppp") < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Set PPP file descriptor + * @arg link Link object + * @arg flags PPP file descriptor + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_ppp_set_fd(struct rtnl_link *link, int32_t fd) +{ + struct ppp_info *info = link->l_info; + + IS_PPP_LINK_ASSERT(link); + + info->pi_fd |= fd; + info->ce_mask |= PPP_ATTR_FD; + + return 0; +} + +/** + * Get PPP file descriptor + * @arg link Link object + * + * @return PPP file descriptor, 0 if not set or a negative error code. + */ +int rtnl_link_ppp_get_fd(struct rtnl_link *link, int32_t *fd) +{ + struct ppp_info *info = link->l_info; + + IS_PPP_LINK_ASSERT(link); + + if (!(info->ce_mask & PPP_ATTR_FD)) + return -NLE_NOATTR; + + if (fd) + *fd = info->pi_fd; + + return 0; +} + +/** @} */ + +static void _nl_init ppp_init(void) +{ + rtnl_link_register_info(&ppp_info_ops); +} + +static void _nl_exit ppp_exit(void) +{ + rtnl_link_unregister_info(&ppp_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/sit.c b/libnl/lib/route/link/sit.c new file mode 100644 index 0000000..69dd316 --- /dev/null +++ b/libnl/lib/route/link/sit.c @@ -0,0 +1,873 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +/** + * @ingroup link + * @defgroup sit SIT + * sit link module + * + * @details + * \b Link Type Name: "sit" + * + * @route_doc{link_sit, SIT Documentation} + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" + +#define SIT_ATTR_LINK (1 << 0) +#define SIT_ATTR_LOCAL (1 << 1) +#define SIT_ATTR_REMOTE (1 << 2) +#define SIT_ATTR_TTL (1 << 3) +#define SIT_ATTR_TOS (1 << 4) +#define SIT_ATTR_PMTUDISC (1 << 5) +#define SIT_ATTR_FLAGS (1 << 6) +#define SIT_ATTR_PROTO (1 << 7) +#define SIT_ATTR_6RD_PREFIX (1 << 8) +#define SIT_ATTR_6RD_RELAY_PREFIX (1 << 9) +#define SIT_ATTR_6RD_PREFIXLEN (1 << 10) +#define SIT_ATTR_6RD_RELAY_PREFIXLEN (1 << 11) +#define SIT_ATTR_FWMARK (1 << 12) + +struct sit_info +{ + uint8_t ttl; + uint8_t tos; + uint8_t pmtudisc; + uint8_t proto; + uint16_t flags; + uint32_t link; + uint32_t local; + uint32_t remote; + struct in6_addr ip6rd_prefix; + uint32_t ip6rd_relay_prefix; + uint16_t ip6rd_prefixlen; + uint16_t ip6rd_relay_prefixlen; + uint32_t fwmark; + uint32_t sit_mask; +}; + +static struct nla_policy sit_policy[IFLA_IPTUN_MAX + 1] = { + [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, + [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, + [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 }, + [IFLA_IPTUN_TTL] = { .type = NLA_U8 }, + [IFLA_IPTUN_TOS] = { .type = NLA_U8 }, + [IFLA_IPTUN_PMTUDISC] = { .type = NLA_U8 }, + [IFLA_IPTUN_FLAGS] = { .type = NLA_U16 }, + [IFLA_IPTUN_PROTO] = { .type = NLA_U8 }, + [IFLA_IPTUN_6RD_PREFIX] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_IPTUN_6RD_RELAY_PREFIX] = { .type = NLA_U32 }, + [IFLA_IPTUN_6RD_PREFIXLEN] = { .type = NLA_U16 }, + [IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = { .type = NLA_U16 }, + [IFLA_IPTUN_FWMARK] = { .type = NLA_U32 }, +}; + +static int sit_alloc(struct rtnl_link *link) +{ + struct sit_info *sit; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*sit)); + else { + sit = calloc(1, sizeof(*sit)); + if (!sit) + return -NLE_NOMEM; + + link->l_info = sit; + } + + return 0; +} + +static int sit_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_IPTUN_MAX + 1]; + struct sit_info *sit; + int err; + + NL_DBG(3, "Parsing SIT link info\n"); + + err = nla_parse_nested(tb, IFLA_IPTUN_MAX, data, sit_policy); + if (err < 0) + goto errout; + + err = sit_alloc(link); + if (err < 0) + goto errout; + + sit = link->l_info; + + if (tb[IFLA_IPTUN_LINK]) { + sit->link = nla_get_u32(tb[IFLA_IPTUN_LINK]); + sit->sit_mask |= SIT_ATTR_LINK; + } + + if (tb[IFLA_IPTUN_LOCAL]) { + sit->local = nla_get_u32(tb[IFLA_IPTUN_LOCAL]); + sit->sit_mask |= SIT_ATTR_LOCAL; + } + + if (tb[IFLA_IPTUN_REMOTE]) { + sit->remote = nla_get_u32(tb[IFLA_IPTUN_REMOTE]); + sit->sit_mask |= SIT_ATTR_REMOTE; + } + + if (tb[IFLA_IPTUN_TTL]) { + sit->ttl = nla_get_u8(tb[IFLA_IPTUN_TTL]); + sit->sit_mask |= SIT_ATTR_TTL; + } + + if (tb[IFLA_IPTUN_TOS]) { + sit->tos = nla_get_u8(tb[IFLA_IPTUN_TOS]); + sit->sit_mask |= SIT_ATTR_TOS; + } + + if (tb[IFLA_IPTUN_PMTUDISC]) { + sit->pmtudisc = nla_get_u8(tb[IFLA_IPTUN_PMTUDISC]); + sit->sit_mask |= SIT_ATTR_PMTUDISC; + } + + if (tb[IFLA_IPTUN_FLAGS]) { + sit->flags = nla_get_u16(tb[IFLA_IPTUN_FLAGS]); + sit->sit_mask |= SIT_ATTR_FLAGS; + } + + if (tb[IFLA_IPTUN_PROTO]) { + sit->proto = nla_get_u8(tb[IFLA_IPTUN_PROTO]); + sit->sit_mask |= SIT_ATTR_PROTO; + } + + if (tb[IFLA_IPTUN_6RD_PREFIX]) { + nla_memcpy(&sit->ip6rd_prefix, tb[IFLA_IPTUN_6RD_PREFIX], + sizeof(struct in6_addr)); + sit->sit_mask |= SIT_ATTR_6RD_PREFIX; + } + + if (tb[IFLA_IPTUN_6RD_RELAY_PREFIX]) { + sit->ip6rd_relay_prefix = nla_get_u32(tb[IFLA_IPTUN_6RD_RELAY_PREFIX]); + sit->sit_mask |= SIT_ATTR_6RD_RELAY_PREFIX; + } + + if (tb[IFLA_IPTUN_6RD_PREFIXLEN]) { + sit->ip6rd_prefixlen = nla_get_u16(tb[IFLA_IPTUN_6RD_PREFIXLEN]); + sit->sit_mask |= SIT_ATTR_6RD_PREFIXLEN; + } + + if (tb[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]) { + sit->ip6rd_relay_prefixlen = nla_get_u16(tb[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]); + sit->sit_mask |= SIT_ATTR_6RD_RELAY_PREFIXLEN; + } + + if (tb[IFLA_IPTUN_FWMARK]) { + sit->fwmark = nla_get_u32(tb[IFLA_IPTUN_FWMARK]); + sit->sit_mask |= SIT_ATTR_FWMARK; + } + + err = 0; + +errout: + return err; +} + +static int sit_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct sit_info *sit = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + + if (sit->sit_mask & SIT_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_IPTUN_LINK, sit->link); + + if (sit->sit_mask & SIT_ATTR_LOCAL) + NLA_PUT_U32(msg, IFLA_IPTUN_LOCAL, sit->local); + + if (sit->sit_mask & SIT_ATTR_REMOTE) + NLA_PUT_U32(msg, IFLA_IPTUN_REMOTE, sit->remote); + + if (sit->sit_mask & SIT_ATTR_TTL) + NLA_PUT_U8(msg, IFLA_IPTUN_TTL, sit->ttl); + + if (sit->sit_mask & SIT_ATTR_TOS) + NLA_PUT_U8(msg, IFLA_IPTUN_TOS, sit->tos); + + if (sit->sit_mask & SIT_ATTR_PMTUDISC) + NLA_PUT_U8(msg, IFLA_IPTUN_PMTUDISC, sit->pmtudisc); + + if (sit->sit_mask & SIT_ATTR_FLAGS) + NLA_PUT_U16(msg, IFLA_IPTUN_FLAGS, sit->flags); + + if (sit->sit_mask & SIT_ATTR_PROTO) + NLA_PUT_U8(msg, IFLA_IPTUN_PROTO, sit->proto); + + if (sit->sit_mask & SIT_ATTR_6RD_PREFIX) + NLA_PUT(msg, IFLA_IPTUN_6RD_PREFIX, sizeof(struct in6_addr), &sit->ip6rd_prefix); + + if (sit->sit_mask & SIT_ATTR_6RD_RELAY_PREFIX) + NLA_PUT_U32(msg, IFLA_IPTUN_6RD_RELAY_PREFIX, sit->ip6rd_relay_prefix); + + if (sit->sit_mask & SIT_ATTR_6RD_PREFIXLEN) + NLA_PUT_U16(msg, IFLA_IPTUN_6RD_PREFIXLEN, sit->ip6rd_prefixlen); + + if (sit->sit_mask & SIT_ATTR_6RD_RELAY_PREFIXLEN) + NLA_PUT_U16(msg, IFLA_IPTUN_6RD_RELAY_PREFIXLEN, sit->ip6rd_relay_prefixlen); + + if (sit->sit_mask & SIT_ATTR_FWMARK) + NLA_PUT_U32(msg, IFLA_IPTUN_FWMARK, sit->fwmark); + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static void sit_free(struct rtnl_link *link) +{ + struct sit_info *sit = link->l_info; + + free(sit); + link->l_info = NULL; +} + +static void sit_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + nl_dump(p, "sit : %s", link->l_name); +} + +static void sit_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct sit_info *sit = link->l_info; + char *name, addr[INET_ADDRSTRLEN], addr6[INET6_ADDRSTRLEN]; + struct rtnl_link *parent; + + if (sit->sit_mask & SIT_ATTR_LINK) { + nl_dump(p, " link "); + + name = NULL; + parent = link_lookup(link->ce_cache, sit->link); + if (parent) + name = rtnl_link_get_name(parent); + + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", sit->link); + } + + if (sit->sit_mask & SIT_ATTR_LOCAL) { + nl_dump(p, " local "); + if(inet_ntop(AF_INET, &sit->local, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ntohs(sit->local)); + } + + if (sit->sit_mask & SIT_ATTR_REMOTE) { + nl_dump(p, " remote "); + if(inet_ntop(AF_INET, &sit->remote, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ntohs(sit->remote)); + } + + if (sit->sit_mask & SIT_ATTR_TTL) { + nl_dump(p, " ttl "); + nl_dump_line(p, "%u\n", sit->ttl); + } + + if (sit->sit_mask & SIT_ATTR_TOS) { + nl_dump(p, " tos "); + nl_dump_line(p, "%u\n", sit->tos); + } + + if (sit->sit_mask & SIT_ATTR_FLAGS) { + nl_dump(p, " flags "); + nl_dump_line(p, " (%x)\n", sit->flags); + } + + if (sit->sit_mask & SIT_ATTR_PROTO) { + nl_dump(p, " proto "); + nl_dump_line(p, " (%x)\n", sit->proto); + } + + if (sit->sit_mask & SIT_ATTR_6RD_PREFIX) { + nl_dump(p, " 6rd_prefix "); + if(inet_ntop(AF_INET6, &sit->ip6rd_prefix, addr6, INET6_ADDRSTRLEN)) + nl_dump_line(p, "%s\n", addr6); + else + nl_dump_line(p, "[unknown]\n"); + } + + if (sit->sit_mask & SIT_ATTR_6RD_RELAY_PREFIX) { + nl_dump(p, " 6rd_relay_prefix "); + if(inet_ntop(AF_INET, &sit->ip6rd_relay_prefix, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "[unknown]\n"); + } + + if (sit->sit_mask & SIT_ATTR_6RD_PREFIXLEN) { + nl_dump(p, " 6rd_prefixlen "); + nl_dump_line(p, "%d\n", sit->ip6rd_prefixlen); + } + + if (sit->sit_mask & SIT_ATTR_6RD_RELAY_PREFIXLEN) { + nl_dump(p, " 6rd_relay_prefixlen "); + nl_dump_line(p, "%d\n", sit->ip6rd_relay_prefixlen); + } + + if (sit->sit_mask & SIT_ATTR_FWMARK) { + nl_dump(p, " fwmark "); + nl_dump_line(p, "%x\n", sit->fwmark); + } +} + +static int sit_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct sit_info *sit_dst, *sit_src = src->l_info; + int err; + + dst->l_info = NULL; + + err = rtnl_link_set_type(dst, "sit"); + if (err < 0) + return err; + + sit_dst = dst->l_info; + + if (!sit_dst || !sit_src) + return -NLE_NOMEM; + + memcpy(sit_dst, sit_src, sizeof(struct sit_info)); + + return 0; +} + +static struct rtnl_link_info_ops sit_info_ops = { + .io_name = "sit", + .io_alloc = sit_alloc, + .io_parse = sit_parse, + .io_dump = { + [NL_DUMP_LINE] = sit_dump_line, + [NL_DUMP_DETAILS] = sit_dump_details, + }, + .io_clone = sit_clone, + .io_put_attrs = sit_put_attrs, + .io_free = sit_free, +}; + +#define IS_SIT_LINK_ASSERT(link, sit) \ + struct sit_info *sit; \ + do { \ + const struct rtnl_link *_link = (link); \ + if (!_link || _link->l_info_ops != &sit_info_ops) { \ + APPBUG("Link is not a sit link. set type \"sit\" first."); \ + return -NLE_OPNOTSUPP; \ + } \ + (sit) = _link->l_info; \ + } while (0) + +struct rtnl_link *rtnl_link_sit_alloc(void) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_alloc(); + if (!link) + return NULL; + + err = rtnl_link_set_type(link, "sit"); + if (err < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a SIT link + * @arg link Link object + * + * @return True if link is a SIT link, otherwise false is returned. + */ +int rtnl_link_is_sit(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "sit"); +} + +/** + * Create a new sit tunnel device + * @arg sock netlink socket + * @arg name name of the tunnel device + * + * Creates a new sit tunnel device in the kernel + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_add(struct nl_sock *sk, const char *name) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_sit_alloc(); + if (!link) + return -NLE_NOMEM; + + if(name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + rtnl_link_put(link); + + return err; +} + +/** + * Set SIT tunnel interface index + * @arg link Link object + * @arg index interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_set_link(struct rtnl_link *link, uint32_t index) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->link = index; + sit->sit_mask |= SIT_ATTR_LINK; + + return 0; +} + +/** + * Get SIT tunnel interface index + * @arg link Link object + * + * @return interface index value + */ +uint32_t rtnl_link_sit_get_link(struct rtnl_link *link) +{ + IS_SIT_LINK_ASSERT(link, sit); + + return sit->link; +} + +/** + * Set SIT tunnel local address + * @arg link Link object + * @arg addr local address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_set_local(struct rtnl_link *link, uint32_t addr) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->local = addr; + sit->sit_mask |= SIT_ATTR_LOCAL; + + return 0; +} + +/** + * Get SIT tunnel local address + * @arg link Link object + * + * @return local address value + */ +uint32_t rtnl_link_sit_get_local(struct rtnl_link *link) +{ + IS_SIT_LINK_ASSERT(link, sit); + + return sit->local; +} + +/** + * Set SIT tunnel remote address + * @arg link Link object + * @arg remote remote address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_set_remote(struct rtnl_link *link, uint32_t addr) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->remote = addr; + sit->sit_mask |= SIT_ATTR_REMOTE; + + return 0; +} + +/** + * Get SIT tunnel remote address + * @arg link Link object + * + * @return remote address + */ +uint32_t rtnl_link_sit_get_remote(struct rtnl_link *link) +{ + IS_SIT_LINK_ASSERT(link, sit); + + return sit->remote; +} + +/** + * Set SIT tunnel ttl + * @arg link Link object + * @arg ttl tunnel ttl + * + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_set_ttl(struct rtnl_link *link, uint8_t ttl) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->ttl = ttl; + sit->sit_mask |= SIT_ATTR_TTL; + + return 0; +} + +/** + * Get SIT tunnel ttl + * @arg link Link object + * + * @return ttl value + */ +uint8_t rtnl_link_sit_get_ttl(struct rtnl_link *link) +{ + IS_SIT_LINK_ASSERT(link, sit); + + return sit->ttl; +} + +/** + * Set SIT tunnel tos + * @arg link Link object + * @arg tos tunnel tos + * + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_set_tos(struct rtnl_link *link, uint8_t tos) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->tos = tos; + sit->sit_mask |= SIT_ATTR_TOS; + + return 0; +} + +/** + * Get SIT tunnel tos + * @arg link Link object + * + * @return tos value + */ +uint8_t rtnl_link_sit_get_tos(struct rtnl_link *link) +{ + IS_SIT_LINK_ASSERT(link, sit); + + return sit->tos; +} + +/** + * Set SIT tunnel path MTU discovery + * @arg link Link object + * @arg pmtudisc path MTU discovery + * + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->pmtudisc = pmtudisc; + sit->sit_mask |= SIT_ATTR_PMTUDISC; + + return 0; +} + +/** + * Get SIT path MTU discovery + * @arg link Link object + * + * @return pmtudisc value + */ +uint8_t rtnl_link_sit_get_pmtudisc(struct rtnl_link *link) +{ + IS_SIT_LINK_ASSERT(link, sit); + + return sit->pmtudisc; +} + +/** + * Set SIT tunnel flags + * @arg link Link object + * @arg flags tunnel flags + * + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_set_flags(struct rtnl_link *link, uint16_t flags) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->flags = flags; + sit->sit_mask |= SIT_ATTR_FLAGS; + + return 0; +} + +/** + * Get SIT path flags + * @arg link Link object + * + * @return flags value + */ +uint16_t rtnl_link_sit_get_flags(struct rtnl_link *link) +{ + IS_SIT_LINK_ASSERT(link, sit); + + return sit->flags; +} + +/** + * Set SIT tunnel proto + * @arg link Link object + * @arg proto tunnel proto + * + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_set_proto(struct rtnl_link *link, uint8_t proto) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->proto = proto; + sit->sit_mask |= SIT_ATTR_PROTO; + + return 0; +} + +/** + * Get SIT proto + * @arg link Link object + * + * @return proto value + */ +uint8_t rtnl_link_sit_get_proto(struct rtnl_link *link) +{ + IS_SIT_LINK_ASSERT(link, sit); + + return sit->proto; +} + +/** + * Set ip6rd prefix + * @arg link Link object + * @arg prefix The IPv6 prefix + * + * @return 0 on success or an error code. + */ +int rtnl_link_sit_set_ip6rd_prefix(struct rtnl_link *link, const struct in6_addr *prefix) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->ip6rd_prefix = *prefix; + sit->sit_mask |= SIT_ATTR_6RD_PREFIX; + return 0; +} + +/** + * Get ip6rd prefix + * @arg link Link object + * @arg prefix The output IPv6 prefix + * + * @return 0 on success or an error code. If the property is unset, + * this call fails too. + */ +int rtnl_link_sit_get_ip6rd_prefix(const struct rtnl_link *link, struct in6_addr *prefix) +{ + IS_SIT_LINK_ASSERT(link, sit); + + if (!(sit->sit_mask & SIT_ATTR_6RD_PREFIX)) + return -NLE_NOATTR; + + if (prefix) + *prefix = sit->ip6rd_prefix; + return 0; +} + +/** + * Set ip6rd prefix length + * @arg link Link object + * @arg prefixlen The IPv6 prefix length + * + * @return 0 on success or an error code. + */ +int rtnl_link_sit_set_ip6rd_prefixlen(struct rtnl_link *link, uint16_t prefixlen) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->sit_mask |= SIT_ATTR_6RD_PREFIXLEN; + sit->ip6rd_prefixlen = prefixlen; + return 0; +} + +/** + * Get ip6rd prefix length + * @arg link Link object + * @arg prefixlen Output pointer for the prefix length + * + * @return 0 on success or an error code. If the property is unset, + * this call fails. + */ +int rtnl_link_sit_get_ip6rd_prefixlen(struct rtnl_link *link, uint16_t *prefixlen) +{ + IS_SIT_LINK_ASSERT(link, sit); + + if (!(sit->sit_mask & SIT_ATTR_6RD_PREFIXLEN)) + return -NLE_NOATTR; + + if (prefixlen) + *prefixlen = sit->ip6rd_prefixlen; + return 0; +} + +/** + * Set ip6rd relay prefix + * @arg link Link object + * @arg prefix The IPv6 prefix length + * + * @return 0 on success or an error code. + */ +int rtnl_link_sit_set_ip6rd_relay_prefix(struct rtnl_link *link, uint32_t prefix) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->sit_mask |= SIT_ATTR_6RD_RELAY_PREFIX; + sit->ip6rd_relay_prefix = prefix; + return 0; +} + +/** + * Get ip6rd prefix length + * @arg link Link object + * @arg prefixlen Output pointer for the prefix length + * + * @return 0 on success or an error code. If the property is unset, + * this call fails. + */ +int rtnl_link_sit_get_ip6rd_relay_prefix(const struct rtnl_link *link, uint32_t *prefix) +{ + IS_SIT_LINK_ASSERT(link, sit); + + if (!(sit->sit_mask & SIT_ATTR_6RD_RELAY_PREFIX)) + return -NLE_NOATTR; + + if (prefix) + *prefix = sit->ip6rd_relay_prefix; + return 0; +} + +/** + * Set ip6rd relay prefix length + * @arg link Link object + * @arg prefixlen The IPv6 prefix length + * + * @return 0 on success or an error code. + */ +int rtnl_link_sit_set_ip6rd_relay_prefixlen(struct rtnl_link *link, uint16_t prefixlen) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->sit_mask |= SIT_ATTR_6RD_RELAY_PREFIXLEN; + sit->ip6rd_relay_prefixlen = prefixlen; + return 0; +} + +/** + * Get ip6rd relay prefix length + * @arg link Link object + * @arg prefixlen Output pointer for the prefix length + * + * @return 0 on success or an error code. If the property is unset, + * this call fails. + */ +int rtnl_link_sit_get_ip6rd_relay_prefixlen(struct rtnl_link *link, uint16_t *prefixlen) +{ + IS_SIT_LINK_ASSERT(link, sit); + + if (!(sit->sit_mask & SIT_ATTR_6RD_RELAY_PREFIX)) + return -NLE_NOATTR; + + if (prefixlen) + *prefixlen = sit->ip6rd_relay_prefixlen; + return 0; +} + +/** + * Set SIT tunnel fwmark + * @arg link Link object + * @arg fwmark fwmark + * + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_set_fwmark(struct rtnl_link *link, uint32_t fwmark) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->fwmark = fwmark; + sit->sit_mask |= SIT_ATTR_FWMARK; + + return 0; +} + +/** + * Get SIT tunnel fwmark + * @arg link Link object + * @arg fwmark addr to fill in with the fwmark + * + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_get_fwmark(struct rtnl_link *link, uint32_t *fwmark) +{ + IS_SIT_LINK_ASSERT(link, sit); + + if (!(sit->sit_mask & SIT_ATTR_FWMARK)) + return -NLE_NOATTR; + + *fwmark = sit->fwmark; + + return 0; +} + +static void _nl_init sit_init(void) +{ + rtnl_link_register_info(&sit_info_ops); +} + +static void _nl_exit sit_exit(void) +{ + rtnl_link_unregister_info(&sit_info_ops); +} diff --git a/libnl/lib/route/link/sriov.c b/libnl/lib/route/link/sriov.c new file mode 100644 index 0000000..98087e2 --- /dev/null +++ b/libnl/lib/route/link/sriov.c @@ -0,0 +1,1479 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Intel Corp. All rights reserved. + * Copyright (c) 2016 Jef Oliver + */ + +/** + * @ingroup link + * @defgroup sriov SRIOV + * SR-IOV VF link module + * + * @details + * SR-IOV (Single Root Input/Output Virtualization) is a network interface + * that allows for the isolation of the PCI Express resources. In a virtual + * environment, SR-IOV allows multiple virtual machines can share a single + * PCI Express hardware interface. This is done via VFs (Virtual Functions), + * virtual hardware devices with their own PCI address. + * + * @{ + */ + +#include "nl-default.h" + +#include +#include + +#include +#include +#include + +#include "nl-route.h" +#include "link-sriov.h" +#include "link-api.h" + +/** @cond SKIP */ +struct rtnl_link_vf { + struct nl_list_head vf_list; + int ce_refcnt; + uint32_t ce_mask; + uint32_t vf_index; + uint64_t vf_guid_node; + uint64_t vf_guid_port; + uint32_t vf_linkstate; + struct nl_addr *vf_lladdr; + uint32_t vf_max_tx_rate; + uint32_t vf_min_tx_rate; + uint32_t vf_rate; + uint32_t vf_rss_query_en; + uint32_t vf_spoofchk; + uint64_t vf_stats[RTNL_LINK_VF_STATS_MAX + 1]; + uint32_t vf_trust; + struct nl_vf_vlans *vf_vlans; +}; + +#define SRIOVON "on" +#define SRIOVOFF "off" + +#define SET_VF_STAT(link, vf_num, stb, stat, attr) \ + vf_data->vf_stats[stat] = nla_get_u64(stb[attr]) + +/* SRIOV-VF Attributes */ +#define SRIOV_ATTR_INDEX (1 << 0) +#define SRIOV_ATTR_ADDR (1 << 1) +#define SRIOV_ATTR_VLAN (1 << 2) +#define SRIOV_ATTR_TX_RATE (1 << 3) +#define SRIOV_ATTR_SPOOFCHK (1 << 4) +#define SRIOV_ATTR_RATE_MAX (1 << 5) +#define SRIOV_ATTR_RATE_MIN (1 << 6) +#define SRIOV_ATTR_LINK_STATE (1 << 7) +#define SRIOV_ATTR_RSS_QUERY_EN (1 << 8) +#define SRIOV_ATTR_STATS (1 << 9) +#define SRIOV_ATTR_TRUST (1 << 10) +#define SRIOV_ATTR_IB_NODE_GUID (1 << 11) +#define SRIOV_ATTR_IB_PORT_GUID (1 << 12) + +static struct nla_policy sriov_info_policy[IFLA_VF_MAX+1] = { + [IFLA_VF_MAC] = { .minlen = sizeof(struct ifla_vf_mac) }, + [IFLA_VF_VLAN] = { .minlen = sizeof(struct ifla_vf_vlan) }, + [IFLA_VF_VLAN_LIST] = { .type = NLA_NESTED }, + [IFLA_VF_TX_RATE] = { .minlen = sizeof(struct ifla_vf_tx_rate) }, + [IFLA_VF_SPOOFCHK] = { .minlen = sizeof(struct ifla_vf_spoofchk) }, + [IFLA_VF_RATE] = { .minlen = sizeof(struct ifla_vf_rate) }, + [IFLA_VF_LINK_STATE] = { .minlen = sizeof(struct ifla_vf_link_state) }, + [IFLA_VF_RSS_QUERY_EN] = { .minlen = sizeof(struct ifla_vf_rss_query_en) }, + [IFLA_VF_STATS] = { .type = NLA_NESTED }, + [IFLA_VF_TRUST] = { .minlen = sizeof(struct ifla_vf_trust) }, + [IFLA_VF_IB_NODE_GUID] = { .minlen = sizeof(struct ifla_vf_guid) }, + [IFLA_VF_IB_PORT_GUID] = { .minlen = sizeof(struct ifla_vf_guid) }, +}; + +static struct nla_policy sriov_stats_policy[IFLA_VF_STATS_MAX+1] = { + [IFLA_VF_STATS_RX_PACKETS] = { .type = NLA_U64 }, + [IFLA_VF_STATS_TX_PACKETS] = { .type = NLA_U64 }, + [IFLA_VF_STATS_RX_BYTES] = { .type = NLA_U64 }, + [IFLA_VF_STATS_TX_BYTES] = { .type = NLA_U64 }, + [IFLA_VF_STATS_BROADCAST] = { .type = NLA_U64 }, + [IFLA_VF_STATS_MULTICAST] = { .type = NLA_U64 }, +}; + +/** @endcond */ + +/* Clone SRIOV VF list in link object */ +int rtnl_link_sriov_clone(struct rtnl_link *dst, struct rtnl_link *src) { + int err = 0; + struct nl_addr *vf_addr; + struct rtnl_link_vf *s_list, *d_vf, *s_vf, *next, *dest_h = NULL; + nl_vf_vlans_t *src_vlans = NULL, *dst_vlans = NULL; + nl_vf_vlan_info_t *src_vlan_info = NULL, *dst_vlan_info = NULL; + + if (!rtnl_link_has_vf_list(src)) + return 0; + + dst->l_vf_list = rtnl_link_vf_alloc(); + if (!dst->l_vf_list) + return -NLE_NOMEM; + dest_h = dst->l_vf_list; + s_list = src->l_vf_list; + + nl_list_for_each_entry_safe(s_vf, next, &s_list->vf_list, vf_list) { + if (!(d_vf = rtnl_link_vf_alloc())) + return -NLE_NOMEM; + + memcpy(d_vf, s_vf, sizeof(*s_vf)); + + if (s_vf->ce_mask & SRIOV_ATTR_ADDR) { + vf_addr = nl_addr_clone(s_vf->vf_lladdr); + if (!vf_addr) { + rtnl_link_vf_put(d_vf); + return -NLE_NOMEM; + } + d_vf->vf_lladdr = vf_addr; + } + + if (s_vf->ce_mask & SRIOV_ATTR_VLAN) { + src_vlans = s_vf->vf_vlans; + src_vlan_info = src_vlans->vlans; + + err = rtnl_link_vf_vlan_alloc(&dst_vlans, + src_vlans->size); + if (err < 0) { + rtnl_link_vf_put(d_vf); + return err; + } + dst_vlan_info = dst_vlans->vlans; + memcpy(dst_vlans, src_vlans, sizeof(nl_vf_vlans_t)); + memcpy(dst_vlan_info, src_vlan_info, + dst_vlans->size * sizeof(*dst_vlan_info)); + d_vf->vf_vlans = dst_vlans; + } + + nl_list_add_head(&d_vf->vf_list, &dest_h->vf_list); + dest_h = d_vf; + } + + return 0; +} + +/* Dump VLAN details for each SRIOV VF */ +static void dump_sriov_vlans(nl_vf_vlans_t *vlans, + struct nl_dump_params *p) { + char buf[64]; + int cur = 0; + nl_vf_vlan_info_t *vlan_data; + uint16_t prot; + + vlan_data = vlans->vlans; + nl_dump(p, "\t VLANS:\n"); + while (cur < vlans->size) { + nl_dump(p, "\t vlan %u", vlan_data[cur].vf_vlan); + if (vlan_data[cur].vf_vlan_qos) + nl_dump(p, " qos %u", vlan_data[cur].vf_vlan_qos); + if (vlan_data[cur].vf_vlan_proto) { + prot = vlan_data[cur].vf_vlan_proto; + nl_dump(p, " proto %s", + rtnl_link_vf_vlanproto2str(prot, buf, + sizeof(buf))); + } + nl_dump(p, "\n"); + cur++; + } + + return; +} + +/* Dump details for each SRIOV VF */ +static void dump_vf_details(struct rtnl_link_vf *vf_data, + struct nl_dump_params *p) { + char buf[64]; + int err = 0; + struct nl_vf_rate vf_rate; + uint32_t v = 0; + + nl_dump(p, "\tvf %u: ", vf_data->vf_index); + if (vf_data->ce_mask & SRIOV_ATTR_LINK_STATE) { + v = vf_data->vf_linkstate; + nl_dump(p, "state %s ", + rtnl_link_vf_linkstate2str(v, buf, sizeof(buf))); + } + if (vf_data->ce_mask & SRIOV_ATTR_ADDR) { + nl_dump(p, "addr %s ", + nl_addr2str(vf_data->vf_lladdr, buf, sizeof(buf))); + } + nl_dump(p, "\n"); + + v = vf_data->vf_spoofchk; + nl_dump(p, "\t spoofchk %s ", v ? SRIOVON : SRIOVOFF); + v = vf_data->vf_trust; + nl_dump(p, "trust %s ", v ? SRIOVON : SRIOVOFF); + v = vf_data->vf_rss_query_en; + nl_dump(p, "rss_query %s\n", v ? SRIOVON : SRIOVOFF); + + err = rtnl_link_vf_get_rate(vf_data, &vf_rate); + if (!err) { + if (vf_rate.api == RTNL_LINK_VF_RATE_API_OLD) + nl_dump(p, "\t rate_api old rate %u\n", + vf_rate.rate); + else if (vf_rate.api == RTNL_LINK_VF_RATE_API_NEW) + nl_dump(p, "\t rate_api new min_rate %u " + "max_rate %u\n", vf_rate.min_tx_rate, + vf_rate.max_tx_rate); + } + if (vf_data->ce_mask & SRIOV_ATTR_VLAN) + dump_sriov_vlans(vf_data->vf_vlans, p); + + return; +} + +/* Loop through SRIOV VF list dump details */ +void rtnl_link_sriov_dump_details(struct rtnl_link *link, + struct nl_dump_params *p) { + struct rtnl_link_vf *vf_data, *list, *next; + + if (!rtnl_link_has_vf_list(link)) + BUG(); + + nl_dump(p, " SRIOV VF List\n"); + list = link->l_vf_list; + nl_list_for_each_entry_safe(vf_data, next, &list->vf_list, vf_list) { + if (vf_data->ce_mask & SRIOV_ATTR_INDEX) + dump_vf_details(vf_data, p); + } + + return; +} + +/* Dump stats for each SRIOV VF */ +static void dump_vf_stats(struct rtnl_link_vf *vf_data, + struct nl_dump_params *p) { + char *unit; + float res; + + nl_dump(p, " VF %u Stats:\n", vf_data->vf_index); + nl_dump_line(p, "\tRX: %-14s %-10s %-10s %-10s\n", + "bytes", "packets", "multicast", "broadcast"); + + res = nl_cancel_down_bytes(vf_data->vf_stats[RTNL_LINK_VF_STATS_RX_BYTES], + &unit); + + nl_dump_line(p, + "\t%10.2f %3s %10" PRIu64 " %10" PRIu64 " %10" PRIu64 "\n", + res, unit, + vf_data->vf_stats[RTNL_LINK_VF_STATS_RX_PACKETS], + vf_data->vf_stats[RTNL_LINK_VF_STATS_MULTICAST], + vf_data->vf_stats[RTNL_LINK_VF_STATS_BROADCAST]); + + nl_dump_line(p, "\tTX: %-14s %-10s\n", "bytes", "packets"); + + res = nl_cancel_down_bytes(vf_data->vf_stats[RTNL_LINK_VF_STATS_TX_BYTES], + &unit); + + nl_dump_line(p, "\t%10.2f %3s %10" PRIu64 "\n", res, unit, + vf_data->vf_stats[RTNL_LINK_VF_STATS_TX_PACKETS]); + + return; +} + +/* Loop through SRIOV VF list dump stats */ +void rtnl_link_sriov_dump_stats(struct rtnl_link *link, + struct nl_dump_params *p) { + struct rtnl_link_vf *vf_data, *list, *next; + + list = link->l_vf_list; + nl_list_for_each_entry_safe(vf_data, next, &list->vf_list, vf_list) { + if (vf_data->ce_mask & SRIOV_ATTR_INDEX) + dump_vf_stats(vf_data, p); + } + nl_dump(p, "\n"); + + return; +} + +/* Free stored SRIOV VF data */ +void rtnl_link_sriov_free_data(struct rtnl_link *link) { + struct rtnl_link_vf *list, *vf, *next; + + if (!rtnl_link_has_vf_list(link)) + return; + + list = link->l_vf_list; + nl_list_for_each_entry_safe(vf, next, &list->vf_list, vf_list) { + nl_list_del(&vf->vf_list); + rtnl_link_vf_put(vf); + } + + rtnl_link_vf_put(link->l_vf_list); + + return; +} + +/* Fill VLAN info array */ +static int rtnl_link_vf_vlan_info(int len, struct ifla_vf_vlan_info **vi, + nl_vf_vlans_t **nvi) { + int cur = 0, err; + nl_vf_vlans_t *vlans; + + if (len <= 0) + return 0; + + if ((err = rtnl_link_vf_vlan_alloc(&vlans, len)) < 0) + return err; + + cur = 0; + while (cur < len) { + vlans->vlans[cur].vf_vlan = vi[cur]->vlan ? vi[cur]->vlan : 0; + vlans->vlans[cur].vf_vlan_qos = vi[cur]->qos ? vi[cur]->qos : 0; + if (vi[cur]->vlan_proto) { + vlans->vlans[cur].vf_vlan_proto = ntohs(vi[cur]->vlan_proto); + } else { + vlans->vlans[cur].vf_vlan_proto = ETH_P_8021Q; + } + cur++; + } + + *nvi = vlans; + return 0; +} + +/* Fill the IFLA_VF_VLAN attribute */ +static void sriov_fill_vf_vlan(struct nl_msg *msg, nl_vf_vlan_info_t *vinfo, + uint32_t index) { + struct ifla_vf_vlan vlan; + + vlan.vf = index; + vlan.vlan = vinfo[0].vf_vlan; + vlan.qos = vinfo[0].vf_vlan_qos; + NLA_PUT(msg, IFLA_VF_VLAN, sizeof(vlan), &vlan); + +nla_put_failure: + return; +} + +/* Fill the IFLA_VF_VLAN_LIST attribute */ +static int sriov_fill_vf_vlan_list(struct nl_msg *msg, nl_vf_vlans_t *vlans, + uint32_t index) { + int cur = 0; + nl_vf_vlan_info_t *vlan_info = vlans->vlans; + struct ifla_vf_vlan_info vlan; + struct nlattr *list; + + if (!(list = nla_nest_start(msg, IFLA_VF_VLAN_LIST))) + return -NLE_MSGSIZE; + + vlan.vf = index; + while (cur < vlans->size) { + vlan.vlan = vlan_info[cur].vf_vlan; + vlan.qos = vlan_info[cur].vf_vlan_qos; + vlan.vlan_proto = vlan_info[cur].vf_vlan_proto; + + NLA_PUT(msg, IFLA_VF_VLAN_INFO, sizeof(vlan), &vlan); + + cur++; + } + +nla_put_failure: + nla_nest_end(msg, list); + + return 0; +} + +/* Fill individual IFLA_VF_INFO attributes */ +static int sriov_fill_vfinfo(struct nl_msg *msg, + struct rtnl_link_vf *vf_data) { + int err = 0, new_rate = 0; + nl_vf_vlans_t *vlan_list; + nl_vf_vlan_info_t *vlan_info; + struct ifla_vf_guid vf_node_guid; + struct ifla_vf_guid vf_port_guid; + struct ifla_vf_link_state vf_link_state; + struct ifla_vf_mac vf_mac; + struct ifla_vf_rate new_vf_rate; + struct ifla_vf_rss_query_en vf_rss_query_en; + struct ifla_vf_spoofchk vf_spoofchk; + struct ifla_vf_trust vf_trust; + struct ifla_vf_tx_rate vf_rate; + struct nlattr *list; + uint16_t proto; + + if (!(vf_data->ce_mask & SRIOV_ATTR_INDEX)) + return -NLE_MISSING_ATTR; + + if (!(list = nla_nest_start(msg, IFLA_VF_INFO))) + return -NLE_MSGSIZE; + + /* IFLA_VF_MAC */ + if (vf_data->ce_mask & SRIOV_ATTR_ADDR) { + vf_mac.vf = vf_data->vf_index; + memset(vf_mac.mac, 0, sizeof(vf_mac.mac)); + memcpy(vf_mac.mac, nl_addr_get_binary_addr(vf_data->vf_lladdr), + nl_addr_get_len(vf_data->vf_lladdr)); + NLA_PUT(msg, IFLA_VF_MAC, sizeof(vf_mac), &vf_mac); + } + + /* IFLA_VF_VLAN IFLA_VF_VLAN_LIST */ + if (vf_data->ce_mask & SRIOV_ATTR_VLAN) { + vlan_list = vf_data->vf_vlans; + vlan_info = vlan_list->vlans; + proto = vlan_info[0].vf_vlan_proto; + if (!proto) + proto = ETH_P_8021Q; + + if ((vlan_list->size == 1) && (proto == ETH_P_8021Q)) + sriov_fill_vf_vlan(msg, vlan_info, vf_data->vf_index); + else + err = sriov_fill_vf_vlan_list(msg, vlan_list, + vf_data->vf_index); + } + + /* IFLA_VF_TX_RATE */ + if (vf_data->ce_mask & SRIOV_ATTR_TX_RATE) { + vf_rate.vf = vf_data->vf_index; + vf_rate.rate = vf_data->vf_rate; + + NLA_PUT(msg, IFLA_VF_TX_RATE, sizeof(vf_rate), &vf_rate); + } + + /* IFLA_VF_RATE */ + new_vf_rate.min_tx_rate = 0; + new_vf_rate.max_tx_rate = 0; + new_vf_rate.vf = vf_data->vf_index; + if (vf_data->ce_mask & SRIOV_ATTR_RATE_MIN) { + new_vf_rate.min_tx_rate = vf_data->vf_min_tx_rate; + new_rate = 1; + } + if (vf_data->ce_mask & SRIOV_ATTR_RATE_MAX) { + new_vf_rate.max_tx_rate = vf_data->vf_max_tx_rate; + new_rate = 1; + } + if (new_rate) + NLA_PUT(msg, IFLA_VF_RATE, sizeof(new_vf_rate), &new_vf_rate); + + /* IFLA_VF_SPOOFCHK */ + if (vf_data->ce_mask & SRIOV_ATTR_SPOOFCHK) { + vf_spoofchk.vf = vf_data->vf_index; + vf_spoofchk.setting = vf_data->vf_spoofchk; + + NLA_PUT(msg, IFLA_VF_SPOOFCHK, sizeof(vf_spoofchk), + &vf_spoofchk); + } + + /* IFLA_VF_LINK_STATE */ + if (vf_data->ce_mask & SRIOV_ATTR_LINK_STATE) { + vf_link_state.vf = vf_data->vf_index; + vf_link_state.link_state = vf_data->vf_linkstate; + + NLA_PUT(msg, IFLA_VF_LINK_STATE, sizeof(vf_link_state), + &vf_link_state); + } + + /* IFLA_VF_RSS_QUERY_EN */ + if (vf_data->ce_mask & SRIOV_ATTR_RSS_QUERY_EN) { + vf_rss_query_en.vf = vf_data->vf_index; + vf_rss_query_en.setting = vf_data->vf_rss_query_en; + + NLA_PUT(msg, IFLA_VF_RSS_QUERY_EN, sizeof(vf_rss_query_en), + &vf_rss_query_en); + } + + /* IFLA_VF_TRUST */ + if (vf_data->ce_mask & SRIOV_ATTR_TRUST) { + vf_trust.vf = vf_data->vf_index; + vf_trust.setting = vf_data->vf_trust; + + NLA_PUT(msg, IFLA_VF_TRUST, sizeof(vf_trust), &vf_trust); + } + + /* IFLA_VF_IB_NODE_GUID */ + if (vf_data->ce_mask & SRIOV_ATTR_IB_NODE_GUID) { + vf_node_guid.vf = vf_data->vf_index; + vf_node_guid.guid = vf_data->vf_guid_node; + + NLA_PUT(msg, IFLA_VF_IB_NODE_GUID, sizeof(vf_node_guid), + &vf_node_guid); + } + + /* IFLA_VF_IB_PORT_GUID */ + if (vf_data->ce_mask & SRIOV_ATTR_IB_PORT_GUID) { + vf_port_guid.vf = vf_data->vf_index; + vf_port_guid.guid = vf_data->vf_guid_port; + + NLA_PUT(msg, IFLA_VF_IB_PORT_GUID, sizeof(vf_port_guid), + &vf_port_guid); + } + +nla_put_failure: + nla_nest_end(msg, list); + + return err; +} + +/* Fill the IFLA_VFINFO_LIST attribute */ +int rtnl_link_sriov_fill_vflist(struct nl_msg *msg, struct rtnl_link *link) { + int err = 0; + struct nlattr *data; + struct rtnl_link_vf *list, *vf, *next; + + if (!(err = rtnl_link_has_vf_list(link))) + return 0; + + if (!(data = nla_nest_start(msg, IFLA_VFINFO_LIST))) + return -NLE_MSGSIZE; + + list = link->l_vf_list; + nl_list_for_each_entry_safe(vf, next, &list->vf_list, vf_list) { + if (vf->ce_mask & SRIOV_ATTR_INDEX) { + if ((err = sriov_fill_vfinfo(msg, vf)) < 0) + goto nla_nest_list_failure; + } + } + +nla_nest_list_failure: + nla_nest_end(msg, data); + + return err; +} + +/* Parse IFLA_VFINFO_LIST and IFLA_VF_INFO attributes */ +int rtnl_link_sriov_parse_vflist(struct rtnl_link *link, struct nlattr **tb) { + int err, len, list_len, list_rem; + struct ifla_vf_mac *vf_lladdr; + struct ifla_vf_vlan *vf_vlan; + struct ifla_vf_vlan_info *vf_vlan_info[MAX_VLAN_LIST_LEN]; + struct ifla_vf_tx_rate *vf_tx_rate; + struct ifla_vf_spoofchk *vf_spoofchk; + struct ifla_vf_link_state *vf_linkstate; + struct ifla_vf_rate *vf_rate; + struct ifla_vf_rss_query_en *vf_rss_query; + struct ifla_vf_trust *vf_trust; + struct nlattr *nla, *nla_list, *t[IFLA_VF_MAX+1], + *stb[RTNL_LINK_VF_STATS_MAX+1]; + nl_vf_vlans_t *vf_vlans = NULL; + struct rtnl_link_vf *vf_data, *vf_head = NULL; + + len = nla_len(tb[IFLA_VFINFO_LIST]); + link->l_vf_list = rtnl_link_vf_alloc(); + if (!link->l_vf_list) + return -NLE_NOMEM; + vf_head = link->l_vf_list; + + for (nla = nla_data(tb[IFLA_VFINFO_LIST]); nla_ok(nla, len); + nla = nla_next(nla, &len)) { + err = nla_parse(t, IFLA_VF_MAX, nla_data(nla), nla_len(nla), + sriov_info_policy); + if (err < 0) + return err; + + vf_data = rtnl_link_vf_alloc(); + if (!vf_data) + return -NLE_NOMEM; + + if (t[IFLA_VF_MAC]) { + vf_lladdr = nla_data(t[IFLA_VF_MAC]); + + vf_data->vf_index = vf_lladdr->vf; + vf_data->ce_mask |= SRIOV_ATTR_INDEX; + + vf_data->vf_lladdr = nl_addr_build(AF_LLC, + vf_lladdr->mac, 6); + if (vf_data->vf_lladdr == NULL) { + rtnl_link_vf_put(vf_data); + return -NLE_NOMEM; + } + nl_addr_set_family(vf_data->vf_lladdr, AF_LLC); + vf_data->ce_mask |= SRIOV_ATTR_ADDR; + } + + if (t[IFLA_VF_VLAN_LIST]) { + list_len = 0; + nla_for_each_nested(nla_list, t[IFLA_VF_VLAN_LIST], + list_rem) { + if (list_len >= MAX_VLAN_LIST_LEN) + break; + vf_vlan_info[list_len] = nla_data(nla_list); + list_len++; + } + + err = rtnl_link_vf_vlan_info(list_len, vf_vlan_info, + &vf_vlans); + if (err < 0) { + rtnl_link_vf_put(vf_data); + return err; + } + + vf_data->vf_vlans = vf_vlans; + vf_data->ce_mask |= SRIOV_ATTR_VLAN; + } else if (t[IFLA_VF_VLAN]) { + vf_vlan = nla_data(t[IFLA_VF_VLAN]); + + if (vf_vlan->vlan) { + err = rtnl_link_vf_vlan_alloc(&vf_vlans, 1); + if (err < 0) { + rtnl_link_vf_put(vf_data); + return err; + } + + vf_vlans->vlans[0].vf_vlan = vf_vlan->vlan; + vf_vlans->vlans[0].vf_vlan_qos = vf_vlan->qos; + vf_vlans->vlans[0].vf_vlan_proto = ETH_P_8021Q; + + vf_data->vf_vlans = vf_vlans; + vf_data->ce_mask |= SRIOV_ATTR_VLAN; + } + } + + if (t[IFLA_VF_TX_RATE]) { + vf_tx_rate = nla_data(t[IFLA_VF_TX_RATE]); + + if (vf_tx_rate->rate) { + vf_data->vf_rate = vf_tx_rate->rate; + vf_data->ce_mask |= SRIOV_ATTR_TX_RATE; + } + } + + if (t[IFLA_VF_SPOOFCHK]) { + vf_spoofchk = nla_data(t[IFLA_VF_SPOOFCHK]); + + if (vf_spoofchk->setting != ((uint32_t)-1)) { + vf_data->vf_spoofchk = vf_spoofchk->setting ? 1 : 0; + vf_data->ce_mask |= SRIOV_ATTR_SPOOFCHK; + } + } + + if (t[IFLA_VF_LINK_STATE]) { + vf_linkstate = nla_data(t[IFLA_VF_LINK_STATE]); + + vf_data->vf_linkstate = vf_linkstate->link_state; + vf_data->ce_mask |= SRIOV_ATTR_LINK_STATE; + } + + if (t[IFLA_VF_RATE]) { + vf_rate = nla_data(t[IFLA_VF_RATE]); + + if (vf_rate->max_tx_rate) { + vf_data->vf_max_tx_rate = vf_rate->max_tx_rate; + vf_data->ce_mask |= SRIOV_ATTR_RATE_MAX; + } + if (vf_rate->min_tx_rate) { + vf_data->vf_min_tx_rate = vf_rate->min_tx_rate; + vf_data->ce_mask |= SRIOV_ATTR_RATE_MIN; + } + } + + if (t[IFLA_VF_RSS_QUERY_EN]) { + vf_rss_query = nla_data(t[IFLA_VF_RSS_QUERY_EN]); + + if (vf_rss_query->setting != ((uint32_t)-1)) { + vf_data->vf_rss_query_en = vf_rss_query->setting ? 1 : 0; + vf_data->ce_mask |= SRIOV_ATTR_RSS_QUERY_EN; + } + } + + if (t[IFLA_VF_STATS]) { + err = nla_parse_nested(stb, RTNL_LINK_VF_STATS_MAX, + t[IFLA_VF_STATS], + sriov_stats_policy); + if (err < 0) { + rtnl_link_vf_put(vf_data); + return err; + } + + SET_VF_STAT(link, cur, stb, + RTNL_LINK_VF_STATS_RX_PACKETS, + IFLA_VF_STATS_RX_PACKETS); + SET_VF_STAT(link, cur, stb, + RTNL_LINK_VF_STATS_TX_PACKETS, + IFLA_VF_STATS_TX_PACKETS); + SET_VF_STAT(link, cur, stb, + RTNL_LINK_VF_STATS_RX_BYTES, + IFLA_VF_STATS_RX_BYTES); + SET_VF_STAT(link, cur, stb, + RTNL_LINK_VF_STATS_TX_BYTES, + IFLA_VF_STATS_TX_BYTES); + SET_VF_STAT(link, cur, stb, + RTNL_LINK_VF_STATS_BROADCAST, + IFLA_VF_STATS_BROADCAST); + SET_VF_STAT(link, cur, stb, + RTNL_LINK_VF_STATS_MULTICAST, + IFLA_VF_STATS_MULTICAST); + + vf_data->ce_mask |= SRIOV_ATTR_STATS; + } + + if (t[IFLA_VF_TRUST]) { + vf_trust = nla_data(t[IFLA_VF_TRUST]); + + if (vf_trust->setting != ((uint32_t)-1)) { + vf_data->vf_trust = vf_trust->setting ? 1 : 0; + vf_data->ce_mask |= SRIOV_ATTR_TRUST; + } + } + + nl_list_add_head(&vf_data->vf_list, &vf_head->vf_list); + vf_head = vf_data; + } + + return 0; +} + +/** + * @name SR-IOV Sub-Object + * @{ + */ + +/** + * Add a SRIOV VF object to a link object + * @param link Link object to add to + * @param vf_data SRIOV VF object to add + * + * @return 0 if SRIOV VF object added successfully + * @return -NLE_OBJ_NOTFOUND if \p link or \p vf_data not provided + * @return -NLE_NOMEM if out of memory + */ +int rtnl_link_vf_add(struct rtnl_link *link, struct rtnl_link_vf *vf_data) { + struct rtnl_link_vf *vf_head = NULL; + + if (!link||!vf_data) + return -NLE_OBJ_NOTFOUND; + + if (!link->l_vf_list) { + link->l_vf_list = rtnl_link_vf_alloc(); + if (!link->l_vf_list) + return -NLE_NOMEM; + } + + vf_head = vf_data; + vf_head->ce_refcnt++; + + vf_head = link->l_vf_list; + nl_list_add_head(&vf_data->vf_list, &vf_head->vf_list); + link->l_vf_list = vf_head; + + rtnl_link_set_vf_list(link); + + return 0; +} + +/** + * Allocate a new SRIOV VF object + * + * @return NULL if out of memory + * @return New VF Object + * + * @see rtnl_link_vf_put() + * + * The SRIOV VF object must be returned to the link object with + * rtnl_link_vf_put() when operations are done to prevent memory leaks. + */ +struct rtnl_link_vf *rtnl_link_vf_alloc(void) { + struct rtnl_link_vf *vf; + + if (!(vf = calloc(1, sizeof(*vf)))) + return NULL; + + NL_INIT_LIST_HEAD(&vf->vf_list); + vf->ce_refcnt = 1; + + NL_DBG(4, "Allocated new SRIOV VF object %p\n", vf); + + return vf; +} + +/** + * Free SRIOV VF object. + * @arg vf_data SRIOV VF data object + */ +void rtnl_link_vf_free(struct rtnl_link_vf *vf_data) { + if (!vf_data) + return; + + if (vf_data->ce_refcnt > 0) + NL_DBG(1, "Warning: Freeing SRIOV VF object in use...\n"); + + if (vf_data->ce_mask & SRIOV_ATTR_ADDR) + nl_addr_put(vf_data->vf_lladdr); + if (vf_data->ce_mask & SRIOV_ATTR_VLAN) + rtnl_link_vf_vlan_put(vf_data->vf_vlans); + + NL_DBG(4, "Freed SRIOV VF object %p\n", vf_data); + free(vf_data); + + return; +} + +/** + * Lookup SRIOV VF in link object by VF index. + * + * @return NULL if VF not found + * @return VF Object + * + * @see rtnl_link_vf_put() + * + * The SRIOV VF object must be returned to the link object with + * rtnl_link_vf_put() when operations are done to prevent memory leaks. + */ +struct rtnl_link_vf *rtnl_link_vf_get(struct rtnl_link *link, uint32_t vf_num) { + struct rtnl_link_vf *list, *vf, *next, *ret = NULL; + + list = link->l_vf_list; + nl_list_for_each_entry_safe(vf, next, &list->vf_list, vf_list) { + if (vf->vf_index == vf_num) { + ret = vf; + break; + } + } + + if (ret) { + ret->ce_refcnt++; + NL_DBG(4, "New reference to SRIOV VF object %p, total %i\n", + ret, ret->ce_refcnt); + } + + return ret; +} + +/** + * Return SRIOV VF object to the owning link object. + * @arg vf_data SRIOV VF data object + * + * @see rtnl_link_vf_alloc() + * @see rtnl_link_vf_get() + */ +void rtnl_link_vf_put(struct rtnl_link_vf *vf_data) { + if (!vf_data) + return; + + vf_data->ce_refcnt--; + NL_DBG(4, "Returned SRIOV VF object reference %p, %i remaining\n", + vf_data, vf_data->ce_refcnt); + + if (vf_data->ce_refcnt < 0) + BUG(); + + if (vf_data->ce_refcnt <= 0) + rtnl_link_vf_free(vf_data); + + return; +} + +/** + * Get link layer address of SRIOV Virtual Function + * @arg vf_data SRIOV VF object + * @arg addr Pointer to store Link Layer address + * + * @see rtnl_link_get_num_vf() + * @see rtnl_link_vf_set_addr() + * + * @copydoc pointer_lifetime_warning + * @return 0 if addr is present and addr is set to pointer containing address + * @return -NLE_OBJ_NOTFOUND if information for VF info is not found + * @return -NLE_NOATTR if the link layer address is not set + */ +int rtnl_link_vf_get_addr(struct rtnl_link_vf *vf_data, struct nl_addr **addr) +{ + if (!vf_data) + return -NLE_OBJ_NOTFOUND; + + if (vf_data->ce_mask & SRIOV_ATTR_ADDR) + *addr = vf_data->vf_lladdr; + else + return -NLE_NOATTR; + + return 0; +} + +/** + * Set link layer address of SRIOV Virtual Function object + * @param vf_data SRIOV VF object + * @param addr New link layer address + * + * This function increments the reference counter of the address object + * and overwrites any existing link layer address previously assigned. + * + * @see rtnl_link_vf_get_addr() + */ +void rtnl_link_vf_set_addr(struct rtnl_link_vf *vf_data, struct nl_addr *addr) { + if (vf_data->vf_lladdr) + nl_addr_put(vf_data->vf_lladdr); + + nl_addr_get(addr); + vf_data->vf_lladdr = addr; + vf_data->ce_mask |= SRIOV_ATTR_ADDR; + + return; +} + +/** + * Set the Infiniband node GUID for the SRIOV Virtual Function object + * @param vf_data SRIOV VF object + * @param guid node GUID + */ +void rtnl_link_vf_set_ib_node_guid(struct rtnl_link_vf *vf_data, + uint64_t guid) { + vf_data->vf_guid_node = guid; + vf_data->ce_mask |= SRIOV_ATTR_IB_NODE_GUID; + + return; +} + +/** + * Set the Infiniband port GUID for the SRIOV Virtual Function object + * @param vf_data SRIOV VF object + * @param guid port GUID + */ +void rtnl_link_vf_set_ib_port_guid(struct rtnl_link_vf *vf_data, + uint64_t guid) { + vf_data->vf_guid_port = guid; + vf_data->ce_mask |= SRIOV_ATTR_IB_PORT_GUID; + + return; +} + +/** + * Get index of SRIOV Virtual Function + * @arg vf_data SRIOV VF object + * @arg vf_index Pointer to store VF index + * + * @see rtnl_link_get_num_vf() + * + * @return 0 if index is present and vf_index is set + * @return -NLE_OBJ_NOTFOUND if information for VF info is not found + * @return -NLE_NOATTR if the VF index is not set + */ +int rtnl_link_vf_get_index(struct rtnl_link_vf *vf_data, uint32_t *vf_index) +{ + if (!vf_data) + return -NLE_OBJ_NOTFOUND; + + if (vf_data->ce_mask & SRIOV_ATTR_INDEX) + *vf_index = vf_data->vf_index; + else + return -NLE_NOATTR; + + return 0; +} + +/** + * Set index of SRIOV Virtual Function object + * @param vf_data SRIOV VF object + * @param vf_index Index value + * + * @see rtnl_link_vf_get_index() + */ +void rtnl_link_vf_set_index(struct rtnl_link_vf *vf_data, uint32_t vf_index) +{ + vf_data->vf_index = vf_index; + vf_data->ce_mask |= SRIOV_ATTR_INDEX; + + return; +} + +/** + * Get link state of SRIOV Virtual Function + * @arg vf_data SRIOV VF object + * @arg vf_linkstate Pointer to store VF link state + * + * @see rtnl_link_get_num_vf() + * @see rtnl_link_set_linkstate() + * + * @return 0 if link state is present and vf_linkstate is set + * @return -NLE_OBJ_NOTFOUND if information for VF info is not found + * @return -NLE_NOATTR if the VF link state is not set + */ +int rtnl_link_vf_get_linkstate(struct rtnl_link_vf *vf_data, + uint32_t *vf_linkstate) +{ + if (!vf_data) + return -NLE_OBJ_NOTFOUND; + + if (vf_data->ce_mask & SRIOV_ATTR_LINK_STATE) + *vf_linkstate = vf_data->vf_linkstate; + else + return -NLE_NOATTR; + + return 0; +} + +/** + * Set link state of SRIOV Virtual Function object + * @param vf_data SRIOV VF object + * @param vf_linkstate Link state value + * + * @see rtnl_link_get_linkstate() + * + * Not all hardware supports setting link state. If the feature is unsupported, + * the link change request will fail with -NLE_OPNOTSUPP + */ +void rtnl_link_vf_set_linkstate(struct rtnl_link_vf *vf_data, + uint32_t vf_linkstate) { + vf_data->vf_linkstate = vf_linkstate; + vf_data->ce_mask |= SRIOV_ATTR_LINK_STATE; + + return; +} + +/** + * Get TX Rate Limit of SRIOV Virtual Function + * @arg vf_data SRIOV VF object + * @arg vf_rate Pointer to store VF rate limiting data + * + * @see rtnl_link_get_num_vf() + * @see rtnl_link_set_rate() + * + * When the older rate API has been implemented, the rate member of the struct + * will be set, and the api member will be set to RTNL_LINK_VF_API_OLD. + * When the newer rate API has been implemented, the max_tx_rate + * and/or the minx_tx_rate will be set, and the api member will be set to + * RTNL_LINK_VF_API_NEW. + * + * Old rate API supports only a maximum TX rate. + * ip link set dev vf 0 rate + * New rate API supports minumum and maximum TX rates. + * ip link set dev vf 0 min_tx_rate + * ip link set dev vf 0 max_tx_rate + * + * @return 0 if rate is present and vf_rate is set + * @return -NLE_OBJ_NOTFOUND if information for VF info is not found + * @return -NLE_NOATTR if the VF rate is not set + */ +int rtnl_link_vf_get_rate(struct rtnl_link_vf *vf_data, + struct nl_vf_rate *vf_rate) +{ + int set = 0; + + if (!vf_data) + return -NLE_OBJ_NOTFOUND; + + vf_rate->api = RTNL_LINK_VF_RATE_API_UNSPEC; + vf_rate->rate = 0; + vf_rate->max_tx_rate = 0; + vf_rate->min_tx_rate = 0; + + if (vf_data->ce_mask & SRIOV_ATTR_RATE_MAX) { + if (vf_data->vf_max_tx_rate) { + vf_rate->api = RTNL_LINK_VF_RATE_API_NEW; + vf_rate->max_tx_rate = vf_data->vf_max_tx_rate; + set = 1; + } + } + if (vf_data->ce_mask & SRIOV_ATTR_RATE_MIN) { + if (vf_data->vf_min_tx_rate) { + vf_rate->api = RTNL_LINK_VF_RATE_API_NEW; + vf_rate->min_tx_rate = vf_data->vf_min_tx_rate; + set = 1; + } + } + if ((!set) && (vf_data->ce_mask & SRIOV_ATTR_TX_RATE)) { + if (vf_data->vf_rate) { + vf_rate->api = RTNL_LINK_VF_RATE_API_OLD; + vf_rate->rate = vf_data->vf_rate; + set = 1; + } + } + + if (!set) + return -NLE_NOATTR; + + return 0; +} + +/** + * Set TX Rate Limit of SRIOV Virtual Function object + * @param vf_data SRIOV VF object + * @param vf_rate Rate limiting structure + * + * @see rtnl_link_vf_get_rate() + * + * When setting the rate, the API level must be specificed. + * Valid API levels: + * RTNL_LINK_VF_RATE_API_NEW + * RTNL_LINK_VF_RATE_API_OLD + * + * When using the new API, if either the min_tx_rate or + * max_tx_rate has been set, and the other is being changed, + * you must specify the currently set values to preserve + * them. If this is not done, that setting will be disabled. + * + * Old rate API supports only a maximum TX rate. + * ip link set dev vf 0 rate + * New rate API supports minumum and maximum TX rates. + * ip link set dev vf 0 min_tx_rate + * ip link set dev vf 0 max_tx_rate + * + * Not all hardware supports min_tx_rate. + */ +void rtnl_link_vf_set_rate(struct rtnl_link_vf *vf_data, + struct nl_vf_rate *vf_rate) { + if (vf_rate->api == RTNL_LINK_VF_RATE_API_OLD) { + vf_data->vf_rate = vf_rate->rate; + vf_data->ce_mask |= SRIOV_ATTR_TX_RATE; + } else if (vf_rate->api == RTNL_LINK_VF_RATE_API_NEW) { + vf_data->vf_max_tx_rate = vf_rate->max_tx_rate; + vf_data->ce_mask |= SRIOV_ATTR_RATE_MAX; + + vf_data->vf_min_tx_rate = vf_rate->min_tx_rate; + vf_data->ce_mask |= SRIOV_ATTR_RATE_MIN; + } + + return; +} + +/** + * Get RSS Query EN value of SRIOV Virtual Function + * @arg vf_data SRIOV VF object + * @arg vf_rss_query_en Pointer to store VF RSS Query value + * + * @see rtnl_link_get_num_vf() + * @see rtnl_link_vf_set_rss_query_en() + * + * @return 0 if rss_query_en is present and vf_rss_query_en is set + * @return -NLE_OBJ_NOTFOUND if information for VF info is not found + * @return -NLE_NOATTR if the VF RSS Query EN value is not set + */ +int rtnl_link_vf_get_rss_query_en(struct rtnl_link_vf *vf_data, + uint32_t *vf_rss_query_en) +{ + if (!vf_data) + return -NLE_OBJ_NOTFOUND; + + if (vf_data->ce_mask & SRIOV_ATTR_RSS_QUERY_EN) + *vf_rss_query_en = vf_data->vf_rss_query_en; + else + return -NLE_NOATTR; + + return 0; +} + +/** + * Set RSS configuration querying of SRIOV Virtual Function Object + * @arg vf_data SRIOV VF object + * @arg vf_rss_query_en RSS Query value + * + * @see rtnl_link_vf_get_rss_query_en() + */ +void rtnl_link_vf_set_rss_query_en(struct rtnl_link_vf *vf_data, + uint32_t vf_rss_query_en) { + vf_data->vf_rss_query_en = vf_rss_query_en; + vf_data->ce_mask |= SRIOV_ATTR_RSS_QUERY_EN; + + return; +} + +/** + * Get spoof checking value of SRIOV Virtual Function + * @arg vf_data SRIOV VF object + * @arg vf_spoofchk Pointer to store VF spoofchk value + * + * @see rtnl_link_get_num_vf() + * @see rtnl_link_set_spoofchk() + * + * @return 0 if spoofchk is present and vf_spoofchk is set + * @return -NLE_OBJ_NOTFOUND if information for VF info is not found + * @return -NLE_NOATTR if the VF spoofcheck is not set + */ +int rtnl_link_vf_get_spoofchk(struct rtnl_link_vf *vf_data, + uint32_t *vf_spoofchk) +{ + if (!vf_data) + return -NLE_OBJ_NOTFOUND; + + if (vf_data->ce_mask & SRIOV_ATTR_SPOOFCHK) + *vf_spoofchk = vf_data->vf_spoofchk; + else + return -NLE_NOATTR; + + return 0; +} + +/** + * Set spoof checking value of SRIOV Virtual Function Object + * @param vf_data + * @param vf_spoofchk + * + * @see rtnl_link_vf_get_spoofchk() + */ +void rtnl_link_vf_set_spoofchk(struct rtnl_link_vf *vf_data, + uint32_t vf_spoofchk) { + vf_data->vf_spoofchk = vf_spoofchk; + vf_data->ce_mask |= SRIOV_ATTR_SPOOFCHK; + + return; +} + +/** + * Get value of stat counter for SRIOV Virtual Function + * @arg vf_data SRIOV VF object + * @arg stat Identifier of statistical counter + * @arg vf_stat Pointer to store VF stat value in + * + * @see rtnl_link_get_num_vf() + * + * @return 0 if stat is present and vf_stat is set + * @return -NLE_OBJ_NOTFOUND if information for VF info is not found + * @return -NLE_NOATTR if the VF stat is not set + */ +int rtnl_link_vf_get_stat(struct rtnl_link_vf *vf_data, + rtnl_link_vf_stats_t stat, uint64_t *vf_stat) +{ + if (!vf_data) + return -NLE_OBJ_NOTFOUND; + + if (vf_data->ce_mask & SRIOV_ATTR_STATS) + *vf_stat = vf_data->vf_stats[stat]; + else + return -NLE_NOATTR; + + return 0; +} + +/** + * Get trust setting of SRIOV Virtual Function + * @arg vf_data SRIOV VF object + * @arg vf_trust Pointer to store VF trust value + * + * @see rtnl_link_get_num_vf() + * @see rtnl_link_set_trust() + * + * @return 0 if trust is present and vf_trust is set + * @return -NLE_OBJ_NOTFOUND if information for VF info is not found + * @return -NLE_NOATTR if the VF trust setting is not set + */ +int rtnl_link_vf_get_trust(struct rtnl_link_vf *vf_data, uint32_t *vf_trust) +{ + if (!vf_data) + return -NLE_OBJ_NOTFOUND; + + if (vf_data->ce_mask & SRIOV_ATTR_TRUST) + *vf_trust = vf_data->vf_trust; + else + return -NLE_NOATTR; + + return 0; +} + +/** + * Set user trust setting on SRIOV Virtual Function Object + * @param vf_data + * @param vf_trust + * + * @see rtnl_link_vf_get_trust() + */ +void rtnl_link_vf_set_trust(struct rtnl_link_vf *vf_data, uint32_t vf_trust) { + vf_data->vf_trust = vf_trust; + vf_data->ce_mask |= SRIOV_ATTR_TRUST; + + return; +} + +/** + * Get an array of VLANS on SRIOV Virtual Function + * @arg vf_data SRIOV VF object + * @arg vf_vlans Pointer to nl_vf_vlans_t struct to store vlan info. + * + * @see rtnl_link_get_num_vf() + * + * The SRIOV VF VLANs object must be returned to the SRIOV VF object with + * rtnl_link_vf_vlans_put() when operations are done to prevent memory leaks. + * + * @copydoc pointer_lifetime_warning + * @return 0 if VLAN info is present and vf_vlans is set + * @return -NLE_OBJ_NOTFOUND if information for VF info is not found + * @return -NLE_NOATTR if the VF vlans is not set + */ +int rtnl_link_vf_get_vlans(struct rtnl_link_vf *vf_data, + nl_vf_vlans_t **vf_vlans) { + nl_vf_vlans_t *vf; + + if (!vf_data) + return -NLE_OBJ_NOTFOUND; + + if (vf_data->ce_mask & SRIOV_ATTR_VLAN) { + vf = vf_data->vf_vlans; + vf->ce_refcnt++; + *vf_vlans = vf; + } else + return -NLE_NOATTR; + + return 0; +} + +/** + * Add a SRIOV VF VLANs object to the SRIOV Virtual Function Object + * @param vf_data SRIOV VF object + * @param vf_vlans SRIOV VF VLANs object + * + * @see rtnl_link_vf_get_vlans() + * @see rtnl_link_vf_vlan_alloc() + * + * This function assigns ownership of the SRIOV VF object \p vf_vlans + * to the SRIOV Virtual Function object \p vf_data. Do not use + * rtnl_link_vf_vlan_put() on \p vf_vlans after this. + */ +void rtnl_link_vf_set_vlans(struct rtnl_link_vf *vf_data, + nl_vf_vlans_t *vf_vlans) { + if (!vf_data||!vf_vlans) + return; + + vf_data->vf_vlans = vf_vlans; + vf_data->vf_vlans->ce_refcnt++; + vf_data->ce_mask |= SRIOV_ATTR_VLAN; + + return; +} + +/** + * Allocate a SRIOV VF VLAN object + * @param vf_vlans Pointer to store VLAN object at + * @param vlan_count Number of VLANs that will be stored in VLAN object + * + * The SRIOV VF VLANs object must be returned to the sRIOV VF object with + * rtnl_link_vf_vlan_put() when operations are done to prevent memory leaks. + * + * @return 0 if VLAN object is created and vf_vlans is set. + * @return -NLE_NOMEM if object could not be allocated. + * @return -NLE_INVAL if vlan_count is more than supported by SRIOV VF + */ +int rtnl_link_vf_vlan_alloc(nl_vf_vlans_t **vf_vlans, int vlan_count) { + nl_vf_vlans_t *vlans; + nl_vf_vlan_info_t *vlan_info; + + if (vlan_count > MAX_VLAN_LIST_LEN) + return -NLE_INVAL; + + vlans = calloc(1, sizeof(*vlans)); + if (!vlans) + return -NLE_NOMEM; + + vlan_info = calloc(vlan_count+1, sizeof(*vlan_info)); + if (!vlan_info) { + free(vlans); + return -NLE_NOMEM; + } + + NL_DBG(4, "Allocated new SRIOV VF VLANs object %p\n", vlans); + + vlans->ce_refcnt = 1; + vlans->size = vlan_count; + vlans->vlans = vlan_info; + *vf_vlans = vlans; + + return 0; +} + +/** + * Free an allocated SRIOV VF VLANs object + * @param vf_vlans SRIOV VF VLANs object + */ +void rtnl_link_vf_vlan_free(nl_vf_vlans_t *vf_vlans) { + if (!vf_vlans) + return; + + if (vf_vlans->ce_refcnt > 0) + NL_DBG(1, "Warning: Freeing SRIOV VF VLANs object in use...\n"); + + NL_DBG(4, "Freed SRIOV VF object %p\n", vf_vlans); + free(vf_vlans->vlans); + free(vf_vlans); + + return; +} + +/** + * Return SRIOV VF VLANs object to the owning SRIOV VF object. + * @param vf_vlans SRIOV VF VLANs object + */ +void rtnl_link_vf_vlan_put(nl_vf_vlans_t *vf_vlans) { + if (!vf_vlans) + return; + + vf_vlans->ce_refcnt--; + NL_DBG(4, "Returned SRIOV VF VLANs object reference %p, %i remaining\n", + vf_vlans, vf_vlans->ce_refcnt); + + if (vf_vlans->ce_refcnt < 0) + BUG(); + + if (vf_vlans->ce_refcnt <= 0) + rtnl_link_vf_vlan_free(vf_vlans); + + return; +} + +/** @} */ + +/** + * @name Utilities + * @{ + */ + +static const struct trans_tbl vf_link_states[] = { + __ADD(IFLA_VF_LINK_STATE_AUTO, autodetect), + __ADD(IFLA_VF_LINK_STATE_ENABLE, up), + __ADD(IFLA_VF_LINK_STATE_DISABLE, down), +}; + +char *rtnl_link_vf_linkstate2str(uint32_t ls, char *buf, size_t len) +{ + return __type2str(ls, buf, len, vf_link_states, + ARRAY_SIZE(vf_link_states)); +} + +int rtnl_link_vf_str2linkstate(const char *name) +{ + return __str2type(name, vf_link_states, ARRAY_SIZE(vf_link_states)); +} + +static const struct trans_tbl vf_vlan_proto[] = { + __ADD(ETH_P_8021Q, 8021Q), + __ADD(ETH_P_8021AD, 8021AD), +}; + +char *rtnl_link_vf_vlanproto2str(uint16_t proto, char *buf, size_t len) +{ + return __type2str(proto, buf, len, vf_vlan_proto, + ARRAY_SIZE(vf_vlan_proto)); +} + +int rtnl_link_vf_str2vlanproto(const char *name) +{ + return __str2type(name, vf_vlan_proto, ARRAY_SIZE(vf_vlan_proto)); +} + +/* Return a guid from a format checked string. + * Format string must be xx:xx:xx:xx:xx:xx:xx:xx where XX can be an + * arbitrary hex digit + * + * Function modified from original at iproute2/lib/utils.c:get_guid() + * Original by Eli Cohen . + * iproute2 git commit d91fb3f4c7e4dba806541bdc90b1fb60a3581541 + */ +int rtnl_link_vf_str2guid(uint64_t *guid, const char *guid_s) { + unsigned long int tmp; + char *endptr; + int i; + + if (strlen(guid_s) != RTNL_VF_GUID_STR_LEN) + return -1; + + for (i = 0; i < 7; i++) { + if (guid_s[2 + i * 3] != ':') + return -1; + } + + *guid = 0; + for (i = 0; i < 8; i++) { + tmp = strtoul(guid_s + i * 3, &endptr, 16); + if (endptr != guid_s + i * 3 + 2) + return -1; + + if (tmp > 255) + return -1; + + *guid |= tmp << (56 - 8 * i); + } + + return 0; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/route/link/team.c b/libnl/lib/route/link/team.c new file mode 100644 index 0000000..0f1161b --- /dev/null +++ b/libnl/lib/route/link/team.c @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2015 Jonas Johansson + */ + +/** + * @ingroup link + * @defgroup team Team + * + * @details + * \b Link Type Name: "team" + * + * @route_doc{link_team, Team Documentation} + * @{ + */ + +#include "nl-default.h" + +#include +#include + +#include "link-api.h" + +/** + * Allocate link object of type team + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_team_alloc(void) +{ + struct rtnl_link *link; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if (rtnl_link_set_type(link, "team") < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Create a new kernel team device + * @arg sock netlink socket + * @arg name name of team device or NULL + * @arg opts team options (currently unused) + * + * Creates a new team device in the kernel. If no name is + * provided, the kernel will automatically pick a name of the + * form "type%d" (e.g. team0, vlan1, etc.) + * + * The \a opts argument is currently unused. In the future, it + * may be used to carry additional team options to be set + * when creating the team device. + * + * @note When letting the kernel assign a name, it will become + * difficult to retrieve the interface afterwards because + * you have to guess the name the kernel has chosen. It is + * therefore not recommended to not provide a device name. + * + * @see rtnl_link_team_enslave() + * @see rtnl_link_team_release() + * + * @return 0 on success or a negative error code + */ +int rtnl_link_team_add(struct nl_sock *sock, const char *name, + struct rtnl_link *opts) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_team_alloc())) + return -NLE_NOMEM; + + if (!name && opts) + name = rtnl_link_get_name(opts); + + if (name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sock, link, NLM_F_CREATE); + + rtnl_link_put(link); + + return err; +} + +static struct rtnl_link_info_ops team_info_ops = { + .io_name = "team", +}; + +static void _nl_init team_init(void) +{ + rtnl_link_register_info(&team_info_ops); +} + +static void _nl_exit team_exit(void) +{ + rtnl_link_unregister_info(&team_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/veth.c b/libnl/lib/route/link/veth.c new file mode 100644 index 0000000..be4b795 --- /dev/null +++ b/libnl/lib/route/link/veth.c @@ -0,0 +1,305 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +/** + * @ingroup link + * @defgroup veth VETH + * Virtual Ethernet + * + * @details + * \b Link Type Name: "veth" + * + * @route_doc{link_veth, VETH Documentation} + * + * @{ + */ + +#include "nl-default.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" + +static struct nla_policy veth_policy[VETH_INFO_MAX+1] = { + [VETH_INFO_PEER] = { .minlen = sizeof(struct ifinfomsg) }, +}; + +static int veth_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[VETH_INFO_MAX+1]; + struct nlattr *peer_tb[IFLA_MAX + 1]; + struct rtnl_link *peer = link->l_info; + int err; + + NL_DBG(3, "Parsing veth link info\n"); + + if ((err = nla_parse_nested(tb, VETH_INFO_MAX, data, veth_policy)) < 0) + goto errout; + + if (tb[VETH_INFO_PEER]) { + struct nlattr *nla_peer; + struct ifinfomsg *ifi; + + nla_peer = tb[VETH_INFO_PEER]; + ifi = nla_data(nla_peer); + + peer->l_family = ifi->ifi_family; + peer->l_arptype = ifi->ifi_type; + peer->l_index = ifi->ifi_index; + peer->l_flags = ifi->ifi_flags; + peer->l_change = ifi->ifi_change; + err = nla_parse(peer_tb, IFLA_MAX, (struct nlattr *) + ((char *) nla_data(nla_peer) + sizeof(struct ifinfomsg)), + nla_len(nla_peer) - sizeof(struct ifinfomsg), + rtln_link_policy); + if (err < 0) + goto errout; + + err = rtnl_link_info_parse(peer, peer_tb); + if (err < 0) + goto errout; + } + + err = 0; + +errout: + return err; +} + +static void veth_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ +} + +static void veth_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct rtnl_link *peer = link->l_info; + char *name; + name = rtnl_link_get_name(peer); + nl_dump(p, " peer "); + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", peer->l_index); +} + +static int veth_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct rtnl_link *dst_peer = NULL, *src_peer = src->l_info; + + /* we are calling nl_object_clone() recursively, this should + * happen only once */ + if (src_peer) { + src_peer->l_info = NULL; + dst_peer = (struct rtnl_link *)nl_object_clone(OBJ_CAST(src_peer)); + if (!dst_peer) + return -NLE_NOMEM; + src_peer->l_info = src; + dst_peer->l_info = dst; + } + dst->l_info = dst_peer; + return 0; +} + +static int veth_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct rtnl_link *peer = link->l_info; + struct ifinfomsg ifi; + struct nlattr *data, *info_peer; + + memset(&ifi, 0, sizeof ifi); + ifi.ifi_family = peer->l_family; + ifi.ifi_type = peer->l_arptype; + ifi.ifi_index = peer->l_index; + ifi.ifi_flags = peer->l_flags; + ifi.ifi_change = peer->l_change; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + return -NLE_MSGSIZE; + if (!(info_peer = nla_nest_start(msg, VETH_INFO_PEER))) + return -NLE_MSGSIZE; + if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0) + return -NLE_MSGSIZE; + rtnl_link_fill_info(msg, peer); + nla_nest_end(msg, info_peer); + nla_nest_end(msg, data); + + return 0; +} + +static int veth_alloc(struct rtnl_link *link) +{ + struct rtnl_link *peer; + int err; + + /* return early if we are in recursion */ + if (link->l_info) + return 0; + + if (!(peer = rtnl_link_alloc())) + return -NLE_NOMEM; + + /* We don't need to hold a reference here, as link and + * its peer should always be freed together. + */ + peer->l_info = link; + if ((err = rtnl_link_set_type(peer, "veth")) < 0) { + rtnl_link_put(peer); + return err; + } + + link->l_info = peer; + return 0; +} + +static void veth_free(struct rtnl_link *link) +{ + struct rtnl_link *peer = link->l_info; + if (peer) { + link->l_info = NULL; + /* avoid calling this recursively */ + peer->l_info = NULL; + rtnl_link_put(peer); + } + /* the caller should finally free link */ +} + +static struct rtnl_link_info_ops veth_info_ops = { + .io_name = "veth", + .io_parse = veth_parse, + .io_dump = { + [NL_DUMP_LINE] = veth_dump_line, + [NL_DUMP_DETAILS] = veth_dump_details, + }, + .io_alloc = veth_alloc, + .io_clone = veth_clone, + .io_put_attrs = veth_put_attrs, + .io_free = veth_free, +}; + +/** @cond SKIP */ + +#define IS_VETH_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &veth_info_ops) { \ + APPBUG("Link is not a veth link. set type \"veth\" first."); \ + return NULL; \ + } +/** @endcond */ + +/** + * @name VETH Object + * @{ + */ + +/** + * Allocate link object of type veth + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_veth_alloc(void) +{ + struct rtnl_link *link; + + if (!(link = rtnl_link_alloc())) + return NULL; + if (rtnl_link_set_type(link, "veth") < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Get the peer link of a veth link + * + * @return the peer link object. + */ +struct rtnl_link *rtnl_link_veth_get_peer(struct rtnl_link *link) +{ + IS_VETH_LINK_ASSERT(link); + nl_object_get(OBJ_CAST(link->l_info)); + return link->l_info; +} + +/** + * Release a veth link and its peer + * + */ +void rtnl_link_veth_release(struct rtnl_link *link) +{ + veth_free(link); + rtnl_link_put(link); +} + +/** + * Check if link is a veth link + * @arg link Link object + * + * @return True if link is a veth link, otherwise false is returned. + */ +int rtnl_link_is_veth(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "veth"); +} + +/** + * Create a new kernel veth device + * @arg sock netlink socket + * @arg name name of the veth device or NULL + * @arg peer_name name of its peer or NULL + * @arg pid pid of the process in the new netns + * + * Creates a new veth device pair in the kernel and move the peer + * to the network namespace where the process is. If no name is + * provided, the kernel will automatically pick a name of the + * form "veth%d" (e.g. veth0, veth1, etc.) + * + * @return 0 on success or a negative error code + */ +int rtnl_link_veth_add(struct nl_sock *sock, const char *name, + const char *peer_name, pid_t pid) +{ + struct rtnl_link *link, *peer; + int err = -NLE_NOMEM; + + if (!(link = rtnl_link_veth_alloc())) + return -NLE_NOMEM; + peer = link->l_info; + + if (name) + rtnl_link_set_name(link, name); + if (peer_name) + rtnl_link_set_name(peer, peer_name); + + rtnl_link_set_ns_pid(peer, pid); + err = rtnl_link_add(sock, link, NLM_F_CREATE | NLM_F_EXCL); + + rtnl_link_put(link); + return err; +} + +/** @} */ + +static void _nl_init veth_init(void) +{ + rtnl_link_register_info(&veth_info_ops); +} + +static void _nl_exit veth_exit(void) +{ + rtnl_link_unregister_info(&veth_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/vlan.c b/libnl/lib/route/link/vlan.c new file mode 100644 index 0000000..75842d8 --- /dev/null +++ b/libnl/lib/route/link/vlan.c @@ -0,0 +1,688 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +/** + * @ingroup link + * @defgroup vlan VLAN + * Virtual LAN link module + * + * @details + * \b Link Type Name: "vlan" + * + * @route_doc{link_vlan, VLAN Documentation} + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" + +/** @cond SKIP */ +#define VLAN_HAS_ID (1<<0) +#define VLAN_HAS_FLAGS (1<<1) +#define VLAN_HAS_INGRESS_QOS (1<<2) +#define VLAN_HAS_EGRESS_QOS (1<<3) +#define VLAN_HAS_PROTOCOL (1<<4) + +struct vlan_info +{ + uint16_t vi_vlan_id; + uint16_t vi_protocol; + unsigned int vi_ingress_qos_mask:(VLAN_PRIO_MAX+1); + uint32_t vi_flags; + uint32_t vi_flags_mask; + uint32_t vi_ingress_qos[VLAN_PRIO_MAX+1]; + uint32_t vi_negress; + uint32_t vi_egress_size; + struct vlan_map * vi_egress_qos; + uint32_t vi_mask; +}; + +/** @endcond */ + +static struct nla_policy vlan_policy[IFLA_VLAN_MAX+1] = { + [IFLA_VLAN_ID] = { .type = NLA_U16 }, + [IFLA_VLAN_FLAGS] = { .minlen = sizeof(struct ifla_vlan_flags) }, + [IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED }, + [IFLA_VLAN_EGRESS_QOS] = { .type = NLA_NESTED }, + [IFLA_VLAN_PROTOCOL] = { .type = NLA_U16 }, +}; + +static int vlan_alloc(struct rtnl_link *link) +{ + struct vlan_info *vi; + + if (link->l_info) { + vi = link->l_info; + free(vi->vi_egress_qos); + memset(link->l_info, 0, sizeof(*vi)); + } else { + if ((vi = calloc(1, sizeof(*vi))) == NULL) + return -NLE_NOMEM; + + link->l_info = vi; + } + + return 0; +} + +static int vlan_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_VLAN_MAX+1]; + struct vlan_info *vi; + int err; + + NL_DBG(3, "Parsing VLAN link info\n"); + + if ((err = nla_parse_nested(tb, IFLA_VLAN_MAX, data, vlan_policy)) < 0) + goto errout; + + if ((err = vlan_alloc(link)) < 0) + goto errout; + + vi = link->l_info; + + if (tb[IFLA_VLAN_ID]) { + vi->vi_vlan_id = nla_get_u16(tb[IFLA_VLAN_ID]); + vi->vi_mask |= VLAN_HAS_ID; + } + + if (tb[IFLA_VLAN_PROTOCOL]) { + vi->vi_protocol = nla_get_u16(tb[IFLA_VLAN_PROTOCOL]); + vi->vi_mask |= VLAN_HAS_PROTOCOL; + } + + if (tb[IFLA_VLAN_FLAGS]) { + struct ifla_vlan_flags flags; + nla_memcpy(&flags, tb[IFLA_VLAN_FLAGS], sizeof(flags)); + + vi->vi_flags = flags.flags; + vi->vi_mask |= VLAN_HAS_FLAGS; + } + + if (tb[IFLA_VLAN_INGRESS_QOS]) { + struct ifla_vlan_qos_mapping *map; + struct nlattr *nla; + int remaining; + + vi->vi_ingress_qos_mask = 0; + memset(vi->vi_ingress_qos, 0, sizeof(vi->vi_ingress_qos)); + + nla_for_each_nested(nla, tb[IFLA_VLAN_INGRESS_QOS], remaining) { + if (_nla_len(nla) < sizeof(*map)) + return -NLE_INVAL; + + map = nla_data(nla); + if (map->from > VLAN_PRIO_MAX) { + return -NLE_INVAL; + } + + /* Kernel will not explicitly serialize mappings with "to" zero + * (although they are implicitly set). + * + * Thus we only mark those as "set" which are explicitly sent. + * That is similar to what we do with the egress map and it preserves + * previous behavior before NL_CAPABILITY_RTNL_LINK_VLAN_INGRESS_MAP_CLEAR. + * + * It matters only when a received object is send back to kernel to modify + * the link. + */ + vi->vi_ingress_qos_mask |= (1 << map->from); + vi->vi_ingress_qos[map->from] = map->to; + } + + vi->vi_mask |= VLAN_HAS_INGRESS_QOS; + } + + if (tb[IFLA_VLAN_EGRESS_QOS]) { + struct ifla_vlan_qos_mapping *map; + struct nlattr *nla; + int remaining, i = 0; + + nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) { + if (_nla_len(nla) < sizeof(*map)) + return -NLE_INVAL; + i++; + } + + /* align to have a little reserve */ + vi->vi_egress_size = (i + 32) & ~31; + vi->vi_egress_qos = calloc(vi->vi_egress_size, sizeof(*vi->vi_egress_qos)); + if (vi->vi_egress_qos == NULL) + return -NLE_NOMEM; + + i = 0; + nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) { + map = nla_data(nla); + NL_DBG(4, "Assigning egress qos mapping %d\n", i); + vi->vi_egress_qos[i].vm_from = map->from; + vi->vi_egress_qos[i++].vm_to = map->to; + } + + vi->vi_negress = i; + vi->vi_mask |= VLAN_HAS_EGRESS_QOS; + } + + err = 0; +errout: + return err; +} + +static void vlan_free(struct rtnl_link *link) +{ + struct vlan_info *vi = link->l_info; + + if (vi) { + free(vi->vi_egress_qos); + vi->vi_egress_qos = NULL; + } + + free(vi); + link->l_info = NULL; +} + +static void vlan_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct vlan_info *vi = link->l_info; + + nl_dump(p, "vlan-id %d", vi->vi_vlan_id); +} + +static void vlan_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct vlan_info *vi = link->l_info; + int printed; + uint32_t i; + char buf[64]; + + rtnl_link_vlan_flags2str(vi->vi_flags, buf, sizeof(buf)); + nl_dump_line(p, " vlan-info id %d <%s>", vi->vi_vlan_id, buf); + + if (vi->vi_mask & VLAN_HAS_PROTOCOL) + nl_dump_line(p, " vlan protocol <%d>", ntohs(vi->vi_protocol)); + + nl_dump(p, "\n"); + + if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) { + nl_dump_line(p, + " ingress vlan prio -> qos/socket prio mapping:\n"); + for (i = 0, printed = 0; i <= VLAN_PRIO_MAX; i++) { + if (vi->vi_ingress_qos_mask & (1 << i)) { + if (printed == 0) + nl_dump_line(p, " "); + nl_dump(p, "%x -> %#08x, ", + i, vi->vi_ingress_qos[i]); + if (printed++ == 3) { + nl_dump(p, "\n"); + printed = 0; + } + } + } + + if (printed > 0 && printed != 4) + nl_dump(p, "\n"); + } + + if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) { + nl_dump_line(p, + " egress qos/socket prio -> vlan prio mapping:\n"); + for (i = 0, printed = 0; i < vi->vi_negress; i++) { + if (printed == 0) + nl_dump_line(p, " "); + nl_dump(p, "%#08x -> %x, ", + vi->vi_egress_qos[i].vm_from, + vi->vi_egress_qos[i].vm_to); + if (printed++ == 3) { + nl_dump(p, "\n"); + printed = 0; + } + } + + if (printed > 0 && printed != 4) + nl_dump(p, "\n"); + } +} + +static int vlan_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct vlan_info *vdst, *vsrc = src->l_info; + int err; + struct vlan_map *p = NULL; + + dst->l_info = NULL; + if ((err = rtnl_link_set_type(dst, "vlan")) < 0) + return err; + vdst = dst->l_info; + + if (vsrc->vi_negress) { + p = calloc(vsrc->vi_negress, + sizeof(struct vlan_map)); + if (!p) + return -NLE_NOMEM; + } + + *vdst = *vsrc; + + if (vsrc->vi_negress) { + vdst->vi_egress_size = vsrc->vi_negress; + vdst->vi_egress_qos = p; + memcpy(vdst->vi_egress_qos, vsrc->vi_egress_qos, + vsrc->vi_negress * sizeof(struct vlan_map)); + } + + return 0; +} + +static int vlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct vlan_info *vi = link->l_info; + struct nlattr *data; + int nest_count = 0; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + nest_count++; + + if (vi->vi_mask & VLAN_HAS_ID) + NLA_PUT_U16(msg, IFLA_VLAN_ID, vi->vi_vlan_id); + + if (vi->vi_mask & VLAN_HAS_PROTOCOL) + NLA_PUT_U16(msg, IFLA_VLAN_PROTOCOL, vi->vi_protocol); + + if (vi->vi_mask & VLAN_HAS_FLAGS) { + struct ifla_vlan_flags flags = { + .flags = vi->vi_flags, + .mask = vi->vi_flags_mask, + }; + + NLA_PUT(msg, IFLA_VLAN_FLAGS, sizeof(flags), &flags); + } + + if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) { + struct ifla_vlan_qos_mapping map; + struct nlattr *qos; + int i; + + if (!(qos = nla_nest_start(msg, IFLA_VLAN_INGRESS_QOS))) + goto nla_put_failure; + + nest_count++; + + for (i = 0; i <= VLAN_PRIO_MAX; i++) { + if (vi->vi_ingress_qos_mask & (1 << i)) { + map.from = i; + map.to = vi->vi_ingress_qos[i]; + + NLA_PUT(msg, i, sizeof(map), &map); + } + } + + nla_nest_end(msg, qos); + nest_count--; + } + + if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) { + struct ifla_vlan_qos_mapping map; + struct nlattr *qos; + uint32_t i; + + if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS))) + goto nla_put_failure; + + nest_count++; + + for (i = 0; i < vi->vi_negress; i++) { + map.from = vi->vi_egress_qos[i].vm_from; + map.to = vi->vi_egress_qos[i].vm_to; + + NLA_PUT(msg, i, sizeof(map), &map); + } + + nla_nest_end(msg, qos); + nest_count--; + } + + nla_nest_end(msg, data); + return 0; + +nla_put_failure: + for (; nest_count > 0; nest_count--) + nla_nest_cancel(msg, data); + return -NLE_MSGSIZE; +} + +static struct rtnl_link_info_ops vlan_info_ops = { + .io_name = "vlan", + .io_alloc = vlan_alloc, + .io_parse = vlan_parse, + .io_dump = { + [NL_DUMP_LINE] = vlan_dump_line, + [NL_DUMP_DETAILS] = vlan_dump_details, + }, + .io_clone = vlan_clone, + .io_put_attrs = vlan_put_attrs, + .io_free = vlan_free, +}; + +/** @cond SKIP */ +#define IS_VLAN_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &vlan_info_ops) { \ + APPBUG("Link is not a vlan link. set type \"vlan\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +/** + * @name VLAN Object + * @{ + */ + +/** + * Allocate link object of type VLAN + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_vlan_alloc(void) +{ + struct rtnl_link *link; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if (rtnl_link_set_type(link, "vlan") < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a VLAN link + * @arg link Link object + * + * @return True if link is a VLAN link, otherwise false is returned. + */ +int rtnl_link_is_vlan(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "vlan"); +} + +/** + * Set VLAN ID + * @arg link Link object + * @arg id VLAN identifier + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vlan_set_id(struct rtnl_link *link, uint16_t id) +{ + struct vlan_info *vi = link->l_info; + + IS_VLAN_LINK_ASSERT(link); + + vi->vi_vlan_id = id; + vi->vi_mask |= VLAN_HAS_ID; + + return 0; +} + +/** + * Get VLAN Id + * @arg link Link object + * + * @return VLAN id, 0 if not set or a negative error code. + */ +int rtnl_link_vlan_get_id(struct rtnl_link *link) +{ + struct vlan_info *vi = link->l_info; + + IS_VLAN_LINK_ASSERT(link); + + if (vi->vi_mask & VLAN_HAS_ID) + return vi->vi_vlan_id; + else + return 0; +} + +/** + * Set VLAN protocol + * @arg link Link object + * @arg protocol VLAN protocol in network byte order. + * Probably you want to set it to something like htons(ETH_P_8021Q). + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vlan_set_protocol(struct rtnl_link *link, uint16_t protocol) +{ + struct vlan_info *vi = link->l_info; + + IS_VLAN_LINK_ASSERT(link); + + vi->vi_protocol = protocol; + vi->vi_mask |= VLAN_HAS_PROTOCOL; + + return 0; +} + +/** + * Get VLAN protocol + * @arg link Link object + * + * @return VLAN protocol in network byte order like htons(ETH_P_8021Q), + * 0 if not set or a negative error code. + */ +int rtnl_link_vlan_get_protocol(struct rtnl_link *link) +{ + struct vlan_info *vi = link->l_info; + + IS_VLAN_LINK_ASSERT(link); + + if (vi->vi_mask & VLAN_HAS_PROTOCOL) + return vi->vi_protocol; + else + return 0; +} + +/** + * Set VLAN flags + * @arg link Link object + * @arg flags VLAN flags + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_vlan_set_flags(struct rtnl_link *link, unsigned int flags) +{ + struct vlan_info *vi = link->l_info; + + IS_VLAN_LINK_ASSERT(link); + + vi->vi_flags_mask |= flags; + vi->vi_flags |= flags; + vi->vi_mask |= VLAN_HAS_FLAGS; + + return 0; +} + +/** + * Unset VLAN flags + * @arg link Link object + * @arg flags VLAN flags + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_vlan_unset_flags(struct rtnl_link *link, unsigned int flags) +{ + struct vlan_info *vi = link->l_info; + + IS_VLAN_LINK_ASSERT(link); + + vi->vi_flags_mask |= flags; + vi->vi_flags &= ~flags; + vi->vi_mask |= VLAN_HAS_FLAGS; + + return 0; +} + +/** + * Get VLAN flags + * @arg link Link object + * + * @return VLAN flags, 0 if none set, or a negative error code. + */ +int rtnl_link_vlan_get_flags(struct rtnl_link *link) +{ + struct vlan_info *vi = link->l_info; + + IS_VLAN_LINK_ASSERT(link); + + return vi->vi_flags; +} + +/** @} */ + +/** + * @name Quality of Service + * @{ + */ + +int rtnl_link_vlan_set_ingress_map(struct rtnl_link *link, int from, + uint32_t to) +{ + struct vlan_info *vi = link->l_info; + + IS_VLAN_LINK_ASSERT(link); + + if (from < 0 || from > VLAN_PRIO_MAX) + return -NLE_INVAL; + + vi->vi_ingress_qos_mask |= (1 << from); + vi->vi_ingress_qos[from] = to; + vi->vi_mask |= VLAN_HAS_INGRESS_QOS; + + return 0; +} + +uint32_t *rtnl_link_vlan_get_ingress_map(struct rtnl_link *link) +{ + struct vlan_info *vi = link->l_info; + + if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) + return NULL; + + if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) + return vi->vi_ingress_qos; + else + return NULL; +} + +int rtnl_link_vlan_set_egress_map(struct rtnl_link *link, uint32_t from, int to) +{ + struct vlan_info *vi = link->l_info; + + if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) + return -NLE_OPNOTSUPP; + + if (to < 0 || to > VLAN_PRIO_MAX) + return -NLE_INVAL; + + if (vi->vi_negress >= vi->vi_egress_size) { + uint32_t new_size = vi->vi_egress_size + 1 + vi->vi_egress_size / 2; + size_t bytes; + void *ptr; + + if (new_size < vi->vi_egress_size) + return -NLE_NOMEM; + bytes = (size_t) new_size * sizeof(struct vlan_map); + if (bytes / sizeof (struct vlan_map) != new_size) + return -NLE_NOMEM; + ptr = realloc(vi->vi_egress_qos, bytes); + if (!ptr) + return -NLE_NOMEM; + + vi->vi_egress_qos = ptr; + vi->vi_egress_size = new_size; + } + + vi->vi_egress_qos[vi->vi_negress].vm_from = from; + vi->vi_egress_qos[vi->vi_negress].vm_to = to; + vi->vi_negress++; + vi->vi_mask |= VLAN_HAS_EGRESS_QOS; + + return 0; +} + +struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *link, + int *negress) +{ + struct vlan_info *vi = link->l_info; + + if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) + return NULL; + + if (negress == NULL) + return NULL; + + if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) { + *negress = vi->vi_negress; + return vi->vi_egress_qos; + } else { + *negress = 0; + return NULL; + } +} + +/** @} */ + +static const struct trans_tbl vlan_flags[] = { + __ADD(VLAN_FLAG_REORDER_HDR, reorder_hdr), + __ADD(VLAN_FLAG_GVRP, gvrp), + __ADD(VLAN_FLAG_LOOSE_BINDING, loose_binding), + __ADD(VLAN_FLAG_MVRP, mvrp), + __ADD(VLAN_FLAG_BRIDGE_BINDING, bridge_binding), +}; + +/** + * @name Flag Translation + * @{ + */ + +char *rtnl_link_vlan_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, vlan_flags, ARRAY_SIZE(vlan_flags)); +} + +int rtnl_link_vlan_str2flags(const char *name) +{ + return __str2flags(name, vlan_flags, ARRAY_SIZE(vlan_flags)); +} + +/** @} */ + + +static void _nl_init vlan_init(void) +{ + rtnl_link_register_info(&vlan_info_ops); +} + +static void _nl_exit vlan_exit(void) +{ + rtnl_link_unregister_info(&vlan_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/vrf.c b/libnl/lib/route/link/vrf.c new file mode 100644 index 0000000..17cb3bd --- /dev/null +++ b/libnl/lib/route/link/vrf.c @@ -0,0 +1,260 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2015 Cumulus Networks. All rights reserved. + * Copyright (c) 2015 David Ahern + */ + +/** + * @ingroup link + * @defgroup vrf VRF + * Virtual Routing and Forwarding link module + * + * @details + * \b Link Type Name: "vrf" + * + * @route_doc{link_vrf, VRF Documentation} + * + * @{ + */ + +#include "nl-default.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" + +#define VRF_TABLE_ID_MAX RT_TABLE_MAX + +/** @cond SKIP */ +#define VRF_HAS_TABLE_ID (1<<0) + +struct vrf_info { + uint32_t table_id; + uint32_t vi_mask; +}; + +/** @endcond */ + +static struct nla_policy vrf_policy[IFLA_VRF_MAX + 1] = { + [IFLA_VRF_TABLE] = { .type = NLA_U32 }, +}; + +static int vrf_alloc(struct rtnl_link *link) +{ + struct vrf_info *vi; + + if (link->l_info) { + memset(link->l_info, 0, sizeof (*vi)); + return 0; + } + + if ((vi = calloc(1, sizeof(*vi))) == NULL) + return -NLE_NOMEM; + + link->l_info = vi; + + return 0; +} + +static int vrf_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_VRF_MAX+1]; + struct vrf_info *vi; + int err; + + NL_DBG(3, "Parsing VRF link info"); + + if ((err = nla_parse_nested(tb, IFLA_VRF_MAX, data, vrf_policy)) < 0) + goto errout; + + if ((err = vrf_alloc(link)) < 0) + goto errout; + + vi = link->l_info; + + if (tb[IFLA_VRF_TABLE]) { + vi->table_id = nla_get_u32(tb[IFLA_VRF_TABLE]); + vi->vi_mask |= VRF_HAS_TABLE_ID; + } + + err = 0; + +errout: + return err; +} + +static void vrf_free(struct rtnl_link *link) +{ + free(link->l_info); + link->l_info = NULL; +} + +static int vrf_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct vrf_info *vdst, *vsrc = src->l_info; + int err; + + dst->l_info = NULL; + if ((err = rtnl_link_set_type(dst, "vrf")) < 0) + return err; + vdst = dst->l_info; + + BUG_ON(!vdst || !vsrc); + + memcpy(vdst, vsrc, sizeof(struct vrf_info)); + + return 0; +} + +static int vrf_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct vrf_info *vi = link->l_info; + struct nlattr *data; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + return -NLE_NOMEM; + + if (vi->vi_mask & VRF_HAS_TABLE_ID) { + NLA_PUT_U32(msg, IFLA_VRF_TABLE, vi->table_id); + } + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static void vrf_dump(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct vrf_info *vi = link->l_info; + + if (vi->vi_mask & VRF_HAS_TABLE_ID) { + nl_dump(p, "table-id %u", vi->table_id); + } +} + +static struct rtnl_link_info_ops vrf_info_ops = { + .io_name = "vrf", + .io_alloc = vrf_alloc, + .io_parse = vrf_parse, + .io_dump = { + [NL_DUMP_LINE] = vrf_dump, + [NL_DUMP_DETAILS] = vrf_dump, + }, + .io_clone = vrf_clone, + .io_put_attrs = vrf_put_attrs, + .io_free = vrf_free, +}; + +/** @cond SKIP */ +#define IS_VRF_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &vrf_info_ops) { \ + APPBUG("Link is not a VRF link. set type \"vrf\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +/** + * @name VRF Object + * @{ + */ + +/** + * Allocate link object of type VRF + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_vrf_alloc(void) +{ + struct rtnl_link *link; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if (rtnl_link_set_type(link, "vrf") < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a VRF link + * @arg link Link object + * + * @return True if link is a VRF link, otherwise false is returned. + */ +int rtnl_link_is_vrf(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "vrf"); +} + +/** + * Get VRF table id + * @arg link Link object + * @arg id Pointer to store table identifier + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vrf_get_tableid(struct rtnl_link *link, uint32_t *id) +{ + struct vrf_info *vi = link->l_info; + + IS_VRF_LINK_ASSERT(link); + if(!id) + return -NLE_INVAL; + + if (vi->vi_mask & VRF_HAS_TABLE_ID) + *id = vi->table_id; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set VRF table id + * @arg link Link object + * @arg id Table identifier associated with VRF link + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vrf_set_tableid(struct rtnl_link *link, uint32_t id) +{ + struct vrf_info *vi = link->l_info; + + IS_VRF_LINK_ASSERT(link); + + _NL_STATIC_ASSERT(VRF_TABLE_ID_MAX == UINT32_MAX); + + vi->table_id = id; + vi->vi_mask |= VRF_HAS_TABLE_ID; + + return 0; +} + +/** @} */ + +static void _nl_init vrf_init(void) +{ + rtnl_link_register_info(&vrf_info_ops); +} + +static void _nl_exit vrf_exit(void) +{ + rtnl_link_unregister_info(&vrf_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/vxlan.c b/libnl/lib/route/link/vxlan.c new file mode 100644 index 0000000..0603bf5 --- /dev/null +++ b/libnl/lib/route/link/vxlan.c @@ -0,0 +1,1788 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Yasunobu Chiba + */ + +/** + * @ingroup link + * @defgroup vxlan VXLAN + * Virtual eXtensible Local Area Network link module + * + * @details + * \b Link Type Name: "vxlan" + * + * @route_doc{link_vxlan, VXLAN Documentation} + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" + +/** @cond SKIP */ +#define VXLAN_ATTR_ID (1<<0) +#define VXLAN_ATTR_GROUP (1<<1) +#define VXLAN_ATTR_LINK (1<<2) +#define VXLAN_ATTR_LOCAL (1<<3) +#define VXLAN_ATTR_TTL (1<<4) +#define VXLAN_ATTR_TOS (1<<5) +#define VXLAN_ATTR_LEARNING (1<<6) +#define VXLAN_ATTR_AGEING (1<<7) +#define VXLAN_ATTR_LIMIT (1<<8) +#define VXLAN_ATTR_PORT_RANGE (1<<9) +#define VXLAN_ATTR_PROXY (1<<10) +#define VXLAN_ATTR_RSC (1<<11) +#define VXLAN_ATTR_L2MISS (1<<12) +#define VXLAN_ATTR_L3MISS (1<<13) +#define VXLAN_ATTR_GROUP6 (1<<14) +#define VXLAN_ATTR_LOCAL6 (1<<15) +#define VXLAN_ATTR_PORT (1<<16) +#define VXLAN_ATTR_UDP_CSUM (1<<17) +#define VXLAN_ATTR_UDP_ZERO_CSUM6_TX (1<<18) +#define VXLAN_ATTR_UDP_ZERO_CSUM6_RX (1<<19) +#define VXLAN_ATTR_REMCSUM_TX (1<<20) +#define VXLAN_ATTR_REMCSUM_RX (1<<21) +#define VXLAN_ATTR_COLLECT_METADATA (1<<22) +#define VXLAN_ATTR_LABEL (1<<23) +#define VXLAN_ATTR_FLAGS (1<<24) + +struct vxlan_info +{ + uint32_t vxi_id; + uint32_t vxi_group; + struct in6_addr vxi_group6; + uint32_t vxi_link; + uint32_t vxi_local; + struct in6_addr vxi_local6; + uint8_t vxi_ttl; + uint8_t vxi_tos; + uint8_t vxi_learning; + uint8_t vxi_flags; + uint32_t vxi_ageing; + uint32_t vxi_limit; + struct ifla_vxlan_port_range vxi_port_range; + uint8_t vxi_proxy; + uint8_t vxi_rsc; + uint8_t vxi_l2miss; + uint8_t vxi_l3miss; + uint16_t vxi_port; + uint8_t vxi_udp_csum; + uint8_t vxi_udp_zero_csum6_tx; + uint8_t vxi_udp_zero_csum6_rx; + uint8_t vxi_remcsum_tx; + uint8_t vxi_remcsum_rx; + uint8_t vxi_collect_metadata; + uint32_t vxi_label; + uint32_t ce_mask; +}; + +/** @endcond */ + +static struct nla_policy vxlan_policy[IFLA_VXLAN_MAX+1] = { + [IFLA_VXLAN_ID] = { .type = NLA_U32 }, + [IFLA_VXLAN_GROUP] = { .minlen = sizeof(uint32_t) }, + [IFLA_VXLAN_GROUP6] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_VXLAN_LINK] = { .type = NLA_U32 }, + [IFLA_VXLAN_LOCAL] = { .minlen = sizeof(uint32_t) }, + [IFLA_VXLAN_LOCAL6] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_VXLAN_TTL] = { .type = NLA_U8 }, + [IFLA_VXLAN_TOS] = { .type = NLA_U8 }, + [IFLA_VXLAN_LABEL] = { .type = NLA_U32 }, + [IFLA_VXLAN_LEARNING] = { .type = NLA_U8 }, + [IFLA_VXLAN_AGEING] = { .type = NLA_U32 }, + [IFLA_VXLAN_LIMIT] = { .type = NLA_U32 }, + [IFLA_VXLAN_PORT_RANGE] = { .minlen = sizeof(struct ifla_vxlan_port_range) }, + [IFLA_VXLAN_PROXY] = { .type = NLA_U8 }, + [IFLA_VXLAN_RSC] = { .type = NLA_U8 }, + [IFLA_VXLAN_L2MISS] = { .type = NLA_U8 }, + [IFLA_VXLAN_L3MISS] = { .type = NLA_U8 }, + [IFLA_VXLAN_COLLECT_METADATA] = { .type = NLA_U8 }, + [IFLA_VXLAN_PORT] = { .type = NLA_U16 }, + [IFLA_VXLAN_UDP_CSUM] = { .type = NLA_U8 }, + [IFLA_VXLAN_UDP_ZERO_CSUM6_TX] = { .type = NLA_U8 }, + [IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = { .type = NLA_U8 }, + [IFLA_VXLAN_REMCSUM_TX] = { .type = NLA_U8 }, + [IFLA_VXLAN_REMCSUM_RX] = { .type = NLA_U8 }, + [IFLA_VXLAN_GBP] = { .type = NLA_FLAG, }, + [IFLA_VXLAN_GPE] = { .type = NLA_FLAG, }, + [IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NLA_FLAG }, +}; + +static int vxlan_alloc(struct rtnl_link *link) +{ + struct vxlan_info *vxi; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*vxi)); + else { + if ((vxi = calloc(1, sizeof(*vxi))) == NULL) + return -NLE_NOMEM; + + link->l_info = vxi; + } + + return 0; +} + +static int vxlan_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_VXLAN_MAX+1]; + struct vxlan_info *vxi; + int err; + + NL_DBG(3, "Parsing VXLAN link info\n"); + + if ((err = nla_parse_nested(tb, IFLA_VXLAN_MAX, data, vxlan_policy)) < 0) + goto errout; + + if ((err = vxlan_alloc(link)) < 0) + goto errout; + + vxi = link->l_info; + + if (tb[IFLA_VXLAN_ID]) { + vxi->vxi_id = nla_get_u32(tb[IFLA_VXLAN_ID]); + vxi->ce_mask |= VXLAN_ATTR_ID; + } + + if (tb[IFLA_VXLAN_GROUP6]) { + nla_memcpy(&vxi->vxi_group6, tb[IFLA_VXLAN_GROUP6], + sizeof(vxi->vxi_group6)); + vxi->ce_mask |= VXLAN_ATTR_GROUP6; + } + + if (tb[IFLA_VXLAN_GROUP]) { + nla_memcpy(&vxi->vxi_group, tb[IFLA_VXLAN_GROUP], + sizeof(vxi->vxi_group)); + vxi->ce_mask |= VXLAN_ATTR_GROUP; + vxi->ce_mask &= ~VXLAN_ATTR_GROUP6; + } + + if (tb[IFLA_VXLAN_LINK]) { + vxi->vxi_link = nla_get_u32(tb[IFLA_VXLAN_LINK]); + vxi->ce_mask |= VXLAN_ATTR_LINK; + } + + if (tb[IFLA_VXLAN_LOCAL6]) { + nla_memcpy(&vxi->vxi_local6, tb[IFLA_VXLAN_LOCAL6], + sizeof(vxi->vxi_local6)); + vxi->ce_mask |= VXLAN_ATTR_LOCAL6; + } + + if (tb[IFLA_VXLAN_LOCAL]) { + nla_memcpy(&vxi->vxi_local, tb[IFLA_VXLAN_LOCAL], + sizeof(vxi->vxi_local)); + vxi->ce_mask |= VXLAN_ATTR_LOCAL; + vxi->ce_mask &= ~VXLAN_ATTR_LOCAL6; + } + + if (tb[IFLA_VXLAN_TTL]) { + vxi->vxi_ttl = nla_get_u8(tb[IFLA_VXLAN_TTL]); + vxi->ce_mask |= VXLAN_ATTR_TTL; + } + + if (tb[IFLA_VXLAN_TOS]) { + vxi->vxi_tos = nla_get_u8(tb[IFLA_VXLAN_TOS]); + vxi->ce_mask |= VXLAN_ATTR_TOS; + } + + if (tb[IFLA_VXLAN_LEARNING]) { + vxi->vxi_learning = nla_get_u8(tb[IFLA_VXLAN_LEARNING]); + vxi->ce_mask |= VXLAN_ATTR_LEARNING; + } + + if (tb[IFLA_VXLAN_AGEING]) { + vxi->vxi_ageing = nla_get_u32(tb[IFLA_VXLAN_AGEING]); + vxi->ce_mask |= VXLAN_ATTR_AGEING; + } + + if (tb[IFLA_VXLAN_LIMIT]) { + vxi->vxi_limit = nla_get_u32(tb[IFLA_VXLAN_LIMIT]); + vxi->ce_mask |= VXLAN_ATTR_LIMIT; + } + + if (tb[IFLA_VXLAN_PORT_RANGE]) { + nla_memcpy(&vxi->vxi_port_range, tb[IFLA_VXLAN_PORT_RANGE], + sizeof(vxi->vxi_port_range)); + vxi->ce_mask |= VXLAN_ATTR_PORT_RANGE; + } + + if (tb[IFLA_VXLAN_PROXY]) { + vxi->vxi_proxy = nla_get_u8(tb[IFLA_VXLAN_PROXY]); + vxi->ce_mask |= VXLAN_ATTR_PROXY; + } + + if (tb[IFLA_VXLAN_RSC]) { + vxi->vxi_rsc = nla_get_u8(tb[IFLA_VXLAN_RSC]); + vxi->ce_mask |= VXLAN_ATTR_RSC; + } + + if (tb[IFLA_VXLAN_L2MISS]) { + vxi->vxi_l2miss = nla_get_u8(tb[IFLA_VXLAN_L2MISS]); + vxi->ce_mask |= VXLAN_ATTR_L2MISS; + } + + if (tb[IFLA_VXLAN_L3MISS]) { + vxi->vxi_l3miss = nla_get_u8(tb[IFLA_VXLAN_L3MISS]); + vxi->ce_mask |= VXLAN_ATTR_L3MISS; + } + + if (tb[IFLA_VXLAN_PORT]) { + vxi->vxi_port = nla_get_u16(tb[IFLA_VXLAN_PORT]); + vxi->ce_mask |= VXLAN_ATTR_PORT; + } + + if (tb[IFLA_VXLAN_UDP_CSUM]) { + vxi->vxi_udp_csum = nla_get_u8(tb[IFLA_VXLAN_UDP_CSUM]); + vxi->ce_mask |= VXLAN_ATTR_UDP_CSUM; + } + + if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]) { + vxi->vxi_udp_zero_csum6_tx = nla_get_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]); + vxi->ce_mask |= VXLAN_ATTR_UDP_ZERO_CSUM6_TX; + } + + if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]) { + vxi->vxi_udp_zero_csum6_rx = nla_get_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]); + vxi->ce_mask |= VXLAN_ATTR_UDP_ZERO_CSUM6_RX; + } + + if (tb[IFLA_VXLAN_REMCSUM_TX]) { + vxi->vxi_remcsum_tx = nla_get_u8(tb[IFLA_VXLAN_REMCSUM_TX]); + vxi->ce_mask |= VXLAN_ATTR_REMCSUM_TX; + } + + if (tb[IFLA_VXLAN_REMCSUM_RX]) { + vxi->vxi_remcsum_rx = nla_get_u8(tb[IFLA_VXLAN_REMCSUM_RX]); + vxi->ce_mask |= VXLAN_ATTR_REMCSUM_RX; + } + + if (tb[IFLA_VXLAN_GBP]) + vxi->vxi_flags |= RTNL_LINK_VXLAN_F_GBP; + + if (tb[IFLA_VXLAN_REMCSUM_NOPARTIAL]) + vxi->vxi_flags |= RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL; + + if (tb[IFLA_VXLAN_COLLECT_METADATA]) { + vxi->vxi_collect_metadata = nla_get_u8(tb[IFLA_VXLAN_COLLECT_METADATA]); + vxi->ce_mask |= VXLAN_ATTR_COLLECT_METADATA; + } + + if (tb[IFLA_VXLAN_LABEL]) { + vxi->vxi_label = nla_get_u32(tb[IFLA_VXLAN_LABEL]); + vxi->ce_mask |= VXLAN_ATTR_LABEL; + } + + if (tb[IFLA_VXLAN_GPE]) + vxi->vxi_flags |= RTNL_LINK_VXLAN_F_GPE; + + err = 0; + +errout: + return err; +} + +static void vxlan_free(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + free(vxi); + link->l_info = NULL; +} + +static void vxlan_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct vxlan_info *vxi = link->l_info; + + nl_dump(p, "vxlan-id %u", vxi->vxi_id); +} + +static void vxlan_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct vxlan_info *vxi = link->l_info; + char *name, addr[INET6_ADDRSTRLEN]; + struct rtnl_link *parent; + + nl_dump_line(p, " vxlan-id %u\n", vxi->vxi_id); + + if (vxi->ce_mask & VXLAN_ATTR_GROUP) { + nl_dump(p, " group "); + nl_dump_line(p, "%s\n", + _nl_inet_ntop(AF_INET, &vxi->vxi_group, addr)); + } else if (vxi->ce_mask & VXLAN_ATTR_GROUP6) { + nl_dump(p, " group "); + nl_dump_line(p, "%s\n", + _nl_inet_ntop(AF_INET6, &vxi->vxi_group6, addr)); + } + + if (vxi->ce_mask & VXLAN_ATTR_LINK) { + nl_dump(p, " link "); + + name = NULL; + parent = link_lookup(link->ce_cache, vxi->vxi_link); + if (parent) + name = rtnl_link_get_name(parent); + + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", vxi->vxi_link); + } + + if (vxi->ce_mask & VXLAN_ATTR_LOCAL) { + nl_dump(p, " local "); + nl_dump_line(p, "%s\n", + _nl_inet_ntop(AF_INET, &vxi->vxi_local, addr)); + } else if (vxi->ce_mask & VXLAN_ATTR_LOCAL6) { + nl_dump(p, " local "); + nl_dump_line(p, "%s\n", + _nl_inet_ntop(AF_INET6, &vxi->vxi_local6, addr)); + } + + if (vxi->ce_mask & VXLAN_ATTR_TTL) { + nl_dump(p, " ttl "); + if(vxi->vxi_ttl) + nl_dump_line(p, "%u\n", vxi->vxi_ttl); + else + nl_dump_line(p, "inherit\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_TOS) { + nl_dump(p, " tos "); + if (vxi->vxi_tos == 1) + nl_dump_line(p, "inherit\n"); + else + nl_dump_line(p, "%#x\n", vxi->vxi_tos); + } + + if (vxi->ce_mask & VXLAN_ATTR_LEARNING) { + nl_dump(p, " learning "); + if (vxi->vxi_learning) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_learning); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_AGEING) { + nl_dump(p, " ageing "); + if (vxi->vxi_ageing) + nl_dump_line(p, "%u seconds\n", vxi->vxi_ageing); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_LIMIT) { + nl_dump(p, " limit "); + if (vxi->vxi_limit) + nl_dump_line(p, "%u\n", vxi->vxi_limit); + else + nl_dump_line(p, "unlimited\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_PORT_RANGE) + nl_dump_line(p, " port range %u - %u\n", + ntohs(vxi->vxi_port_range.low), + ntohs(vxi->vxi_port_range.high)); + + if (vxi->ce_mask & VXLAN_ATTR_PROXY) { + nl_dump(p, " proxy "); + if (vxi->vxi_proxy) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_proxy); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_RSC) { + nl_dump(p, " rsc "); + if (vxi->vxi_rsc) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_rsc); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_L2MISS) { + nl_dump(p, " l2miss "); + if (vxi->vxi_l2miss) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_l2miss); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_L3MISS) { + nl_dump(p, " l3miss "); + if (vxi->vxi_l3miss) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_l3miss); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_PORT) { + nl_dump(p, " port "); + nl_dump_line(p, "%u\n", ntohs(vxi->vxi_port)); + } + + if (vxi->ce_mask & VXLAN_ATTR_UDP_CSUM) { + nl_dump(p, " UDP checksums "); + if (vxi->vxi_udp_csum) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_udp_csum); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_UDP_ZERO_CSUM6_TX) { + nl_dump(p, " udp-zero-csum6-tx "); + if (vxi->vxi_udp_zero_csum6_tx) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_udp_zero_csum6_tx); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_UDP_ZERO_CSUM6_RX) { + nl_dump(p, " udp-zero-csum6-rx "); + if (vxi->vxi_udp_zero_csum6_rx) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_udp_zero_csum6_rx); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_REMCSUM_TX) { + nl_dump(p, " remcsum-tx "); + if (vxi->vxi_remcsum_tx) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_remcsum_tx); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_REMCSUM_RX) { + nl_dump(p, " remcsum-rx "); + if (vxi->vxi_remcsum_rx) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_remcsum_rx); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->vxi_flags & RTNL_LINK_VXLAN_F_GBP) + nl_dump(p, " gbp\n"); + + if (vxi->vxi_flags & RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL) + nl_dump(p, " rncsum-nopartial\n"); + + if (vxi->ce_mask & VXLAN_ATTR_COLLECT_METADATA) { + nl_dump(p, " remcsum-rx "); + if (vxi->vxi_collect_metadata) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_collect_metadata); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_LABEL) { + nl_dump(p, " label "); + nl_dump_line(p, "%u\n", ntohl(vxi->vxi_label)); + } + + if (vxi->vxi_flags & RTNL_LINK_VXLAN_F_GPE) + nl_dump(p, " gpe\n"); +} + +static int vxlan_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct vxlan_info *vdst, *vsrc = src->l_info; + int err; + + dst->l_info = NULL; + if ((err = rtnl_link_set_type(dst, "vxlan")) < 0) + return err; + vdst = dst->l_info; + + if (!vdst || !vsrc) + return -NLE_NOMEM; + + memcpy(vdst, vsrc, sizeof(struct vxlan_info)); + + return 0; +} + +static int vxlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + struct nlattr *data; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + return -NLE_MSGSIZE; + + if (vxi->ce_mask & VXLAN_ATTR_ID) + NLA_PUT_U32(msg, IFLA_VXLAN_ID, vxi->vxi_id); + + if (vxi->ce_mask & VXLAN_ATTR_GROUP) + NLA_PUT(msg, IFLA_VXLAN_GROUP, sizeof(vxi->vxi_group), &vxi->vxi_group); + + if (vxi->ce_mask & VXLAN_ATTR_GROUP6) + NLA_PUT(msg, IFLA_VXLAN_GROUP6, sizeof(vxi->vxi_group6), &vxi->vxi_group6); + + if (vxi->ce_mask & VXLAN_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_VXLAN_LINK, vxi->vxi_link); + + if (vxi->ce_mask & VXLAN_ATTR_LOCAL) + NLA_PUT(msg, IFLA_VXLAN_LOCAL, sizeof(vxi->vxi_local), &vxi->vxi_local); + + if (vxi->ce_mask & VXLAN_ATTR_LOCAL6) + NLA_PUT(msg, IFLA_VXLAN_LOCAL6, sizeof(vxi->vxi_local6), &vxi->vxi_local6); + + if (vxi->ce_mask & VXLAN_ATTR_TTL) + NLA_PUT_U8(msg, IFLA_VXLAN_TTL, vxi->vxi_ttl); + + if (vxi->ce_mask & VXLAN_ATTR_TOS) + NLA_PUT_U8(msg, IFLA_VXLAN_TOS, vxi->vxi_tos); + + if (vxi->ce_mask & VXLAN_ATTR_LEARNING) + NLA_PUT_U8(msg, IFLA_VXLAN_LEARNING, vxi->vxi_learning); + + if (vxi->ce_mask & VXLAN_ATTR_AGEING) + NLA_PUT_U32(msg, IFLA_VXLAN_AGEING, vxi->vxi_ageing); + + if (vxi->ce_mask & VXLAN_ATTR_LIMIT) + NLA_PUT_U32(msg, IFLA_VXLAN_LIMIT, vxi->vxi_limit); + + if (vxi->ce_mask & VXLAN_ATTR_PORT_RANGE) + NLA_PUT(msg, IFLA_VXLAN_PORT_RANGE, sizeof(vxi->vxi_port_range), + &vxi->vxi_port_range); + + if (vxi->ce_mask & VXLAN_ATTR_PROXY) + NLA_PUT_U8(msg, IFLA_VXLAN_PROXY, vxi->vxi_proxy); + + if (vxi->ce_mask & VXLAN_ATTR_RSC) + NLA_PUT_U8(msg, IFLA_VXLAN_RSC, vxi->vxi_rsc); + + if (vxi->ce_mask & VXLAN_ATTR_L2MISS) + NLA_PUT_U8(msg, IFLA_VXLAN_L2MISS, vxi->vxi_l2miss); + + if (vxi->ce_mask & VXLAN_ATTR_L3MISS) + NLA_PUT_U8(msg, IFLA_VXLAN_L3MISS, vxi->vxi_l3miss); + + if (vxi->ce_mask & VXLAN_ATTR_PORT) + NLA_PUT_U32(msg, IFLA_VXLAN_PORT, vxi->vxi_port); + + if (vxi->ce_mask & VXLAN_ATTR_UDP_CSUM) + NLA_PUT_U8(msg, IFLA_VXLAN_UDP_CSUM, vxi->vxi_udp_csum); + + if (vxi->ce_mask & VXLAN_ATTR_UDP_ZERO_CSUM6_TX) + NLA_PUT_U8(msg, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, vxi->vxi_udp_zero_csum6_tx); + + if (vxi->ce_mask & VXLAN_ATTR_UDP_ZERO_CSUM6_RX) + NLA_PUT_U8(msg, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, vxi->vxi_udp_zero_csum6_rx); + + if (vxi->ce_mask & VXLAN_ATTR_REMCSUM_TX) + NLA_PUT_U8(msg, IFLA_VXLAN_REMCSUM_TX, vxi->vxi_remcsum_tx); + + if (vxi->ce_mask & VXLAN_ATTR_REMCSUM_RX) + NLA_PUT_U8(msg, IFLA_VXLAN_REMCSUM_RX, vxi->vxi_remcsum_rx); + + if (vxi->vxi_flags & RTNL_LINK_VXLAN_F_GBP) + NLA_PUT_FLAG(msg, IFLA_VXLAN_GBP); + + if (vxi->vxi_flags & RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL) + NLA_PUT_FLAG(msg, IFLA_VXLAN_REMCSUM_NOPARTIAL); + + if (vxi->ce_mask & VXLAN_ATTR_COLLECT_METADATA) + NLA_PUT_U8(msg, IFLA_VXLAN_COLLECT_METADATA, vxi->vxi_collect_metadata); + + if (vxi->ce_mask & VXLAN_ATTR_LABEL) + NLA_PUT_U32(msg, IFLA_VXLAN_LABEL, vxi->vxi_label); + + if (vxi->vxi_flags & RTNL_LINK_VXLAN_F_GPE) + NLA_PUT_FLAG(msg, IFLA_VXLAN_GPE); + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static int vxlan_compare(struct rtnl_link *link_a, struct rtnl_link *link_b, + int flags) +{ + struct vxlan_info *a = link_a->l_info; + struct vxlan_info *b = link_b->l_info; + int diff = 0; + uint32_t attrs = flags & LOOSE_COMPARISON ? b->ce_mask : + ~((uint32_t)0u); + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(VXLAN_ATTR_ID, a->vxi_id != b->vxi_id); + diff |= _DIFF(VXLAN_ATTR_GROUP, a->vxi_group != b->vxi_group); + diff |= _DIFF(VXLAN_ATTR_LINK, a->vxi_link != b->vxi_link); + diff |= _DIFF(VXLAN_ATTR_LOCAL, a->vxi_local != b->vxi_local); + diff |= _DIFF(VXLAN_ATTR_TOS, a->vxi_tos != b->vxi_tos); + diff |= _DIFF(VXLAN_ATTR_TTL, a->vxi_ttl != b->vxi_ttl); + diff |= _DIFF(VXLAN_ATTR_LEARNING, a->vxi_learning != b->vxi_learning); + diff |= _DIFF(VXLAN_ATTR_AGEING, a->vxi_ageing != b->vxi_ageing); + diff |= _DIFF(VXLAN_ATTR_LIMIT, a->vxi_limit != b->vxi_limit); + diff |= _DIFF(VXLAN_ATTR_PORT_RANGE, + a->vxi_port_range.low != b->vxi_port_range.low); + diff |= _DIFF(VXLAN_ATTR_PORT_RANGE, + a->vxi_port_range.high != b->vxi_port_range.high); + diff |= _DIFF(VXLAN_ATTR_PROXY, a->vxi_proxy != b->vxi_proxy); + diff |= _DIFF(VXLAN_ATTR_RSC, a->vxi_proxy != b->vxi_proxy); + diff |= _DIFF(VXLAN_ATTR_L2MISS, a->vxi_proxy != b->vxi_proxy); + diff |= _DIFF(VXLAN_ATTR_L3MISS, a->vxi_proxy != b->vxi_proxy); + diff |= _DIFF(VXLAN_ATTR_PORT, a->vxi_port != b->vxi_port); + diff |= _DIFF(VXLAN_ATTR_GROUP6, memcmp(&a->vxi_group6, &b->vxi_group6, + sizeof(a->vxi_group6)) != 0); + diff |= _DIFF(VXLAN_ATTR_LOCAL6, memcmp(&a->vxi_local6, &b->vxi_local6, + sizeof(a->vxi_local6)) != 0); + diff |= _DIFF(VXLAN_ATTR_UDP_CSUM, a->vxi_proxy != b->vxi_proxy); + diff |= _DIFF(VXLAN_ATTR_UDP_ZERO_CSUM6_TX, + a->vxi_proxy != b->vxi_proxy); + diff |= _DIFF(VXLAN_ATTR_UDP_ZERO_CSUM6_RX, + a->vxi_proxy != b->vxi_proxy); + diff |= _DIFF(VXLAN_ATTR_REMCSUM_TX, a->vxi_proxy != b->vxi_proxy); + diff |= _DIFF(VXLAN_ATTR_REMCSUM_RX, a->vxi_proxy != b->vxi_proxy); + diff |= _DIFF(VXLAN_ATTR_COLLECT_METADATA, + a->vxi_collect_metadata != b->vxi_collect_metadata); + diff |= _DIFF(VXLAN_ATTR_LABEL, a->vxi_label != b->vxi_label); + diff |= _DIFF(VXLAN_ATTR_FLAGS, a->vxi_flags != b->vxi_flags); +#undef _DIFF + + return diff; +} + +static struct rtnl_link_info_ops vxlan_info_ops = { + .io_name = "vxlan", + .io_alloc = vxlan_alloc, + .io_parse = vxlan_parse, + .io_dump = { + [NL_DUMP_LINE] = vxlan_dump_line, + [NL_DUMP_DETAILS] = vxlan_dump_details, + }, + .io_clone = vxlan_clone, + .io_put_attrs = vxlan_put_attrs, + .io_free = vxlan_free, + .io_compare = vxlan_compare, +}; + +/** @cond SKIP */ +#define IS_VXLAN_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &vxlan_info_ops) { \ + APPBUG("Link is not a vxlan link. set type \"vxlan\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +/** + * @name VXLAN Object + * @{ + */ + +/** + * Allocate link object of type VXLAN + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_vxlan_alloc(void) +{ + struct rtnl_link *link; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if (rtnl_link_set_type(link, "vxlan") < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a VXLAN link + * @arg link Link object + * + * @return True if link is a VXLAN link, otherwise false is returned. + */ +int rtnl_link_is_vxlan(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "vxlan"); +} + +/** + * Set VXLAN Network Identifier + * @arg link Link object + * @arg id VXLAN network identifier (or VXLAN segment identifier) + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_id(struct rtnl_link *link, uint32_t id) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (id > VXLAN_ID_MAX) + return -NLE_INVAL; + + vxi->vxi_id = id; + vxi->ce_mask |= VXLAN_ATTR_ID; + + return 0; +} + +/** + * Get VXLAN Network Identifier + * @arg link Link object + * @arg id Pointer to store network identifier + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_get_id(struct rtnl_link *link, uint32_t *id) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if(!id) + return -NLE_INVAL; + + if (vxi->ce_mask & VXLAN_ATTR_ID) + *id = vxi->vxi_id; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set VXLAN multicast IP address + * @arg link Link object + * @arg addr Multicast IP address to join + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_group(struct rtnl_link *link, struct nl_addr *addr) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if ((nl_addr_get_family(addr) == AF_INET) && + (nl_addr_get_len(addr) == sizeof(vxi->vxi_group))) { + memcpy(&vxi->vxi_group, nl_addr_get_binary_addr(addr), + sizeof(vxi->vxi_group)); + vxi->ce_mask |= VXLAN_ATTR_GROUP; + vxi->ce_mask &= ~VXLAN_ATTR_GROUP6; + } else if ((nl_addr_get_family(addr) == AF_INET6) && + (nl_addr_get_len(addr) == sizeof(vxi->vxi_group6))) { + memcpy(&vxi->vxi_group6, nl_addr_get_binary_addr(addr), + sizeof(vxi->vxi_group6)); + vxi->ce_mask |= VXLAN_ATTR_GROUP6; + vxi->ce_mask &= ~VXLAN_ATTR_GROUP; + } else + return -NLE_INVAL; + + return 0; +} + +/** + * Get VXLAN multicast IP address + * @arg link Link object + * @arg addr Pointer to store multicast IP address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_get_group(struct rtnl_link *link, struct nl_addr **addr) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!addr) + return -NLE_INVAL; + + if (vxi->ce_mask & VXLAN_ATTR_GROUP) + *addr = nl_addr_build(AF_INET, &vxi->vxi_group, sizeof(vxi->vxi_group)); + else if (vxi->ce_mask & VXLAN_ATTR_GROUP6) + *addr = nl_addr_build(AF_INET6, &vxi->vxi_group6, sizeof(vxi->vxi_group6)); + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set physical device to use for VXLAN + * @arg link Link object + * @arg index Interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_link(struct rtnl_link *link, uint32_t index) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_link = index; + vxi->ce_mask |= VXLAN_ATTR_LINK; + + return 0; +} + +/** + * Get physical device to use for VXLAN + * @arg link Link object + * @arg index Pointer to store interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_get_link(struct rtnl_link *link, uint32_t *index) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!index) + return -NLE_INVAL; + + if (!(vxi->ce_mask & VXLAN_ATTR_LINK)) + return -NLE_AGAIN; + + *index = vxi->vxi_link; + + return 0; +} + +/** + * Set source address to use for VXLAN + * @arg link Link object + * @arg addr Local address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_local(struct rtnl_link *link, struct nl_addr *addr) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if ((nl_addr_get_family(addr) == AF_INET) && + (nl_addr_get_len(addr) == sizeof(vxi->vxi_local))) { + memcpy(&vxi->vxi_local, nl_addr_get_binary_addr(addr), + sizeof(vxi->vxi_local)); + vxi->ce_mask |= VXLAN_ATTR_LOCAL; + vxi->ce_mask &= ~VXLAN_ATTR_LOCAL6; + } else if ((nl_addr_get_family(addr) == AF_INET6) && + (nl_addr_get_len(addr) == sizeof(vxi->vxi_local6))) { + memcpy(&vxi->vxi_local6, nl_addr_get_binary_addr(addr), + sizeof(vxi->vxi_local6)); + vxi->ce_mask |= VXLAN_ATTR_LOCAL6; + vxi->ce_mask &= ~VXLAN_ATTR_LOCAL; + } else + return -NLE_INVAL; + + return 0; +} + +/** + * Get source address to use for VXLAN + * @arg link Link object + * @arg addr Pointer to store local address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_get_local(struct rtnl_link *link, struct nl_addr **addr) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!addr) + return -NLE_INVAL; + + if (vxi->ce_mask & VXLAN_ATTR_LOCAL) + *addr = nl_addr_build(AF_INET, &vxi->vxi_local, sizeof(vxi->vxi_local)); + else if (vxi->ce_mask & VXLAN_ATTR_LOCAL6) + *addr = nl_addr_build(AF_INET6, &vxi->vxi_local6, sizeof(vxi->vxi_local6)); + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set IP TTL value to use for VXLAN + * @arg link Link object + * @arg ttl TTL value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_ttl(struct rtnl_link *link, uint8_t ttl) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_ttl = ttl; + vxi->ce_mask |= VXLAN_ATTR_TTL; + + return 0; +} + +/** + * Get IP TTL value to use for VXLAN + * @arg link Link object + * + * @return TTL value on success or a negative error code + */ +int rtnl_link_vxlan_get_ttl(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_TTL)) + return -NLE_AGAIN; + + return vxi->vxi_ttl; +} + +/** + * Set IP ToS value to use for VXLAN + * @arg link Link object + * @arg tos ToS value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_tos(struct rtnl_link *link, uint8_t tos) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_tos = tos; + vxi->ce_mask |= VXLAN_ATTR_TOS; + + return 0; +} + +/** + * Get IP ToS value to use for VXLAN + * @arg link Link object + * + * @return ToS value on success or a negative error code + */ +int rtnl_link_vxlan_get_tos(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_TOS)) + return -NLE_AGAIN; + + return vxi->vxi_tos; +} + +/** + * Set VXLAN learning status + * @arg link Link object + * @arg learning Learning status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_learning(struct rtnl_link *link, uint8_t learning) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_learning = learning; + vxi->ce_mask |= VXLAN_ATTR_LEARNING; + + return 0; +} + +/** + * Get VXLAN learning status + * @arg link Link object + * + * @return Learning status value on success or a negative error code + */ +int rtnl_link_vxlan_get_learning(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_LEARNING)) + return -NLE_AGAIN; + + return vxi->vxi_learning; +} + +/** + * Enable VXLAN address learning + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_enable_learning(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_learning(link, 1); +} + +/** + * Disable VXLAN address learning + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_disable_learning(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_learning(link, 0); +} + +/** + * Set expiration timer value to use for VXLAN + * @arg link Link object + * @arg expiry Expiration timer value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_ageing(struct rtnl_link *link, uint32_t expiry) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_ageing = expiry; + vxi->ce_mask |= VXLAN_ATTR_AGEING; + + return 0; +} + +/** + * Get expiration timer value to use for VXLAN + * @arg link Link object + * @arg expiry Pointer to store expiration timer value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_get_ageing(struct rtnl_link *link, uint32_t *expiry) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!expiry) + return -NLE_INVAL; + + if (vxi->ce_mask & VXLAN_ATTR_AGEING) + *expiry = vxi->vxi_ageing; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set maximum number of forwarding database entries to use for VXLAN + * @arg link Link object + * @arg limit Maximum number + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_limit(struct rtnl_link *link, uint32_t limit) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_limit = limit; + vxi->ce_mask |= VXLAN_ATTR_LIMIT; + + return 0; +} + +/** + * Get maximum number of forwarding database entries to use for VXLAN + * @arg link Link object + * @arg limit Pointer to store maximum number + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_get_limit(struct rtnl_link *link, uint32_t *limit) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!limit) + return -NLE_INVAL; + + if (vxi->ce_mask & VXLAN_ATTR_LIMIT) + *limit = vxi->vxi_limit; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set range of UDP port numbers to use for VXLAN + * @arg link Link object + * @arg range Port number range + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_port_range(struct rtnl_link *link, + struct ifla_vxlan_port_range *range) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!range) + return -NLE_INVAL; + + memcpy(&vxi->vxi_port_range, range, sizeof(vxi->vxi_port_range)); + vxi->ce_mask |= VXLAN_ATTR_PORT_RANGE; + + return 0; +} + +/** + * Get range of UDP port numbers to use for VXLAN + * @arg link Link object + * @arg range Pointer to store port range + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_get_port_range(struct rtnl_link *link, + struct ifla_vxlan_port_range *range) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!range) + return -NLE_INVAL; + + if (vxi->ce_mask & VXLAN_ATTR_PORT_RANGE) + memcpy(range, &vxi->vxi_port_range, sizeof(*range)); + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set ARP proxy status to use for VXLAN + * @arg link Link object + * @arg proxy Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_proxy(struct rtnl_link *link, uint8_t proxy) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_proxy = proxy; + vxi->ce_mask |= VXLAN_ATTR_PROXY; + + return 0; +} + +/** + * Get ARP proxy status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_proxy(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_PROXY)) + return -NLE_AGAIN; + + return vxi->vxi_proxy; +} + +/** + * Enable ARP proxy + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_enable_proxy(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_proxy(link, 1); +} + +/** + * Disable ARP proxy + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_disable_proxy(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_proxy(link, 0); +} + +/** + * Set Route Short Circuit status to use for VXLAN + * @arg link Link object + * @arg rsc Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_rsc(struct rtnl_link *link, uint8_t rsc) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_rsc = rsc; + vxi->ce_mask |= VXLAN_ATTR_RSC; + + return 0; +} + +/** + * Get Route Short Circuit status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_rsc(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_RSC)) + return -NLE_AGAIN; + + return vxi->vxi_rsc; +} + +/** + * Enable Route Short Circuit + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_enable_rsc(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_rsc(link, 1); +} + +/** + * Disable Route Short Circuit + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_disable_rsc(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_rsc(link, 0); +} + +/** + * Set netlink LLADDR miss notification status to use for VXLAN + * @arg link Link object + * @arg miss Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_l2miss(struct rtnl_link *link, uint8_t miss) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_l2miss = miss; + vxi->ce_mask |= VXLAN_ATTR_L2MISS; + + return 0; +} + +/** + * Get netlink LLADDR miss notification status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_l2miss(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_L2MISS)) + return -NLE_AGAIN; + + return vxi->vxi_l2miss; +} + +/** + * Enable netlink LLADDR miss notifications + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_enable_l2miss(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_l2miss(link, 1); +} + +/** + * Disable netlink LLADDR miss notifications + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_disable_l2miss(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_l2miss(link, 0); +} + +/** + * Set netlink IP ADDR miss notification status to use for VXLAN + * @arg link Link object + * @arg miss Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_l3miss(struct rtnl_link *link, uint8_t miss) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_l3miss = miss; + vxi->ce_mask |= VXLAN_ATTR_L3MISS; + + return 0; +} + +/** + * Get netlink IP ADDR miss notification status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_l3miss(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_L3MISS)) + return -NLE_AGAIN; + + return vxi->vxi_l3miss; +} + +/** + * Enable netlink IP ADDR miss notifications + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_enable_l3miss(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_l3miss(link, 1); +} + +/** + * Disable netlink IP ADDR miss notifications + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_disable_l3miss(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_l3miss(link, 0); +} + +/** + * Set UDP destination port to use for VXLAN + * @arg link Link object + * @arg port Destination port + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_port(struct rtnl_link *link, uint32_t port) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_port = htons(port); + vxi->ce_mask |= VXLAN_ATTR_PORT; + + return 0; +} + +/** + * Get UDP destination port to use for VXLAN + * @arg link Link object + * @arg port Pointer to store destination port + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_get_port(struct rtnl_link *link, uint32_t *port) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!port) + return -NLE_INVAL; + + if (!(vxi->ce_mask & VXLAN_ATTR_PORT)) + return -NLE_NOATTR; + + *port = ntohs(vxi->vxi_port); + + return 0; +} + +/** + * Set UDP checksum status to use for VXLAN + * @arg link Link object + * @arg csum Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_udp_csum(struct rtnl_link *link, uint8_t csum) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_udp_csum = csum; + vxi->ce_mask |= VXLAN_ATTR_UDP_CSUM; + + return 0; +} + +/** + * Get UDP checksum status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_udp_csum(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_UDP_CSUM)) + return -NLE_NOATTR; + + return vxi->vxi_udp_csum; +} + +/** + * Set skip UDP checksum transmitted over IPv6 status to use for VXLAN + * @arg link Link object + * @arg csum Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_udp_zero_csum6_tx(struct rtnl_link *link, uint8_t csum) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_udp_zero_csum6_tx = csum; + vxi->ce_mask |= VXLAN_ATTR_UDP_ZERO_CSUM6_TX; + + return 0; +} + +/** + * Get skip UDP checksum transmitted over IPv6 status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_udp_zero_csum6_tx(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_UDP_ZERO_CSUM6_TX)) + return -NLE_NOATTR; + + return vxi->vxi_udp_zero_csum6_tx; +} + +/** + * Set skip UDP checksum received over IPv6 status to use for VXLAN + * @arg link Link object + * @arg csum Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_udp_zero_csum6_rx(struct rtnl_link *link, uint8_t csum) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_udp_zero_csum6_rx = csum; + vxi->ce_mask |= VXLAN_ATTR_UDP_ZERO_CSUM6_RX; + + return 0; +} + +/** + * Get skip UDP checksum received over IPv6 status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_udp_zero_csum6_rx(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_UDP_ZERO_CSUM6_RX)) + return -NLE_NOATTR; + + return vxi->vxi_udp_zero_csum6_rx; +} + +/** + * Set remote offload transmit checksum status to use for VXLAN + * @arg link Link object + * @arg csum Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_remcsum_tx(struct rtnl_link *link, uint8_t csum) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_remcsum_tx = csum; + vxi->ce_mask |= VXLAN_ATTR_REMCSUM_TX; + + return 0; +} + +/** + * Get remote offload transmit checksum status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_remcsum_tx(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_REMCSUM_TX)) + return -NLE_NOATTR; + + return vxi->vxi_remcsum_tx; +} + +/** + * Set remote offload receive checksum status to use for VXLAN + * @arg link Link object + * @arg csum Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_remcsum_rx(struct rtnl_link *link, uint8_t csum) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_remcsum_rx = csum; + vxi->ce_mask |= VXLAN_ATTR_REMCSUM_RX; + + return 0; +} + +/** + * Get remote offload receive checksum status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_remcsum_rx(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_REMCSUM_RX)) + return -NLE_NOATTR; + + return vxi->vxi_remcsum_rx; +} + +/** + * Set collect metadata status to use for VXLAN + * @arg link Link object + * @arg collect Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_collect_metadata(struct rtnl_link *link, uint8_t collect) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_collect_metadata = collect; + vxi->ce_mask |= VXLAN_ATTR_COLLECT_METADATA; + + return 0; +} + +/** + * Get collect metadata status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_collect_metadata(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_COLLECT_METADATA)) + return -NLE_NOATTR; + + return vxi->vxi_collect_metadata; +} + +/** + * Set flow label to use for VXLAN + * @arg link Link object + * @arg label Destination label + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_label(struct rtnl_link *link, uint32_t label) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_label = htonl(label); + vxi->ce_mask |= VXLAN_ATTR_LABEL; + + return 0; +} + +/** + * Get flow label to use for VXLAN + * @arg link Link object + * @arg label Pointer to store destination label + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_get_label(struct rtnl_link *link, uint32_t *label) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!label) + return -NLE_INVAL; + + if (!(vxi->ce_mask & VXLAN_ATTR_LABEL)) + return -NLE_NOATTR; + + *label = ntohl(vxi->vxi_label); + + return 0; +} + +/** + * Set VXLAN flags RTNL_LINK_VXLAN_F_* + * @arg link Link object + * @flags Which flags to set + * @arg enable Boolean enabling or disabling flag + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_flags(struct rtnl_link *link, uint32_t flags, int enable) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (flags & ~(RTNL_LINK_VXLAN_F_GBP | RTNL_LINK_VXLAN_F_GPE | RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL)) + return -NLE_INVAL; + + if (enable) + vxi->vxi_flags |= flags; + else + vxi->vxi_flags &= ~flags; + + return 0; +} + +/** + * Get VXLAN flags RTNL_LINK_VXLAN_F_* + * @arg link Link object + * @arg out_flags Output value for flags. Must be present. + * + * @return Zero on success or a negative error code + */ +int rtnl_link_vxlan_get_flags(struct rtnl_link *link, uint32_t *out_flags) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + *out_flags = vxi->vxi_flags; + return 0; +} + +/** @} */ + +static void _nl_init vxlan_init(void) +{ + rtnl_link_register_info(&vxlan_info_ops); +} + +static void _nl_exit vxlan_exit(void) +{ + rtnl_link_unregister_info(&vxlan_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/xfrmi.c b/libnl/lib/route/link/xfrmi.c new file mode 100644 index 0000000..09ceb80 --- /dev/null +++ b/libnl/lib/route/link/xfrmi.c @@ -0,0 +1,316 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2019 Eyal Birger + * + * Based on lib/route/link/ipvti.c + */ + +/** + * @ingroup link + * @defgroup xfrmi XFRMI + * xfrmi link module + * + * @details + * \b Link Type Name: "xfrmi" + * + * @route_doc{link_xfrmi, XFRMI Documentation} + * + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "link-api.h" + +#define XFRMI_ATTR_LINK (1 << 0) +#define XFRMI_ATTR_IF_ID (1 << 1) + +#define XFRMI_LINK_TYPE_NAME "xfrm" + +struct xfrmi_info { + uint32_t link; + uint32_t if_id; + uint32_t xfrmi_mask; +}; + +static struct nla_policy xfrmi_policy[IFLA_XFRM_MAX + 1] = { + [IFLA_XFRM_LINK] = { .type = NLA_U32 }, + [IFLA_XFRM_IF_ID] = { .type = NLA_U32 }, +}; + +static int xfrmi_alloc(struct rtnl_link *link) +{ + struct xfrmi_info *xfrmi; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*xfrmi)); + else { + xfrmi = calloc(1, sizeof(*xfrmi)); + if (!xfrmi) + return -NLE_NOMEM; + + link->l_info = xfrmi; + } + + return 0; +} + +static int xfrmi_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_XFRM_MAX + 1]; + struct xfrmi_info *xfrmi; + int err; + + NL_DBG(3, "Parsing XFRMI link info\n"); + + err = nla_parse_nested(tb, IFLA_XFRM_MAX, data, xfrmi_policy); + if (err < 0) + return err; + + err = xfrmi_alloc(link); + if (err < 0) + return err; + + xfrmi = link->l_info; + + if (tb[IFLA_XFRM_LINK]) { + xfrmi->link = nla_get_u32(tb[IFLA_XFRM_LINK]); + xfrmi->xfrmi_mask |= XFRMI_ATTR_LINK; + } + + if (tb[IFLA_XFRM_IF_ID]) { + xfrmi->if_id = nla_get_u32(tb[IFLA_XFRM_IF_ID]); + xfrmi->xfrmi_mask |= XFRMI_ATTR_IF_ID; + } + + return 0; +} + +static int xfrmi_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct xfrmi_info *xfrmi = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + + if (xfrmi->xfrmi_mask & XFRMI_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_XFRM_LINK, xfrmi->link); + + if (xfrmi->xfrmi_mask & XFRMI_ATTR_IF_ID) + NLA_PUT_U32(msg, IFLA_XFRM_IF_ID, xfrmi->if_id); + + nla_nest_end(msg, data); + +nla_put_failure: + return 0; +} + +static void xfrmi_free(struct rtnl_link *link) +{ + struct xfrmi_info *xfrmi = link->l_info; + + free(xfrmi); + link->l_info = NULL; +} + +static void xfrmi_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + nl_dump(p, "xfrmi : %s", link->l_name); +} + +static void xfrmi_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct xfrmi_info *xfrmi = link->l_info; + + if (xfrmi->xfrmi_mask & XFRMI_ATTR_LINK) { + struct rtnl_link *parent; + char *name; + + nl_dump(p, " link "); + + name = NULL; + parent = link_lookup(link->ce_cache, xfrmi->link); + if (parent) + name = rtnl_link_get_name(parent); + + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", xfrmi->link); + } + + if (xfrmi->xfrmi_mask & XFRMI_ATTR_IF_ID) { + nl_dump(p, " if_id "); + nl_dump_line(p, "%x\n", xfrmi->if_id); + } +} + +static int xfrmi_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct xfrmi_info *xfrmi_dst, *xfrmi_src = src->l_info; + int err; + + dst->l_info = NULL; + + err = rtnl_link_set_type(dst, XFRMI_LINK_TYPE_NAME); + if (err < 0) + return err; + + xfrmi_dst = dst->l_info; + + if (!xfrmi_dst || !xfrmi_src) + BUG(); + + memcpy(xfrmi_dst, xfrmi_src, sizeof(struct xfrmi_info)); + + return 0; +} + +static struct rtnl_link_info_ops xfrmi_info_ops = { + .io_name = XFRMI_LINK_TYPE_NAME, + .io_alloc = xfrmi_alloc, + .io_parse = xfrmi_parse, + .io_dump = { + [NL_DUMP_LINE] = xfrmi_dump_line, + [NL_DUMP_DETAILS] = xfrmi_dump_details, + }, + .io_clone = xfrmi_clone, + .io_put_attrs = xfrmi_put_attrs, + .io_free = xfrmi_free, +}; + +#define IS_XFRMI_LINK_ASSERT(link) do { \ + if ((link)->l_info_ops != &xfrmi_info_ops) { \ + APPBUG("Link is not a xfrmi link. set type \"xfrmi\" first."); \ + return -NLE_OPNOTSUPP; \ + } \ + } while(0) + +struct rtnl_link *rtnl_link_xfrmi_alloc(void) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_alloc(); + if (!link) + return NULL; + + err = rtnl_link_set_type(link, XFRMI_LINK_TYPE_NAME); + if (err < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a XFRMI link + * @arg link Link object + * + * @return True if link is a IXFRMI link, otherwise 0 is returned. + */ +int rtnl_link_is_xfrmi(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, + XFRMI_LINK_TYPE_NAME); +} + +/** + * Set XFRMI link interface index + * @arg link Link object + * @arg index interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_xfrmi_set_link(struct rtnl_link *link, uint32_t index) +{ + struct xfrmi_info *xfrmi = link->l_info; + + IS_XFRMI_LINK_ASSERT(link); + + xfrmi->link = index; + xfrmi->xfrmi_mask |= XFRMI_ATTR_LINK; + + return 0; +} + +/** + * Get XFRMI link interface index + * @arg link Link object + * @arg out_link The output value on success + * + * @return 0 on success or a negative error code + */ +int rtnl_link_xfrmi_get_link(struct rtnl_link *link, uint32_t *out_link) +{ + struct xfrmi_info *xfrmi = link->l_info; + + IS_XFRMI_LINK_ASSERT(link); + + if (!(xfrmi->xfrmi_mask & XFRMI_ATTR_LINK)) + return -NLE_NOATTR; + + *out_link = xfrmi->link; + return 0; +} + +/** + * Set XFRMI if_id + * @arg link Link object + * @arg if_id xfrm if_id + * + * @return 0 on success or a negative error code + */ +int rtnl_link_xfrmi_set_if_id(struct rtnl_link *link, uint32_t if_id) +{ + struct xfrmi_info *xfrmi = link->l_info; + + IS_XFRMI_LINK_ASSERT(link); + + xfrmi->if_id = if_id; + xfrmi->xfrmi_mask |= XFRMI_ATTR_IF_ID; + + return 0; +} + +/** + * Get XFRMI if_id + * @arg link Link object + * @arg out_if_id The output value on success + * + * @return 0 on success or a negative error code + */ +int rtnl_link_xfrmi_get_if_id(struct rtnl_link *link, uint32_t *out_if_id) +{ + struct xfrmi_info *xfrmi = link->l_info; + + IS_XFRMI_LINK_ASSERT(link); + + if (!(xfrmi->xfrmi_mask & XFRMI_ATTR_IF_ID)) + return -NLE_NOATTR; + + *out_if_id = xfrmi->if_id; + return 0; +} + +static void _nl_init xfrmi_init(void) +{ + rtnl_link_register_info(&xfrmi_info_ops); +} + +static void _nl_exit xfrmi_exit(void) +{ + rtnl_link_unregister_info(&xfrmi_info_ops); +} diff --git a/libnl/lib/route/mdb.c b/libnl/lib/route/mdb.c new file mode 100644 index 0000000..7749c16 --- /dev/null +++ b/libnl/lib/route/mdb.c @@ -0,0 +1,491 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * lib/route/mdb.c Multicast Database + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "nl-aux-route/nl-route.h" +#include "nl-priv-dynamic-core/object-api.h" +#include "nl-priv-dynamic-core/cache-api.h" + +/** @cond SKIP */ +#define MDB_ATTR_IFINDEX 0x000001 +#define MDB_ATTR_ENTRIES 0x000002 + +struct rtnl_mdb { + NLHDR_COMMON + uint32_t ifindex; + + struct nl_list_head mdb_entry_list; +}; + +struct rtnl_mdb_entry { + struct nl_list_head mdb_list; + struct nl_addr *addr; + uint32_t ifindex; + uint16_t vid; + uint16_t proto; + uint8_t state; +}; + +static struct rtnl_mdb_entry *rtnl_mdb_entry_alloc(void); +static void rtnl_mdb_entry_free(struct rtnl_mdb_entry *mdb_entry); + +static struct nl_cache_ops rtnl_mdb_ops; +static struct nl_object_ops mdb_obj_ops; +/** @endcond */ + +static void mdb_constructor(struct nl_object *obj) +{ + struct rtnl_mdb *_mdb = (struct rtnl_mdb *) obj; + + nl_init_list_head(&_mdb->mdb_entry_list); +} + +static void mdb_free_data(struct nl_object *obj) +{ + struct rtnl_mdb *mdb = (struct rtnl_mdb *)obj; + struct rtnl_mdb_entry *mdb_entry; + struct rtnl_mdb_entry *mdb_entry_safe; + + nl_list_for_each_entry_safe(mdb_entry, mdb_entry_safe, + &mdb->mdb_entry_list, mdb_list) + rtnl_mdb_entry_free(mdb_entry); +} + +static int mdb_entry_equal(struct rtnl_mdb_entry *a, struct rtnl_mdb_entry *b) +{ + return a->ifindex == b->ifindex + && a->vid == b->vid + && a->proto == b->proto + && a->state == b->state + && nl_addr_cmp(a->addr, b->addr) == 0; +} + +static uint64_t mdb_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct rtnl_mdb *a = (struct rtnl_mdb *) _a; + struct rtnl_mdb *b = (struct rtnl_mdb *) _b; + struct rtnl_mdb_entry *a_entry, *b_entry; + uint64_t diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(MDB_ATTR_IFINDEX, a->ifindex != b->ifindex); +#undef _DIFF + + a_entry = nl_list_entry(a->mdb_entry_list.next, struct rtnl_mdb_entry, mdb_list); + b_entry = nl_list_entry(b->mdb_entry_list.next, struct rtnl_mdb_entry, mdb_list); + while (1) { + if ( &a_entry->mdb_list == &a->mdb_entry_list + || &b_entry->mdb_list == &b->mdb_entry_list) { + if ( &a_entry->mdb_list != &a->mdb_entry_list + || &b_entry->mdb_list != &b->mdb_entry_list) + diff |= MDB_ATTR_ENTRIES; + break; + } + if (!mdb_entry_equal(a_entry, b_entry)) { + diff |= MDB_ATTR_ENTRIES; + break; + } + a_entry = nl_list_entry(a_entry->mdb_list.next, struct rtnl_mdb_entry, mdb_list); + b_entry = nl_list_entry(b_entry->mdb_list.next, struct rtnl_mdb_entry, mdb_list); + } + + return diff; +} + +static struct rtnl_mdb_entry *mdb_entry_clone(struct rtnl_mdb_entry *src) +{ + struct rtnl_mdb_entry *dst = rtnl_mdb_entry_alloc(); + if (!dst) + return NULL; + + dst->ifindex = src->ifindex; + dst->state = src->state; + dst->vid = src->vid; + dst->proto = src->proto; + + dst->addr = nl_addr_clone(src->addr); + if (dst->addr == NULL) { + free(dst); + return NULL; + } + + return dst; +} + +static int mdb_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_mdb *dst = nl_object_priv(_dst); + struct rtnl_mdb *src = nl_object_priv(_src); + struct rtnl_mdb_entry *entry; + + nl_init_list_head(&dst->mdb_entry_list); + + nl_list_for_each_entry(entry, &src->mdb_entry_list, mdb_list) { + struct rtnl_mdb_entry *copy = mdb_entry_clone(entry); + + if (!copy) + return -NLE_NOMEM; + + rtnl_mdb_add_entry(dst, copy); + } + + return 0; +} + +static int mdb_update(struct nl_object *old_obj, struct nl_object *new_obj) +{ + struct rtnl_mdb *old = (struct rtnl_mdb *) old_obj; + struct rtnl_mdb *new = (struct rtnl_mdb *) new_obj; + struct rtnl_mdb_entry *entry, *old_entry; + int action = new_obj->ce_msgtype; + + if (new->ifindex != old->ifindex) + return -NLE_OPNOTSUPP; + + switch (action) { + case RTM_NEWMDB: + nl_list_for_each_entry(entry, &new->mdb_entry_list, mdb_list) { + struct rtnl_mdb_entry *copy = mdb_entry_clone(entry); + + if (!copy) + return -NLE_NOMEM; + + rtnl_mdb_add_entry(old, copy); + } + break; + case RTM_DELMDB: + entry = nl_list_first_entry(&new->mdb_entry_list, + struct rtnl_mdb_entry, + mdb_list); + nl_list_for_each_entry(old_entry, &old->mdb_entry_list, mdb_list) { + if ( old_entry->ifindex == entry->ifindex + && !nl_addr_cmp(old_entry->addr, entry->addr)) { + nl_list_del(&old_entry->mdb_list); + break; + } + } + break; + } + + return NLE_SUCCESS; +} + +static struct nla_policy mdb_policy[MDBA_MAX + 1] = { + [MDBA_MDB] = {.type = NLA_NESTED}, +}; + +static struct nla_policy mdb_db_policy[MDBA_MDB_MAX + 1] = { + [MDBA_MDB_ENTRY] = {.type = NLA_NESTED}, +}; + +static int mdb_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + int err = 0; + int rem = 0; + struct nlattr *tb[MDBA_MAX + 1]; + struct br_port_msg *port; + struct nlattr *nla; + struct br_mdb_entry *e; + _nl_auto_rtnl_mdb struct rtnl_mdb *mdb = rtnl_mdb_alloc(); + + if (!mdb) + return -NLE_NOMEM; + + err = nlmsg_parse(nlh, sizeof(struct br_port_msg), tb, MDBA_MAX, + mdb_policy); + if (err < 0) + return err; + + mdb->ce_msgtype = nlh->nlmsg_type; + + port = nlmsg_data(nlh); + mdb->ifindex = port->ifindex; + mdb->ce_mask |= MDB_ATTR_IFINDEX; + + if (tb[MDBA_MDB]) { + struct nlattr *db_attr[MDBA_MDB_MAX+1]; + + err = nla_parse_nested(db_attr, MDBA_MDB_MAX, tb[MDBA_MDB], + mdb_db_policy); + if (err < 0) + return err; + rem = nla_len(tb[MDBA_MDB]); + + for (nla = nla_data(tb[MDBA_MDB]); nla_ok(nla, rem); + nla = nla_next(nla, &rem)) { + int rm = nla_len(nla); + struct nlattr *nla2; + + for (nla2 = nla_data(nla); nla_ok(nla2, rm); + nla2 = nla_next(nla2, &rm)) { + _nl_auto_nl_addr struct nl_addr *addr = NULL; + struct rtnl_mdb_entry *entry; + uint16_t proto; + + e = nla_data(nla2); + + proto = ntohs(e->addr.proto); + + if (proto == ETH_P_IP) { + addr = nl_addr_build( + AF_INET, &e->addr.u.ip4, + sizeof(e->addr.u.ip4)); + } else if (proto == ETH_P_IPV6) { + addr = nl_addr_build( + AF_INET6, &e->addr.u.ip6, + sizeof(e->addr.u.ip6)); + } else { + addr = nl_addr_build( + AF_LLC, e->addr.u.mac_addr, + sizeof(e->addr.u.mac_addr)); + } + if (!addr) + return -NLE_NOMEM; + + entry = rtnl_mdb_entry_alloc(); + if (!entry) + return -NLE_NOMEM; + + mdb->ce_mask |= MDB_ATTR_ENTRIES; + + entry->ifindex = e->ifindex; + entry->vid = e->vid; + entry->state = e->state; + entry->proto = ntohs(e->addr.proto); + entry->addr = _nl_steal_pointer(&addr); + rtnl_mdb_add_entry(mdb, entry); + } + } + } + + return pp->pp_cb((struct nl_object *) mdb, pp); +} + +static int mdb_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + return nl_rtgen_request(sk, RTM_GETMDB, AF_BRIDGE, NLM_F_DUMP); +} + +static void mdb_entry_dump_line(struct rtnl_mdb_entry *entry, + struct nl_dump_params *p) +{ + char buf[INET6_ADDRSTRLEN]; + + nl_dump(p, "port %d ", entry->ifindex); + nl_dump(p, "vid %d ", entry->vid); + nl_dump(p, "proto 0x%04x ", entry->proto); + nl_dump(p, "address %s\n", nl_addr2str(entry->addr, buf, sizeof(buf))); +} + +static void mdb_dump_line(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_mdb *mdb = (struct rtnl_mdb *) obj; + struct rtnl_mdb_entry *_mdb; + + nl_dump(p, "dev %d \n", mdb->ifindex); + + nl_list_for_each_entry(_mdb, &mdb->mdb_entry_list, mdb_list) { + p->dp_ivar = NH_DUMP_FROM_ONELINE; + mdb_entry_dump_line(_mdb, p); + } +} + +static void mdb_dump_details(struct nl_object *obj, struct nl_dump_params *p) +{ + mdb_dump_line(obj, p); +} + +static void mdb_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + mdb_dump_details(obj, p); +} + +void rtnl_mdb_put(struct rtnl_mdb *mdb) +{ + nl_object_put((struct nl_object *) mdb); +} + +/** @} */ + +/** + * @name Cache Management + * @{ + */ +int rtnl_mdb_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&rtnl_mdb_ops, sk, result); +} + +/** + * Build a neighbour cache including all MDB entries currently configured in the kernel. + * @arg sock Netlink socket. + * @arg result Pointer to store resulting cache. + * @arg flags Flags to apply to cache before filling + * + * Allocates a new MDB cache, initializes it properly and updates it + * to include all Multicast Database entries currently configured in the kernel. + * + * @return 0 on success or a negative error code. + */ +int rtnl_mdb_alloc_cache_flags(struct nl_sock *sock, struct nl_cache **result, + unsigned int flags) +{ + struct nl_cache *cache; + int err; + + cache = nl_cache_alloc(&rtnl_mdb_ops); + if (!cache) + return -NLE_NOMEM; + + nl_cache_set_flags(cache, flags); + + if (sock && (err = nl_cache_refill(sock, cache)) < 0) { + nl_cache_free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ +uint32_t rtnl_mdb_get_ifindex(struct rtnl_mdb *mdb) +{ + return mdb->ifindex; +} + +void rtnl_mdb_add_entry(struct rtnl_mdb *mdb, struct rtnl_mdb_entry *entry) +{ + nl_list_add_tail(&entry->mdb_list, &mdb->mdb_entry_list); +} + +void rtnl_mdb_foreach_entry(struct rtnl_mdb *mdb, + void (*cb)(struct rtnl_mdb_entry *, void *), + void *arg) +{ + struct rtnl_mdb_entry *entry; + + nl_list_for_each_entry(entry, &mdb->mdb_entry_list, mdb_list) { + cb(entry, arg); + } +} + +int rtnl_mdb_entry_get_ifindex(struct rtnl_mdb_entry *mdb_entry) +{ + return mdb_entry->ifindex; +} + +int rtnl_mdb_entry_get_vid(struct rtnl_mdb_entry *mdb_entry) +{ + return mdb_entry->vid; +} + +int rtnl_mdb_entry_get_state(struct rtnl_mdb_entry *mdb_entry) +{ + return mdb_entry->state; +} + +struct nl_addr *rtnl_mdb_entry_get_addr(struct rtnl_mdb_entry *mdb_entry) +{ + return mdb_entry->addr; +} + +uint16_t rtnl_mdb_entry_get_proto(struct rtnl_mdb_entry *mdb_entry) +{ + return mdb_entry->proto; +} + +/** @} */ + +static struct nl_object_ops mdb_obj_ops = { + .oo_name = "route/mdb", + .oo_size = sizeof(struct rtnl_mdb), + .oo_constructor = mdb_constructor, + .oo_dump = { + [NL_DUMP_LINE] = mdb_dump_line, + [NL_DUMP_DETAILS] = mdb_dump_details, + [NL_DUMP_STATS] = mdb_dump_stats, + }, + .oo_clone = mdb_clone, + .oo_compare = mdb_compare, + .oo_update = mdb_update, + .oo_free_data = mdb_free_data, +}; + +struct rtnl_mdb *rtnl_mdb_alloc(void) +{ + return (struct rtnl_mdb *) nl_object_alloc(&mdb_obj_ops); +} + +static struct rtnl_mdb_entry *rtnl_mdb_entry_alloc(void) +{ + struct rtnl_mdb_entry *mdb; + + mdb = calloc(1, sizeof(struct rtnl_mdb_entry)); + if (!mdb) + return NULL; + + nl_init_list_head(&mdb->mdb_list); + + return mdb; + +} + +static void rtnl_mdb_entry_free(struct rtnl_mdb_entry *mdb_entry) +{ + nl_list_del(&mdb_entry->mdb_list); + nl_addr_put(mdb_entry->addr); + free(mdb_entry); +} + +static struct nl_af_group mdb_groups[] = { + {AF_BRIDGE, RTNLGRP_MDB}, + {END_OF_GROUP_LIST}, +}; + +static struct nl_cache_ops rtnl_mdb_ops = { + .co_name = "route/mdb", + .co_hdrsize = sizeof(struct br_port_msg), + .co_msgtypes = { + { RTM_NEWMDB, NL_ACT_NEW, "new"}, + { RTM_DELMDB, NL_ACT_DEL, "del"}, + { RTM_GETMDB, NL_ACT_GET, "get"}, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = mdb_groups, + .co_request_update = mdb_request_update, + .co_msg_parser = mdb_msg_parser, + .co_obj_ops = &mdb_obj_ops, +}; + +static void _nl_init mdb_init(void) +{ + nl_cache_mngt_register(&rtnl_mdb_ops); +} + +static void _nl_exit mdb_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_mdb_ops); +} + +/** @} */ diff --git a/libnl/lib/route/neigh.c b/libnl/lib/route/neigh.c new file mode 100644 index 0000000..fd883ec --- /dev/null +++ b/libnl/lib/route/neigh.c @@ -0,0 +1,1239 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup neigh Neighbours + * @brief + * + * The neighbour table establishes bindings between protocol addresses and + * link layer addresses for hosts sharing the same physical link. This + * module allows you to access and manipulate the content of these tables. + * + * @par Neighbour States + * @code + * NUD_INCOMPLETE + * NUD_REACHABLE + * NUD_STALE + * NUD_DELAY + * NUD_PROBE + * NUD_FAILED + * NUD_NOARP + * NUD_PERMANENT + * @endcode + * + * @par Neighbour Flags + * @code + * NTF_USE + * NTF_PROXY + * NTF_ROUTER + * NTF_SELF + * @endcode + * + * @par Neighbour Identification + * A neighbour is uniquely identified by the attributes listed below, whenever + * you refer to an existing neighbour all of the attributes must be set. + * Neighbours from caches automatically have all required attributes set. + * - interface index (rtnl_neigh_set_ifindex()) + * - destination address (rtnl_neigh_set_dst()) + * + * @par Changeable Attributes + * \anchor neigh_changeable + * - state (rtnl_neigh_set_state()) + * - link layer address (rtnl_neigh_set_lladdr()) + * + * @par Required Caches for Dumping + * In order to dump neighbour attributes you must provide the following + * caches via nl_cache_provide() + * - link cache holding all links + * + * @par TODO + * - Document proxy settings + * - Document states and their influence + * + * @par 1) Retrieving information about configured neighbours + * @code + * // The first step is to retrieve a list of all available neighbour within + * // the kernel and put them into a cache. + * struct nl_cache *cache = rtnl_neigh_alloc_cache(sk); + * + * // Neighbours can then be looked up by the interface and destination + * // address: + * struct rtnl_neigh *neigh = rtnl_neigh_get(cache, ifindex, dst_addr); + * + * // After successful usage, the object must be given back to the cache + * rtnl_neigh_put(neigh); + * @endcode + * + * @par 2) Adding new neighbours + * @code + * // Allocate an empty neighbour handle to be filled out with the attributes + * // of the new neighbour. + * struct rtnl_neigh *neigh = rtnl_neigh_alloc(); + * + * // Fill out the attributes of the new neighbour + * rtnl_neigh_set_ifindex(neigh, ifindex); + * rtnl_neigh_set_dst(neigh, dst_addr); + * rtnl_neigh_set_state(neigh, rtnl_neigh_str2state("permanent")); + * + * // Build the netlink message and send it to the kernel, the operation will + * // block until the operation has been completed. Alternatively the required + * // netlink message can be built using rtnl_neigh_build_add_request() + * // to be sent out using nl_send_auto_complete(). + * rtnl_neigh_add(sk, neigh, NLM_F_CREATE); + * + * // Free the memory + * rtnl_neigh_put(neigh); + * @endcode + * + * @par 3) Deleting an existing neighbour + * @code + * // Allocate an empty neighbour object to be filled out with the attributes + * // matching the neighbour to be deleted. Alternatively a fully equipped + * // neighbour object out of a cache can be used instead. + * struct rtnl_neigh *neigh = rtnl_neigh_alloc(); + * + * // Neighbours are uniquely identified by their interface index and + * // destination address, you may fill out other attributes but they + * // will have no influence. + * rtnl_neigh_set_ifindex(neigh, ifindex); + * rtnl_neigh_set_dst(neigh, dst_addr); + * + * // Build the netlink message and send it to the kernel, the operation will + * // block until the operation has been completed. Alternatively the required + * // netlink message can be built using rtnl_neigh_build_delete_request() + * // to be sent out using nl_send_auto_complete(). + * rtnl_neigh_delete(sk, neigh, 0); + * + * // Free the memory + * rtnl_neigh_put(neigh); + * @endcode + * + * @par 4) Changing neighbour attributes + * @code + * // Allocate an empty neighbour object to be filled out with the attributes + * // matching the neighbour to be changed and the new parameters. Alternatively + * // a fully equipped modified neighbour object out of a cache can be used. + * struct rtnl_neigh *neigh = rtnl_neigh_alloc(); + * + * // Identify the neighbour to be changed by its interface index and + * // destination address + * rtnl_neigh_set_ifindex(neigh, ifindex); + * rtnl_neigh_set_dst(neigh, dst_addr); + * + * // The link layer address may be modified, if so it is wise to change + * // its state to "permanent" in order to avoid having it overwritten. + * rtnl_neigh_set_lladdr(neigh, lladdr); + * + * // Secondly the state can be modified allowing normal neighbours to be + * // converted into permanent entries or to manually confirm a neighbour. + * rtnl_neigh_set_state(neigh, state); + * + * // Build the netlink message and send it to the kernel, the operation will + * // block until the operation has been completed. Alternatively the required + * // netlink message can be built using rtnl_neigh_build_change_request() + * // to be sent out using nl_send_auto_complete(). + * rtnl_neigh_add(sk, neigh, NLM_F_REPLACE); + * + * // Free the memory + * rtnl_neigh_put(neigh); + * @endcode + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" + +/** @cond SKIP */ +struct rtnl_ncacheinfo { + uint32_t nci_confirmed; /**< Time since neighbour validty was last confirmed */ + uint32_t nci_used; /**< Time since neighbour entry was last ued */ + uint32_t nci_updated; /**< Time since last update */ + uint32_t nci_refcnt; /**< Reference counter */ +}; + +struct rtnl_neigh { + NLHDR_COMMON + uint32_t n_family; + uint32_t n_ifindex; + uint16_t n_state; + uint8_t n_flags; + uint32_t n_ext_flags; + uint8_t n_type; + struct nl_addr *n_lladdr; + struct nl_addr *n_dst; + uint32_t n_nhid; + uint32_t n_probes; + struct rtnl_ncacheinfo n_cacheinfo; + uint32_t n_state_mask; + uint32_t n_flag_mask; + uint32_t n_ext_flag_mask; + uint32_t n_master; + uint16_t n_vlan; +}; + +#define NEIGH_ATTR_FLAGS 0x01 +#define NEIGH_ATTR_STATE 0x02 +#define NEIGH_ATTR_LLADDR 0x04 +#define NEIGH_ATTR_DST 0x08 +#define NEIGH_ATTR_CACHEINFO 0x10 +#define NEIGH_ATTR_IFINDEX 0x20 +#define NEIGH_ATTR_FAMILY 0x40 +#define NEIGH_ATTR_TYPE 0x80 +#define NEIGH_ATTR_PROBES 0x0100 +#define NEIGH_ATTR_MASTER 0x0200 +#define NEIGH_ATTR_VLAN 0x0400 +#define NEIGH_ATTR_NHID 0x0800 +#define NEIGH_ATTR_EXT_FLAGS 0x1000 + +static struct nl_cache_ops rtnl_neigh_ops; +static struct nl_object_ops neigh_obj_ops; +/** @endcond */ + +static void neigh_free_data(struct nl_object *c) +{ + struct rtnl_neigh *neigh = nl_object_priv(c); + + if (!neigh) + return; + + nl_addr_put(neigh->n_lladdr); + nl_addr_put(neigh->n_dst); +} + +static int neigh_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_neigh *dst = nl_object_priv(_dst); + struct rtnl_neigh *src = nl_object_priv(_src); + + dst->n_lladdr = NULL; + dst->n_dst = NULL; + + if (src->n_lladdr) + if (!(dst->n_lladdr = nl_addr_clone(src->n_lladdr))) + return -NLE_NOMEM; + + if (src->n_dst) + if (!(dst->n_dst = nl_addr_clone(src->n_dst))) + return -NLE_NOMEM; + + return 0; +} + +static void neigh_keygen(struct nl_object *obj, uint32_t *hashkey, + uint32_t table_sz) +{ + struct rtnl_neigh *neigh = (struct rtnl_neigh *) obj; + unsigned int nkey_sz; + struct nl_addr *addr = NULL; + struct neigh_hash_key { + uint32_t n_family; + uint32_t n_ifindex; + uint16_t n_vlan; + char n_addr[0]; + } _nl_packed *nkey; + char buf[INET6_ADDRSTRLEN+5]; + + if (neigh->n_family == AF_BRIDGE) { + if (neigh->n_lladdr) + addr = neigh->n_lladdr; + } else if (neigh->n_dst) { + addr = neigh->n_dst; + } + + nkey_sz = sizeof(*nkey); + if (addr) + nkey_sz += nl_addr_get_len(addr); + + nkey = calloc(1, nkey_sz); + if (!nkey) { + *hashkey = 0; + return; + } + nkey->n_family = neigh->n_family; + if (neigh->n_family == AF_BRIDGE) { + nkey->n_vlan = neigh->n_vlan; + if (neigh->n_flags & NTF_SELF) + nkey->n_ifindex = neigh->n_ifindex; + else + nkey->n_ifindex = neigh->n_master; + } else + nkey->n_ifindex = neigh->n_ifindex; + + if (addr) + memcpy(nkey->n_addr, + nl_addr_get_binary_addr(addr), + nl_addr_get_len(addr)); + + *hashkey = nl_hash(nkey, nkey_sz, 0) % table_sz; + + NL_DBG(5, "neigh %p key (fam %d dev %d addr %s) keysz %d hash 0x%x\n", + neigh, nkey->n_family, nkey->n_ifindex, + nl_addr2str(addr, buf, sizeof(buf)), + nkey_sz, *hashkey); + + free(nkey); + + return; +} + +static uint64_t neigh_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct rtnl_neigh *a = (struct rtnl_neigh *) _a; + struct rtnl_neigh *b = (struct rtnl_neigh *) _b; + uint64_t diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(NEIGH_ATTR_IFINDEX, a->n_ifindex != b->n_ifindex); + diff |= _DIFF(NEIGH_ATTR_FAMILY, a->n_family != b->n_family); + diff |= _DIFF(NEIGH_ATTR_TYPE, a->n_type != b->n_type); + diff |= _DIFF(NEIGH_ATTR_LLADDR, nl_addr_cmp(a->n_lladdr, b->n_lladdr)); + diff |= _DIFF(NEIGH_ATTR_DST, nl_addr_cmp(a->n_dst, b->n_dst)); + diff |= _DIFF(NEIGH_ATTR_MASTER, a->n_master != b->n_master); + diff |= _DIFF(NEIGH_ATTR_VLAN, a->n_vlan != b->n_vlan); + diff |= _DIFF(NEIGH_ATTR_NHID, a->n_nhid != b->n_nhid); + + if (flags & LOOSE_COMPARISON) { + diff |= _DIFF(NEIGH_ATTR_STATE, + (a->n_state ^ b->n_state) & b->n_state_mask); + diff |= _DIFF(NEIGH_ATTR_FLAGS, + (a->n_flags ^ b->n_flags) & b->n_flag_mask); + diff |= _DIFF(NEIGH_ATTR_EXT_FLAGS, + (a->n_ext_flags ^ b->n_ext_flags) & b->n_ext_flag_mask); + } else { + diff |= _DIFF(NEIGH_ATTR_STATE, a->n_state != b->n_state); + diff |= _DIFF(NEIGH_ATTR_FLAGS, a->n_flags != b->n_flags); + diff |= _DIFF(NEIGH_ATTR_EXT_FLAGS, a->n_ext_flags != b->n_ext_flags); + } +#undef _DIFF + + return diff; +} + +static const struct trans_tbl neigh_attrs[] = { + __ADD(NEIGH_ATTR_FLAGS, flags), + __ADD(NEIGH_ATTR_STATE, state), + __ADD(NEIGH_ATTR_LLADDR, lladdr), + __ADD(NEIGH_ATTR_DST, dst), + __ADD(NEIGH_ATTR_CACHEINFO, cacheinfo), + __ADD(NEIGH_ATTR_IFINDEX, ifindex), + __ADD(NEIGH_ATTR_FAMILY, family), + __ADD(NEIGH_ATTR_TYPE, type), + __ADD(NEIGH_ATTR_PROBES, probes), + __ADD(NEIGH_ATTR_MASTER, master), + __ADD(NEIGH_ATTR_VLAN, vlan), + __ADD(NEIGH_ATTR_NHID, nhid), + __ADD(NEIGH_ATTR_EXT_FLAGS, ext_flags), +}; + +static char *neigh_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, neigh_attrs, + ARRAY_SIZE(neigh_attrs)); +} + +static uint32_t neigh_id_attrs_get(struct nl_object *obj) +{ + struct rtnl_neigh *neigh = (struct rtnl_neigh *)obj; + + if (neigh->n_family == AF_BRIDGE) { + if (neigh->n_flags & NTF_SELF) + return (NEIGH_ATTR_LLADDR | NEIGH_ATTR_FAMILY | NEIGH_ATTR_IFINDEX | + ((neigh->ce_mask & NEIGH_ATTR_DST) ? NEIGH_ATTR_DST: 0) | + ((neigh->ce_mask & NEIGH_ATTR_NHID) ? NEIGH_ATTR_NHID: 0) | + ((neigh->ce_mask & NEIGH_ATTR_VLAN) ? NEIGH_ATTR_VLAN : 0)); + else + return (NEIGH_ATTR_LLADDR | NEIGH_ATTR_FAMILY | NEIGH_ATTR_MASTER | NEIGH_ATTR_VLAN); + } else + return neigh_obj_ops.oo_id_attrs; +} + +static struct nla_policy neigh_policy[NDA_MAX+1] = { + [NDA_CACHEINFO] = { .minlen = sizeof(struct nda_cacheinfo) }, + [NDA_PROBES] = { .type = NLA_U32 }, +}; + +static int neigh_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *n, struct nl_parser_param *pp) +{ + struct rtnl_neigh *neigh; + int err; + + if ((err = rtnl_neigh_parse(n, &neigh)) < 0) + return err; + + err = pp->pp_cb((struct nl_object *) neigh, pp); + + rtnl_neigh_put(neigh); + return err; +} + + +int rtnl_neigh_parse(struct nlmsghdr *n, struct rtnl_neigh **result) +{ + struct rtnl_neigh *neigh; + struct nlattr *tb[NDA_MAX + 1]; + struct ndmsg *nm; + int err; + + neigh = rtnl_neigh_alloc(); + if (!neigh) { + err = -NLE_NOMEM; + goto errout; + } + + neigh->ce_msgtype = n->nlmsg_type; + nm = nlmsg_data(n); + + err = nlmsg_parse(n, sizeof(*nm), tb, NDA_MAX, neigh_policy); + if (err < 0) + goto errout; + + neigh->n_family = nm->ndm_family; + neigh->n_ifindex = nm->ndm_ifindex; + neigh->n_state = nm->ndm_state; + neigh->n_flags = nm->ndm_flags; + neigh->n_type = nm->ndm_type; + + neigh->ce_mask |= (NEIGH_ATTR_FAMILY | NEIGH_ATTR_IFINDEX | + NEIGH_ATTR_STATE | NEIGH_ATTR_FLAGS | + NEIGH_ATTR_TYPE); + + if (tb[NDA_LLADDR]) { + neigh->n_lladdr = nl_addr_alloc_attr(tb[NDA_LLADDR], AF_UNSPEC); + if (!neigh->n_lladdr) { + err = -NLE_NOMEM; + goto errout; + } + nl_addr_set_family(neigh->n_lladdr, + nl_addr_guess_family(neigh->n_lladdr)); + neigh->ce_mask |= NEIGH_ATTR_LLADDR; + } + + if (tb[NDA_DST]) { + neigh->n_dst = nl_addr_alloc_attr(tb[NDA_DST], AF_UNSPEC); + if (!neigh->n_dst) { + err = -NLE_NOMEM; + goto errout; + } + nl_addr_set_family(neigh->n_dst, + nl_addr_guess_family(neigh->n_dst)); + neigh->ce_mask |= NEIGH_ATTR_DST; + } + + if (tb[NDA_CACHEINFO]) { + struct nda_cacheinfo *ci = nla_data(tb[NDA_CACHEINFO]); + + neigh->n_cacheinfo.nci_confirmed = ci->ndm_confirmed; + neigh->n_cacheinfo.nci_used = ci->ndm_used; + neigh->n_cacheinfo.nci_updated = ci->ndm_updated; + neigh->n_cacheinfo.nci_refcnt = ci->ndm_refcnt; + + neigh->ce_mask |= NEIGH_ATTR_CACHEINFO; + } + + if (tb[NDA_PROBES]) { + neigh->n_probes = nla_get_u32(tb[NDA_PROBES]); + neigh->ce_mask |= NEIGH_ATTR_PROBES; + } + + if (tb[NDA_VLAN]) { + neigh->n_vlan = nla_get_u16(tb[NDA_VLAN]); + neigh->ce_mask |= NEIGH_ATTR_VLAN; + } + + if (tb[NDA_NH_ID]) { + neigh->n_nhid = nla_get_u32(tb[NDA_NH_ID]); + neigh->ce_mask |= NEIGH_ATTR_NHID; + } + + if (tb[NDA_FLAGS_EXT]) { + neigh->n_ext_flags = nla_get_u32(tb[NDA_FLAGS_EXT]); + neigh->ce_mask |= NEIGH_ATTR_EXT_FLAGS; + } + + /* + * Get the bridge index for AF_BRIDGE family entries + */ + if (neigh->n_family == AF_BRIDGE) { + if (tb[NDA_MASTER]) { + neigh->n_master = nla_get_u32(tb[NDA_MASTER]); + neigh->ce_mask |= NEIGH_ATTR_MASTER; + } else { + struct nl_cache *lcache = nl_cache_mngt_require_safe("route/link"); + if (lcache ) { + struct rtnl_link *link = rtnl_link_get(lcache, + neigh->n_ifindex); + if (link) { + neigh->n_master = link->l_master; + rtnl_link_put(link); + neigh->ce_mask |= NEIGH_ATTR_MASTER; + } + nl_cache_put(lcache); + } + } + } + + *result = neigh; + return 0; + +errout: + rtnl_neigh_put(neigh); + return err; +} + +static int neigh_request_update(struct nl_cache *c, struct nl_sock *h) +{ + int family = c->c_iarg1; + + if (family == AF_UNSPEC) { + return nl_rtgen_request(h, RTM_GETNEIGH, family, NLM_F_DUMP); + } else if (family == AF_BRIDGE) { + struct ifinfomsg hdr = {.ifi_family = family}; + struct nl_msg *msg; + int err; + + msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP); + if (!msg) + return -NLE_NOMEM; + + err = -NLE_MSGSIZE; + if (nlmsg_append(msg, &hdr, sizeof(hdr), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + err = nl_send_auto(h, msg); + if (err > 0) + err = 0; + + nla_put_failure: + nlmsg_free(msg); + return err; + } + + return -NLE_INVAL; +} + + +static void neigh_dump_line(struct nl_object *a, struct nl_dump_params *p) +{ + char dst[INET6_ADDRSTRLEN+5], lladdr[INET6_ADDRSTRLEN+5]; + struct rtnl_neigh *n = (struct rtnl_neigh *) a; + struct nl_cache *link_cache; + char state[128], flags[64], ext_flags[64]; + char buf[128]; + + link_cache = nl_cache_mngt_require_safe("route/link"); + + if (n->n_family != AF_UNSPEC) + nl_dump_line(p, "%s ", nl_af2str(n->n_family, buf, sizeof(buf))); + + if (n->ce_mask & NEIGH_ATTR_DST) + nl_dump_line(p, "%s ", nl_addr2str(n->n_dst, dst, sizeof(dst))); + + if (link_cache) + nl_dump(p, "dev %s ", + rtnl_link_i2name(link_cache, n->n_ifindex, + state, sizeof(state))); + else + nl_dump(p, "dev %d ", n->n_ifindex); + + if (n->ce_mask & NEIGH_ATTR_LLADDR) + nl_dump(p, "lladdr %s ", + nl_addr2str(n->n_lladdr, lladdr, sizeof(lladdr))); + + if (n->ce_mask & NEIGH_ATTR_VLAN) + nl_dump(p, "vlan %d ", n->n_vlan); + + if (n->ce_mask & NEIGH_ATTR_NHID) + nl_dump(p, "nhid %u ", n->n_nhid); + + if (n->ce_mask & NEIGH_ATTR_MASTER) { + if (link_cache) + nl_dump(p, "%s ", rtnl_link_i2name(link_cache, n->n_master, + state, sizeof(state))); + else + nl_dump(p, "%d ", n->n_master); + } + + rtnl_neigh_state2str(n->n_state, state, sizeof(state)); + rtnl_neigh_flags2str(n->n_flags, flags, sizeof(flags)); + rtnl_neigh_extflags2str(n->n_ext_flags, ext_flags, sizeof(ext_flags)); + + if (state[0]) + nl_dump(p, "<%s", state); + if (flags[0]) + nl_dump(p, "%s%s", state[0] ? "," : "<", flags); + if (ext_flags[0]) + nl_dump(p, "%s%s", state[0] || flags[0] ? "," : "<", ext_flags); + if (state[0] || flags[0] || ext_flags[0]) + nl_dump(p, ">"); + nl_dump(p, "\n"); + + if (link_cache) + nl_cache_put(link_cache); +} + +static void neigh_dump_details(struct nl_object *a, struct nl_dump_params *p) +{ + char rtn_type[32]; + struct rtnl_neigh *n = (struct rtnl_neigh *) a; + int hz = nl_get_user_hz(); + + neigh_dump_line(a, p); + + nl_dump_line(p, " refcnt %u type %s confirmed %u used " + "%u updated %u\n", + n->n_cacheinfo.nci_refcnt, + nl_rtntype2str(n->n_type, rtn_type, sizeof(rtn_type)), + n->n_cacheinfo.nci_confirmed/hz, + n->n_cacheinfo.nci_used/hz, n->n_cacheinfo.nci_updated/hz); +} + +static void neigh_dump_stats(struct nl_object *a, struct nl_dump_params *p) +{ + neigh_dump_details(a, p); +} + +/** + * @name Neighbour Object Allocation/Freeage + * @{ + */ + +struct rtnl_neigh *rtnl_neigh_alloc(void) +{ + return (struct rtnl_neigh *) nl_object_alloc(&neigh_obj_ops); +} + +void rtnl_neigh_put(struct rtnl_neigh *neigh) +{ + nl_object_put((struct nl_object *) neigh); +} + +/** @} */ + +/** + * @name Neighbour Cache Managament + * @{ + */ + +/** + * Build a neighbour cache including all neighbours currently configured in the kernel. + * @arg sock Netlink socket. + * @arg result Pointer to store resulting cache. + * + * Allocates a new neighbour cache, initializes it properly and updates it + * to include all neighbours currently configured in the kernel. + * + * @return 0 on success or a negative error code. + */ +int rtnl_neigh_alloc_cache(struct nl_sock *sock, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&rtnl_neigh_ops, sock, result); +} + +/** + * Build a neighbour cache including all neighbours currently configured in the kernel. + * @arg sock Netlink socket. + * @arg result Pointer to store resulting cache. + * @arg flags Flags to apply to cache before filling + * + * Allocates a new neighbour cache, initializes it properly and updates it + * to include all neighbours currently configured in the kernel. + * + * @return 0 on success or a negative error code. + */ +int rtnl_neigh_alloc_cache_flags(struct nl_sock *sock, struct nl_cache **result, + unsigned int flags) +{ + struct nl_cache * cache; + int err; + + cache = nl_cache_alloc(&rtnl_neigh_ops); + if (!cache) + return -NLE_NOMEM; + + nl_cache_set_flags(cache, flags); + + if (sock && (err = nl_cache_refill(sock, cache)) < 0) { + nl_cache_free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** + * Look up a neighbour by interface index and destination address + * @arg cache neighbour cache + * @arg ifindex interface index the neighbour is on + * @arg dst destination address of the neighbour + * + * @return neighbour handle or NULL if no match was found. + */ +struct rtnl_neigh * rtnl_neigh_get(struct nl_cache *cache, int ifindex, + struct nl_addr *dst) +{ + struct rtnl_neigh *neigh; + + nl_list_for_each_entry(neigh, &cache->c_items, ce_list) { + if (neigh->n_ifindex == ((unsigned)ifindex) && + neigh->n_family == ((unsigned)dst->a_family) && + !nl_addr_cmp(neigh->n_dst, dst)) { + nl_object_get((struct nl_object *) neigh); + return neigh; + } + } + + return NULL; +} + +/** + * Look up a neighbour by interface index, link layer address and vlan id + * @arg cache neighbour cache + * @arg ifindex interface index the neighbour is on + * @arg lladdr link layer address of the neighbour + * @arg vlan vlan id of the neighbour + * + * @return neighbour handle or NULL if no match was found. + */ +struct rtnl_neigh * rtnl_neigh_get_by_vlan(struct nl_cache *cache, int ifindex, + struct nl_addr *lladdr, int vlan) +{ + struct rtnl_neigh *neigh; + + nl_list_for_each_entry(neigh, &cache->c_items, ce_list) { + if ((neigh->n_ifindex == (unsigned)ifindex) && + neigh->n_vlan == vlan && neigh->n_lladdr && + !nl_addr_cmp(neigh->n_lladdr, lladdr)) { + nl_object_get((struct nl_object *) neigh); + return neigh; + } + } + + return NULL; +} + +/** @} */ + +/** + * @name Neighbour Addition + * @{ + */ + +static int build_neigh_msg(struct rtnl_neigh *tmpl, int cmd, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + struct ndmsg nhdr = { + .ndm_ifindex = tmpl->n_ifindex, + .ndm_state = NUD_PERMANENT, + }; + + if (tmpl->n_family != AF_BRIDGE) { + if (!(tmpl->ce_mask & NEIGH_ATTR_DST)) + return -NLE_MISSING_ATTR; + nhdr.ndm_family = nl_addr_get_family(tmpl->n_dst); + } + else + nhdr.ndm_family = AF_BRIDGE; + + if (tmpl->ce_mask & NEIGH_ATTR_FLAGS) + nhdr.ndm_flags = tmpl->n_flags; + + if (tmpl->ce_mask & NEIGH_ATTR_STATE) + nhdr.ndm_state = tmpl->n_state; + + msg = nlmsg_alloc_simple(cmd, flags); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (tmpl->ce_mask & NEIGH_ATTR_DST) + NLA_PUT_ADDR(msg, NDA_DST, tmpl->n_dst); + + if (tmpl->ce_mask & NEIGH_ATTR_LLADDR) + NLA_PUT_ADDR(msg, NDA_LLADDR, tmpl->n_lladdr); + + if (tmpl->ce_mask & NEIGH_ATTR_VLAN) + NLA_PUT_U16(msg, NDA_VLAN, tmpl->n_vlan); + + if (tmpl->ce_mask & NEIGH_ATTR_NHID) + NLA_PUT_U32(msg, NDA_NH_ID, tmpl->n_nhid); + + if (tmpl->ce_mask & NEIGH_ATTR_EXT_FLAGS) { + /* The kernel does not allow setting the locked flag from + * userspace, so unset it in the request. */ + uint32_t ext_flags = tmpl->n_ext_flags & + ~(uint32_t)NTF_EXT_LOCKED; + + if (ext_flags) + NLA_PUT_U32(msg, NDA_FLAGS_EXT, ext_flags); + } + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +/** + * Build netlink request message to add a new neighbour + * @arg tmpl template with data of new neighbour + * @arg flags additional netlink message flags + * @arg result Pointer to store resulting message. + * + * Builds a new netlink message requesting a addition of a new + * neighbour. The netlink message header isn't fully equipped with + * all relevant fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. \a tmpl must contain the attributes of the new + * neighbour set via \c rtnl_neigh_set_* functions. + * + * The following attributes must be set in the template: + * - Interface index (rtnl_neigh_set_ifindex()) + * - State (rtnl_neigh_set_state()) + * - Destination address (rtnl_neigh_set_dst()) + * - Link layer address (rtnl_neigh_set_lladdr()) + * + * @return 0 on success or a negative error code. + */ +int rtnl_neigh_build_add_request(struct rtnl_neigh *tmpl, int flags, + struct nl_msg **result) +{ + return build_neigh_msg(tmpl, RTM_NEWNEIGH, flags, result); +} + +/** + * Add a new neighbour + * @arg sk Netlink socket. + * @arg tmpl template with requested changes + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_neigh_build_add_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been fullfilled. + * + * The following attributes must be set in the template: + * - Interface index (rtnl_neigh_set_ifindex()) + * - State (rtnl_neigh_set_state()) + * - Destination address (rtnl_neigh_set_dst()) + * - Link layer address (rtnl_neigh_set_lladdr()) + * + * @return 0 on success or a negative error if an error occured. + */ +int rtnl_neigh_add(struct nl_sock *sk, struct rtnl_neigh *tmpl, int flags) +{ + int err; + struct nl_msg *msg; + + if ((err = rtnl_neigh_build_add_request(tmpl, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** @} */ + +/** + * @name Neighbour Deletion + * @{ + */ + +/** + * Build a netlink request message to delete a neighbour + * @arg neigh neighbour to delete + * @arg flags additional netlink message flags + * @arg result Pointer to store resulting message. + * + * Builds a new netlink message requesting a deletion of a neighbour. + * The netlink message header isn't fully equipped with all relevant + * fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. \a neigh must point to an existing + * neighbour. + * + * @return 0 on success or a negative error code. + */ +int rtnl_neigh_build_delete_request(struct rtnl_neigh *neigh, int flags, + struct nl_msg **result) +{ + return build_neigh_msg(neigh, RTM_DELNEIGH, flags, result); +} + +/** + * Delete a neighbour + * @arg sk Netlink socket. + * @arg neigh neighbour to delete + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_neigh_build_delete_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been fullfilled. + * + * @return 0 on success or a negative error if an error occured. + */ +int rtnl_neigh_delete(struct nl_sock *sk, struct rtnl_neigh *neigh, + int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_neigh_build_delete_request(neigh, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** @} */ + +/** + * @name Neighbour States Translations + * @{ + */ + +static const struct trans_tbl neigh_states[] = { + __ADD(NUD_INCOMPLETE, incomplete), + __ADD(NUD_REACHABLE, reachable), + __ADD(NUD_STALE, stale), + __ADD(NUD_DELAY, delay), + __ADD(NUD_PROBE, probe), + __ADD(NUD_FAILED, failed), + __ADD(NUD_NOARP, noarp), + __ADD(NUD_PERMANENT, permanent), + + /* Accept this value for backward compatibility. Originally + * there was a typo in the string value. This was fixed later, + * but we still want to successfully parse "norarp". */ + __ADD(NUD_NOARP, norarp), +}; + +char * rtnl_neigh_state2str(int state, char *buf, size_t len) +{ + return __flags2str(state, buf, len, neigh_states, + ARRAY_SIZE(neigh_states) - 1); +} + +int rtnl_neigh_str2state(const char *name) +{ + return __str2type(name, neigh_states, ARRAY_SIZE(neigh_states)); +} + +/** @} */ + +/** + * @name Neighbour Flags Translations + * @{ + */ + +static const struct trans_tbl neigh_flags[] = { + __ADD(NTF_USE, use), + __ADD(NTF_PROXY, proxy), + __ADD(NTF_ROUTER, router), + __ADD(NTF_SELF, self), + __ADD(NTF_MASTER, master), + __ADD(NTF_EXT_LEARNED, ext_learned), + __ADD(NTF_OFFLOADED, offloaded), +}; + +static const struct trans_tbl neigh_ext_flags[] = { + __ADD(NTF_EXT_MANAGED, managed), + __ADD(NTF_EXT_LOCKED, locked), +}; + +char * rtnl_neigh_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, neigh_flags, + ARRAY_SIZE(neigh_flags)); +} + +int rtnl_neigh_str2flag(const char *name) +{ + return __str2type(name, neigh_flags, ARRAY_SIZE(neigh_flags)); +} + +char * rtnl_neigh_extflags2str(uint32_t flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, neigh_ext_flags, + ARRAY_SIZE(neigh_ext_flags)); +} + +uint32_t rtnl_neigh_str2extflag(const char *name) +{ + return __str2type(name, neigh_ext_flags, ARRAY_SIZE(neigh_ext_flags)); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void rtnl_neigh_set_state(struct rtnl_neigh *neigh, int state) +{ + neigh->n_state_mask |= state; + neigh->n_state |= state; + neigh->ce_mask |= NEIGH_ATTR_STATE; +} + +int rtnl_neigh_get_state(struct rtnl_neigh *neigh) +{ + if (neigh->ce_mask & NEIGH_ATTR_STATE) + return neigh->n_state; + else + return -1; +} + +void rtnl_neigh_unset_state(struct rtnl_neigh *neigh, int state) +{ + neigh->n_state_mask |= state; + neigh->n_state &= ~state; + neigh->ce_mask |= NEIGH_ATTR_STATE; +} + +void rtnl_neigh_set_flags(struct rtnl_neigh *neigh, unsigned int flags) +{ + neigh->n_flag_mask |= flags; + neigh->n_flags |= flags; + neigh->ce_mask |= NEIGH_ATTR_FLAGS; +} + +unsigned int rtnl_neigh_get_flags(struct rtnl_neigh *neigh) +{ + return neigh->n_flags; +} + +void rtnl_neigh_unset_flags(struct rtnl_neigh *neigh, unsigned int flags) +{ + neigh->n_flag_mask |= flags; + neigh->n_flags &= ~flags; + neigh->ce_mask |= NEIGH_ATTR_FLAGS; +} + +void rtnl_neigh_set_ext_flags(struct rtnl_neigh *neigh, uint32_t ext_flags) +{ + neigh->n_ext_flag_mask |= ext_flags; + neigh->n_ext_flags |= ext_flags; + neigh->ce_mask |= NEIGH_ATTR_EXT_FLAGS; +} + +int rtnl_neigh_get_ext_flags(struct rtnl_neigh *neigh, uint32_t *out_val) +{ + if (!(neigh->ce_mask & NEIGH_ATTR_EXT_FLAGS)) + return -NLE_NOATTR; + + *out_val = neigh->n_ext_flags; + return NLE_SUCCESS; +} + +void rtnl_neigh_unset_ext_flags(struct rtnl_neigh *neigh, uint32_t ext_flags) +{ + neigh->n_ext_flag_mask |= ext_flags; + neigh->n_ext_flags &= ~ext_flags; + neigh->ce_mask |= NEIGH_ATTR_EXT_FLAGS; +} + +void rtnl_neigh_set_ifindex(struct rtnl_neigh *neigh, int ifindex) +{ + neigh->n_ifindex = ifindex; + neigh->ce_mask |= NEIGH_ATTR_IFINDEX; +} + +int rtnl_neigh_get_ifindex(struct rtnl_neigh *neigh) +{ + return neigh->n_ifindex; +} + +static inline int __assign_addr(struct rtnl_neigh *neigh, struct nl_addr **pos, + struct nl_addr *new, int flag, int nocheck) +{ + if (!nocheck) { + if (neigh->ce_mask & NEIGH_ATTR_FAMILY) { + if (neigh->n_family != ((unsigned)new->a_family)) + return -NLE_AF_MISMATCH; + } else { + neigh->n_family = new->a_family; + neigh->ce_mask |= NEIGH_ATTR_FAMILY; + } + } + + if (*pos) + nl_addr_put(*pos); + + nl_addr_get(new); + *pos = new; + + neigh->ce_mask |= flag; + + return 0; +} + +void rtnl_neigh_set_lladdr(struct rtnl_neigh *neigh, struct nl_addr *addr) +{ + __assign_addr(neigh, &neigh->n_lladdr, addr, NEIGH_ATTR_LLADDR, 1); +} + +struct nl_addr *rtnl_neigh_get_lladdr(struct rtnl_neigh *neigh) +{ + if (neigh->ce_mask & NEIGH_ATTR_LLADDR) + return neigh->n_lladdr; + else + return NULL; +} + +int rtnl_neigh_set_dst(struct rtnl_neigh *neigh, struct nl_addr *addr) +{ + return __assign_addr(neigh, &neigh->n_dst, addr, + NEIGH_ATTR_DST, 0); +} + +struct nl_addr *rtnl_neigh_get_dst(struct rtnl_neigh *neigh) +{ + if (neigh->ce_mask & NEIGH_ATTR_DST) + return neigh->n_dst; + else + return NULL; +} + +void rtnl_neigh_set_family(struct rtnl_neigh *neigh, int family) +{ + neigh->n_family = family; + neigh->ce_mask |= NEIGH_ATTR_FAMILY; +} + +int rtnl_neigh_get_family(struct rtnl_neigh *neigh) +{ + return neigh->n_family; +} + +void rtnl_neigh_set_type(struct rtnl_neigh *neigh, int type) +{ + neigh->n_type = type; + neigh->ce_mask = NEIGH_ATTR_TYPE; +} + +int rtnl_neigh_get_type(struct rtnl_neigh *neigh) +{ + if (neigh->ce_mask & NEIGH_ATTR_TYPE) + return neigh->n_type; + else + return -1; +} + +void rtnl_neigh_set_vlan(struct rtnl_neigh *neigh, int vlan) +{ + neigh->n_vlan = vlan; + neigh->ce_mask |= NEIGH_ATTR_VLAN; +} + +int rtnl_neigh_get_vlan(struct rtnl_neigh *neigh) +{ + if (neigh->ce_mask & NEIGH_ATTR_VLAN) + return neigh->n_vlan; + else + return -1; +} + +void rtnl_neigh_set_master(struct rtnl_neigh *neigh, int ifindex) +{ + neigh->n_master = ifindex; + neigh->ce_mask |= NEIGH_ATTR_MASTER; +} + +int rtnl_neigh_get_master(struct rtnl_neigh *neigh) { + return neigh->n_master; +} + +void rtnl_neigh_set_nhid(struct rtnl_neigh *neigh, uint32_t nhid) +{ + neigh->n_nhid = nhid; + neigh->ce_mask |= NEIGH_ATTR_NHID; +} + +int rtnl_neigh_get_nhid(struct rtnl_neigh *neigh, uint32_t *out_val) { + if (!(neigh->ce_mask & NEIGH_ATTR_NHID)) + return -NLE_NOATTR; + + *out_val = neigh->n_nhid; + return NLE_SUCCESS; +} + +/** @} */ + +static struct nl_object_ops neigh_obj_ops = { + .oo_name = "route/neigh", + .oo_size = sizeof(struct rtnl_neigh), + .oo_free_data = neigh_free_data, + .oo_clone = neigh_clone, + .oo_dump = { + [NL_DUMP_LINE] = neigh_dump_line, + [NL_DUMP_DETAILS] = neigh_dump_details, + [NL_DUMP_STATS] = neigh_dump_stats, + }, + .oo_compare = neigh_compare, + .oo_keygen = neigh_keygen, + .oo_attrs2str = neigh_attrs2str, + .oo_id_attrs = (NEIGH_ATTR_IFINDEX | NEIGH_ATTR_DST | NEIGH_ATTR_FAMILY), + .oo_id_attrs_get = neigh_id_attrs_get +}; + +static struct nl_af_group neigh_groups[] = { + { AF_UNSPEC, RTNLGRP_NEIGH }, + { AF_BRIDGE, RTNLGRP_NEIGH }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops rtnl_neigh_ops = { + .co_name = "route/neigh", + .co_hdrsize = sizeof(struct ndmsg), + .co_msgtypes = { + { RTM_NEWNEIGH, NL_ACT_NEW, "new" }, + { RTM_DELNEIGH, NL_ACT_DEL, "del" }, + { RTM_GETNEIGH, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = neigh_groups, + .co_request_update = neigh_request_update, + .co_msg_parser = neigh_msg_parser, + .co_obj_ops = &neigh_obj_ops, +}; + +static void _nl_init neigh_init(void) +{ + nl_cache_mngt_register(&rtnl_neigh_ops); +} + +static void _nl_exit neigh_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_neigh_ops); +} + +/** @} */ diff --git a/libnl/lib/route/neightbl.c b/libnl/lib/route/neightbl.c new file mode 100644 index 0000000..8d5db8a --- /dev/null +++ b/libnl/lib/route/neightbl.c @@ -0,0 +1,933 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup neightbl Neighbour Tables + * @brief + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" + +struct rtnl_neightbl_parms { + /** + * Interface index of the device this parameter set is assigned + * to or 0 for the default set. + */ + uint32_t ntp_ifindex; + + /** + * Number of references to this parameter set. + */ + uint32_t ntp_refcnt; + + /** + * Queue length for pending arp requests, i.e. the number of + * packets which are accepted from other layers while the + * neighbour address is still being resolved + */ + uint32_t ntp_queue_len; + + /** + * Number of requests to send to the user level ARP daemon. + * Specify 0 to disable. + */ + uint32_t ntp_app_probes; + + /** + * Maximum number of retries for unicast solicitation. + */ + uint32_t ntp_ucast_probes; + + /** + * Maximum number of retries for multicast solicitation. + */ + uint32_t ntp_mcast_probes; + + /** + * Base value in milliseconds to ompute reachable time, see RFC2461. + */ + uint64_t ntp_base_reachable_time; + + /** + * Actual reachable time (read-only) + */ + uint64_t ntp_reachable_time; /* secs */ + + /** + * The time in milliseconds between retransmitted Neighbor + * Solicitation messages. + */ + uint64_t ntp_retrans_time; + + /** + * Interval in milliseconds to check for stale neighbour + * entries. + */ + uint64_t ntp_gc_stale_time; /* secs */ + + /** + * Delay in milliseconds for the first time probe if + * the neighbour is reachable. + */ + uint64_t ntp_probe_delay; /* secs */ + + /** + * Maximum delay in milliseconds of an answer to a neighbour + * solicitation message. + */ + uint64_t ntp_anycast_delay; + + /** + * Minimum age in milliseconds before a neighbour entry + * may be replaced. + */ + uint64_t ntp_locktime; + + /** + * Delay in milliseconds before answering to an ARP request + * for which a proxy ARP entry exists. + */ + uint64_t ntp_proxy_delay; + + /** + * Queue length for the delayed proxy arp requests. + */ + uint32_t ntp_proxy_qlen; + + /** + * Mask of available parameter attributes + */ + uint32_t ntp_mask; +}; + +#define NTBLNAMSIZ 32 + +/** + * Neighbour table + * @ingroup neightbl + */ +struct rtnl_neightbl { + NLHDR_COMMON + + char nt_name[NTBLNAMSIZ]; + uint32_t nt_family; + uint32_t nt_gc_thresh1; + uint32_t nt_gc_thresh2; + uint32_t nt_gc_thresh3; + uint64_t nt_gc_interval; + struct ndt_config nt_config; + struct rtnl_neightbl_parms nt_parms; + struct ndt_stats nt_stats; +}; + +/** @cond SKIP */ +#define NEIGHTBL_ATTR_FAMILY 0x001 +#define NEIGHTBL_ATTR_STATS 0x002 +#define NEIGHTBL_ATTR_NAME 0x004 +#define NEIGHTBL_ATTR_THRESH1 0x008 +#define NEIGHTBL_ATTR_THRESH2 0x010 +#define NEIGHTBL_ATTR_THRESH3 0x020 +#define NEIGHTBL_ATTR_CONFIG 0x040 +#define NEIGHTBL_ATTR_PARMS 0x080 +#define NEIGHTBL_ATTR_GC_INTERVAL 0x100 + +#define NEIGHTBLPARM_ATTR_IFINDEX 0x0001 +#define NEIGHTBLPARM_ATTR_REFCNT 0x0002 +#define NEIGHTBLPARM_ATTR_QUEUE_LEN 0x0004 +#define NEIGHTBLPARM_ATTR_APP_PROBES 0x0008 +#define NEIGHTBLPARM_ATTR_UCAST_PROBES 0x0010 +#define NEIGHTBLPARM_ATTR_MCAST_PROBES 0x0020 +#define NEIGHTBLPARM_ATTR_PROXY_QLEN 0x0040 +#define NEIGHTBLPARM_ATTR_REACHABLE_TIME 0x0080 +#define NEIGHTBLPARM_ATTR_BASE_REACHABLE_TIME 0x0100 +#define NEIGHTBLPARM_ATTR_RETRANS_TIME 0x0200 +#define NEIGHTBLPARM_ATTR_GC_STALETIME 0x0400 +#define NEIGHTBLPARM_ATTR_DELAY_PROBE_TIME 0x0800 +#define NEIGHTBLPARM_ATTR_ANYCAST_DELAY 0x1000 +#define NEIGHTBLPARM_ATTR_PROXY_DELAY 0x2000 +#define NEIGHTBLPARM_ATTR_LOCKTIME 0x4000 + +static struct nl_cache_ops rtnl_neightbl_ops; +static struct nl_object_ops neightbl_obj_ops; +/** @endcond */ + +static uint64_t neightbl_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct rtnl_neightbl *a = (struct rtnl_neightbl *)_a; + struct rtnl_neightbl *b = (struct rtnl_neightbl *)_b; + uint64_t diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(NEIGHTBL_ATTR_FAMILY, a->nt_family != b->nt_family); + diff |= _DIFF(NEIGHTBL_ATTR_NAME, strcmp(a->nt_name, b->nt_name)); + diff |= _DIFF(NEIGHTBL_ATTR_THRESH1, + a->nt_gc_thresh1 != b->nt_gc_thresh1); + diff |= _DIFF(NEIGHTBL_ATTR_THRESH2, + a->nt_gc_thresh2 != b->nt_gc_thresh2); + diff |= _DIFF(NEIGHTBL_ATTR_THRESH3, + a->nt_gc_thresh3 != b->nt_gc_thresh3); + diff |= _DIFF(NEIGHTBL_ATTR_GC_INTERVAL, + a->nt_gc_interval != b->nt_gc_interval); +#undef _DIFF + + if (!(a->ce_mask & NEIGHTBL_ATTR_PARMS) && + !(b->ce_mask & NEIGHTBL_ATTR_PARMS)) + return diff; + + /* XXX: FIXME: Compare parameter table */ + +#if 0 +#define REQ(F) (fp->ntp_mask & NEIGHTBLPARM_ATTR_##F) +#define AVAIL(F) (op->ntp_mask & NEIGHTBLPARM_ATTR_##F) +#define _C(F, N) (REQ(F) && (!AVAIL(F) || (op->N != fp->N))) + if (_C(IFINDEX, ntp_ifindex) || + _C(QUEUE_LEN, ntp_queue_len) || + _C(APP_PROBES, ntp_app_probes) || + _C(UCAST_PROBES, ntp_ucast_probes) || + _C(MCAST_PROBES, ntp_mcast_probes) || + _C(PROXY_QLEN, ntp_proxy_qlen) || + _C(LOCKTIME, ntp_locktime) || + _C(RETRANS_TIME, ntp_retrans_time) || + _C(BASE_REACHABLE_TIME, ntp_base_reachable_time) || + _C(GC_STALETIME, ntp_gc_stale_time) || + _C(DELAY_PROBE_TIME, ntp_probe_delay) || + _C(ANYCAST_DELAY, ntp_anycast_delay) || + _C(PROXY_DELAY, ntp_proxy_delay)) + return 0; +#undef REQ +#undef AVAIL +#undef _C +#endif + + return diff; +} + +static struct nla_policy neightbl_policy[NDTA_MAX + 1] = { + [NDTA_NAME] = { .type = NLA_STRING, .maxlen = NTBLNAMSIZ }, + [NDTA_THRESH1] = { .type = NLA_U32 }, + [NDTA_THRESH2] = { .type = NLA_U32 }, + [NDTA_THRESH3] = { .type = NLA_U32 }, + [NDTA_GC_INTERVAL] = { .type = NLA_U32 }, + [NDTA_CONFIG] = { .minlen = sizeof(struct ndt_config) }, + [NDTA_STATS] = { .minlen = sizeof(struct ndt_stats) }, + [NDTA_PARMS] = { .type = NLA_NESTED }, +}; + +static int neightbl_msg_parser(struct nl_cache_ops *ops, + struct sockaddr_nl *who, struct nlmsghdr *n, + struct nl_parser_param *pp) +{ + struct rtnl_neightbl *ntbl; + struct nlattr *tb[NDTA_MAX + 1]; + struct rtgenmsg *rtmsg; + int err; + + ntbl = rtnl_neightbl_alloc(); + if (!ntbl) { + err = -NLE_NOMEM; + goto errout; + } + + ntbl->ce_msgtype = n->nlmsg_type; + rtmsg = nlmsg_data(n); + + err = nlmsg_parse(n, sizeof(*rtmsg), tb, NDTA_MAX, neightbl_policy); + if (err < 0) + goto errout; + + ntbl->nt_family = rtmsg->rtgen_family; + + if (tb[NDTA_NAME] == NULL) { + err = -NLE_MISSING_ATTR; + goto errout; + } + + nla_strlcpy(ntbl->nt_name, tb[NDTA_NAME], NTBLNAMSIZ); + ntbl->ce_mask |= NEIGHTBL_ATTR_NAME; + + if (tb[NDTA_THRESH1]) { + ntbl->nt_gc_thresh1 = nla_get_u32(tb[NDTA_THRESH1]); + ntbl->ce_mask |= NEIGHTBL_ATTR_THRESH1; + } + + if (tb[NDTA_THRESH2]) { + ntbl->nt_gc_thresh2 = nla_get_u32(tb[NDTA_THRESH2]); + ntbl->ce_mask |= NEIGHTBL_ATTR_THRESH2; + } + + if (tb[NDTA_THRESH3]) { + ntbl->nt_gc_thresh3 = nla_get_u32(tb[NDTA_THRESH3]); + ntbl->ce_mask |= NEIGHTBL_ATTR_THRESH3; + } + + if (tb[NDTA_GC_INTERVAL]) { + ntbl->nt_gc_interval = nla_get_u32(tb[NDTA_GC_INTERVAL]); + ntbl->ce_mask |= NEIGHTBL_ATTR_GC_INTERVAL; + } + + if (tb[NDTA_CONFIG]) { + nla_memcpy(&ntbl->nt_config, tb[NDTA_CONFIG], + sizeof(ntbl->nt_config)); + ntbl->ce_mask |= NEIGHTBL_ATTR_CONFIG; + } + + if (tb[NDTA_STATS]) { + nla_memcpy(&ntbl->nt_stats, tb[NDTA_STATS], + sizeof(ntbl->nt_stats)); + ntbl->ce_mask |= NEIGHTBL_ATTR_STATS; + } + + if (tb[NDTA_PARMS]) { + struct nlattr *tbp[NDTPA_MAX + 1]; + struct rtnl_neightbl_parms *p = &ntbl->nt_parms; + + err = nla_parse_nested(tbp, NDTPA_MAX, tb[NDTA_PARMS], NULL); + if (err < 0) + goto errout; + +#define COPY_ENTRY(name, var) \ + if (tbp[NDTPA_##name]) { \ + p->ntp_##var = nla_get_u32(tbp[NDTPA_##name]); \ + p->ntp_mask |= NEIGHTBLPARM_ATTR_##name; \ + } + + COPY_ENTRY(IFINDEX, ifindex); + COPY_ENTRY(REFCNT, refcnt); + COPY_ENTRY(QUEUE_LEN, queue_len); + COPY_ENTRY(APP_PROBES, app_probes); + COPY_ENTRY(UCAST_PROBES, ucast_probes); + COPY_ENTRY(MCAST_PROBES, mcast_probes); + COPY_ENTRY(PROXY_QLEN, proxy_qlen); + COPY_ENTRY(PROXY_DELAY, proxy_delay); + COPY_ENTRY(ANYCAST_DELAY, anycast_delay); + COPY_ENTRY(LOCKTIME, locktime); + COPY_ENTRY(REACHABLE_TIME, reachable_time); + COPY_ENTRY(BASE_REACHABLE_TIME, base_reachable_time); + COPY_ENTRY(RETRANS_TIME, retrans_time); + COPY_ENTRY(GC_STALETIME, gc_stale_time); + COPY_ENTRY(DELAY_PROBE_TIME, probe_delay); +#undef COPY_ENTRY + + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; + } + + err = pp->pp_cb((struct nl_object *)ntbl, pp); +errout: + rtnl_neightbl_put(ntbl); + return err; +} + +static int neightbl_request_update(struct nl_cache *c, struct nl_sock *h) +{ + return nl_rtgen_request(h, RTM_GETNEIGHTBL, AF_UNSPEC, NLM_F_DUMP); +} + +static void neightbl_dump_line(struct nl_object *arg, struct nl_dump_params *p) +{ + struct rtnl_neightbl *ntbl = (struct rtnl_neightbl *)arg; + + nl_dump_line(p, "%s", ntbl->nt_name); + + if (ntbl->nt_parms.ntp_mask & NEIGHTBLPARM_ATTR_IFINDEX) { + struct nl_cache *link_cache; + + link_cache = nl_cache_mngt_require_safe("route/link"); + + if (link_cache) { + char buf[32]; + nl_dump(p, "<%s> ", + rtnl_link_i2name(link_cache, + ntbl->nt_parms.ntp_ifindex, + buf, sizeof(buf))); + nl_cache_put(link_cache); + } else + nl_dump(p, "<%u> ", ntbl->nt_parms.ntp_ifindex); + } else + nl_dump(p, " "); + + if (ntbl->ce_mask & NEIGHTBL_ATTR_CONFIG) + nl_dump(p, "entries %u ", ntbl->nt_config.ndtc_entries); + + if (ntbl->ce_mask & NEIGHTBL_ATTR_PARMS) { + char rt[32], rt2[32]; + struct rtnl_neightbl_parms *pa = &ntbl->nt_parms; + + nl_dump(p, "reachable-time %s retransmit-time %s", + nl_msec2str(pa->ntp_reachable_time, rt, sizeof(rt)), + nl_msec2str(pa->ntp_retrans_time, rt2, sizeof(rt2))); + } + + nl_dump(p, "\n"); +} + +static void neightbl_dump_details(struct nl_object *arg, + struct nl_dump_params *p) +{ + char x[32], y[32], z[32]; + struct rtnl_neightbl *ntbl = (struct rtnl_neightbl *)arg; + + neightbl_dump_line(arg, p); + + if (ntbl->ce_mask & NEIGHTBL_ATTR_CONFIG) { + nl_dump_line(p, " key-len %u entry-size %u last-flush %s\n", + ntbl->nt_config.ndtc_key_len, + ntbl->nt_config.ndtc_entry_size, + nl_msec2str(ntbl->nt_config.ndtc_last_flush, x, + sizeof(x))); + + nl_dump_line(p, + " gc threshold %u/%u/%u interval %s " + "chain-position %u\n", + ntbl->nt_gc_thresh1, ntbl->nt_gc_thresh2, + ntbl->nt_gc_thresh3, + nl_msec2str(ntbl->nt_gc_interval, x, sizeof(x)), + ntbl->nt_config.ndtc_hash_chain_gc); + + nl_dump_line(p, " hash-rand 0x%08X/0x%08X last-rand %s\n", + ntbl->nt_config.ndtc_hash_rnd, + ntbl->nt_config.ndtc_hash_mask, + nl_msec2str(ntbl->nt_config.ndtc_last_rand, x, + sizeof(x))); + } + + if (ntbl->ce_mask & NEIGHTBL_ATTR_PARMS) { + struct rtnl_neightbl_parms *pa = &ntbl->nt_parms; + + nl_dump_line(p, + " refcnt %u pending-queue-limit %u " + "proxy-delayed-queue-limit %u\n", + pa->ntp_refcnt, pa->ntp_queue_len, + pa->ntp_proxy_qlen); + + nl_dump_line(p, + " num-userspace-probes %u num-unicast-probes " + "%u num-multicast-probes %u\n", + pa->ntp_app_probes, pa->ntp_ucast_probes, + pa->ntp_mcast_probes); + + nl_dump_line(p, + " min-age %s base-reachable-time %s " + "stale-check-interval %s\n", + nl_msec2str(pa->ntp_locktime, x, sizeof(x)), + nl_msec2str(pa->ntp_base_reachable_time, y, + sizeof(y)), + nl_msec2str(pa->ntp_gc_stale_time, z, sizeof(z))); + + nl_dump_line(p, + " initial-probe-delay %s answer-delay %s " + "proxy-answer-delay %s\n", + nl_msec2str(pa->ntp_probe_delay, x, sizeof(x)), + nl_msec2str(pa->ntp_anycast_delay, y, sizeof(y)), + nl_msec2str(pa->ntp_proxy_delay, z, sizeof(z))); + } +} + +static void neightbl_dump_stats(struct nl_object *arg, struct nl_dump_params *p) +{ + struct rtnl_neightbl *ntbl = (struct rtnl_neightbl *)arg; + + neightbl_dump_details(arg, p); + + if (!(ntbl->ce_mask & NEIGHTBL_ATTR_STATS)) + return; + + nl_dump_line(p, + " " + " lookups %llu hits %llu failed %llu" + " allocations %llu destroys %llu\n", + (long long unsigned)ntbl->nt_stats.ndts_lookups, + (long long unsigned)ntbl->nt_stats.ndts_hits, + (long long unsigned)ntbl->nt_stats.ndts_res_failed, + (long long unsigned)ntbl->nt_stats.ndts_allocs, + (long long unsigned)ntbl->nt_stats.ndts_destroys); + + nl_dump_line(p, + " " + " hash-grows %llu forced-gc-runs %llu" + " periodic-gc-runs %llu\n", + (long long unsigned)ntbl->nt_stats.ndts_hash_grows, + (long long unsigned)ntbl->nt_stats.ndts_forced_gc_runs, + (long long unsigned)ntbl->nt_stats.ndts_periodic_gc_runs); + + nl_dump_line(p, + " " + " rcv-unicast-probes %llu" + " rcv-multicast-probes %llu" + "\n", + (long long unsigned)ntbl->nt_stats.ndts_rcv_probes_ucast, + (long long unsigned)ntbl->nt_stats.ndts_rcv_probes_mcast); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_neightbl *rtnl_neightbl_alloc(void) +{ + return (struct rtnl_neightbl *)nl_object_alloc(&neightbl_obj_ops); +} + +void rtnl_neightbl_put(struct rtnl_neightbl *neightbl) +{ + nl_object_put((struct nl_object *)neightbl); +} + +/** @} */ + +/** + * @name Neighbour Table Cache Management + * @{ + */ + +/** + * Build a neighbour table cache including all neighbour tables currently configured in the kernel. + * @arg sk Netlink socket. + * @arg result Pointer to store resulting cache. + * + * Allocates a new neighbour table cache, initializes it properly and + * updates it to include all neighbour tables currently configured in + * the kernel. + * + * @return 0 on success or a negative error code. + */ +int rtnl_neightbl_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&rtnl_neightbl_ops, sk, result); +} + +/** + * Lookup neighbour table by name and optional interface index + * @arg cache neighbour table cache + * @arg name name of table + * @arg ifindex optional interface index + * + * Looks up the neighbour table matching the specified name and + * optionally the specified ifindex to retrieve device specific + * parameter sets. + * + * @return ptr to neighbour table inside the cache or NULL if no + * match was found. + */ +struct rtnl_neightbl *rtnl_neightbl_get(struct nl_cache *cache, + const char *name, int ifindex) +{ + struct rtnl_neightbl *nt; + + if (cache->c_ops != &rtnl_neightbl_ops) + return NULL; + + nl_list_for_each_entry(nt, &cache->c_items, ce_list) { + if (!strcasecmp(nt->nt_name, name) && + ((unsigned)ifindex) == nt->nt_parms.ntp_ifindex) { + nl_object_get((struct nl_object *)nt); + return nt; + } + } + + return NULL; +} + +/** @} */ + +/** + * @name Neighbour Table Modifications + * @{ + */ + +/** + * Builds a netlink change request message to change neighbour table attributes + * @arg old neighbour table to change + * @arg tmpl template with requested changes + * @arg result Pointer to store resulting message. + * + * Builds a new netlink message requesting a change of neighbour table + * attributes. The netlink message header isn't fully equipped with all + * relevant fields and must be sent out via nl_send_auto_complete() or + * supplemented as needed. + * \a old must point to a neighbour table currently configured in the + * kernel and \a tmpl must contain the attributes to be changed set via + * \c rtnl_neightbl_set_* functions. + * + * @return 0 on success or a negative error code. + */ +int rtnl_neightbl_build_change_request(struct rtnl_neightbl *old, + struct rtnl_neightbl *tmpl, + struct nl_msg **result) +{ + struct nl_msg *m, *parms = NULL; + struct ndtmsg ndt = { + .ndtm_family = old->nt_family, + }; + + m = nlmsg_alloc_simple(RTM_SETNEIGHTBL, 0); + if (!m) + return -NLE_NOMEM; + + if (nlmsg_append(m, &ndt, sizeof(ndt), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + NLA_PUT_STRING(m, NDTA_NAME, old->nt_name); + + if (tmpl->ce_mask & NEIGHTBL_ATTR_THRESH1) + NLA_PUT_U32(m, NDTA_THRESH1, tmpl->nt_gc_thresh1); + + if (tmpl->ce_mask & NEIGHTBL_ATTR_THRESH2) + NLA_PUT_U32(m, NDTA_THRESH2, tmpl->nt_gc_thresh2); + + if (tmpl->ce_mask & NEIGHTBL_ATTR_THRESH2) + NLA_PUT_U32(m, NDTA_THRESH2, tmpl->nt_gc_thresh2); + + if (tmpl->ce_mask & NEIGHTBL_ATTR_GC_INTERVAL) + NLA_PUT_U64(m, NDTA_GC_INTERVAL, tmpl->nt_gc_interval); + + if (tmpl->ce_mask & NEIGHTBL_ATTR_PARMS) { + struct rtnl_neightbl_parms *p = &tmpl->nt_parms; + + parms = nlmsg_alloc(); + if (!parms) + goto nla_put_failure; + + if (old->nt_parms.ntp_mask & NEIGHTBLPARM_ATTR_IFINDEX) + NLA_PUT_U32(parms, NDTPA_IFINDEX, + old->nt_parms.ntp_ifindex); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_QUEUE_LEN) + NLA_PUT_U32(parms, NDTPA_QUEUE_LEN, p->ntp_queue_len); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_APP_PROBES) + NLA_PUT_U32(parms, NDTPA_APP_PROBES, p->ntp_app_probes); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_UCAST_PROBES) + NLA_PUT_U32(parms, NDTPA_UCAST_PROBES, + p->ntp_ucast_probes); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_MCAST_PROBES) + NLA_PUT_U32(parms, NDTPA_MCAST_PROBES, + p->ntp_mcast_probes); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_PROXY_QLEN) + NLA_PUT_U32(parms, NDTPA_PROXY_QLEN, p->ntp_proxy_qlen); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_BASE_REACHABLE_TIME) + NLA_PUT_U64(parms, NDTPA_BASE_REACHABLE_TIME, + p->ntp_base_reachable_time); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_RETRANS_TIME) + NLA_PUT_U64(parms, NDTPA_RETRANS_TIME, + p->ntp_retrans_time); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_GC_STALETIME) + NLA_PUT_U64(parms, NDTPA_GC_STALETIME, + p->ntp_gc_stale_time); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_DELAY_PROBE_TIME) + NLA_PUT_U64(parms, NDTPA_DELAY_PROBE_TIME, + p->ntp_proxy_delay); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_ANYCAST_DELAY) + NLA_PUT_U64(parms, NDTPA_ANYCAST_DELAY, + p->ntp_anycast_delay); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_PROXY_DELAY) + NLA_PUT_U64(parms, NDTPA_PROXY_DELAY, + p->ntp_proxy_delay); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_LOCKTIME) + NLA_PUT_U64(parms, NDTPA_LOCKTIME, p->ntp_locktime); + + if (nla_put_nested(m, NDTA_PARMS, parms) < 0) + goto nla_put_failure; + + nlmsg_free(parms); + } + + *result = m; + return 0; + +nla_put_failure: + if (parms) + nlmsg_free(parms); + nlmsg_free(m); + return -NLE_MSGSIZE; +} + +/** + * Change neighbour table attributes + * @arg sk Netlink socket. + * @arg old neighbour table to be changed + * @arg tmpl template with requested changes + * + * Builds a new netlink message by calling + * rtnl_neightbl_build_change_request(), sends the request to the + * kernel and waits for the next ACK to be received, i.e. blocks + * until the request has been processed. + * + * @return 0 on success or a negative error code + */ +int rtnl_neightbl_change(struct nl_sock *sk, struct rtnl_neightbl *old, + struct rtnl_neightbl *tmpl) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_neightbl_build_change_request(old, tmpl, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** @} */ + +/** + * @name Attribute Modification + * @{ + */ + +void rtnl_neightbl_set_family(struct rtnl_neightbl *ntbl, int family) +{ + ntbl->nt_family = family; + ntbl->ce_mask |= NEIGHTBL_ATTR_FAMILY; +} + +void rtnl_neightbl_set_gc_interval(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_gc_interval = ms; + ntbl->ce_mask |= NEIGHTBL_ATTR_GC_INTERVAL; +} + +void rtnl_neightbl_set_gc_tresh1(struct rtnl_neightbl *ntbl, int thresh) +{ + ntbl->nt_gc_thresh1 = thresh; + ntbl->ce_mask |= NEIGHTBL_ATTR_THRESH1; +} + +void rtnl_neightbl_set_gc_tresh2(struct rtnl_neightbl *ntbl, int thresh) +{ + ntbl->nt_gc_thresh2 = thresh; + ntbl->ce_mask |= NEIGHTBL_ATTR_THRESH2; +} + +void rtnl_neightbl_set_gc_tresh3(struct rtnl_neightbl *ntbl, int thresh) +{ + ntbl->nt_gc_thresh3 = thresh; + ntbl->ce_mask |= NEIGHTBL_ATTR_THRESH3; +} + +void rtnl_neightbl_set_name(struct rtnl_neightbl *ntbl, const char *name) +{ + _nl_strncpy_trunc(ntbl->nt_name, name, sizeof(ntbl->nt_name)); + ntbl->ce_mask |= NEIGHTBL_ATTR_NAME; +} + +void rtnl_neightbl_set_dev(struct rtnl_neightbl *ntbl, int ifindex) +{ + ntbl->nt_parms.ntp_ifindex = ifindex; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_IFINDEX; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the queue length for pending requests of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg len new queue len + */ +void rtnl_neightbl_set_queue_len(struct rtnl_neightbl *ntbl, int len) +{ + ntbl->nt_parms.ntp_queue_len = len; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_QUEUE_LEN; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the queue length for delay proxy arp requests of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg len new queue len + */ +void rtnl_neightbl_set_proxy_queue_len(struct rtnl_neightbl *ntbl, int len) +{ + ntbl->nt_parms.ntp_proxy_qlen = len; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_PROXY_QLEN; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the number of application probes of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg probes new probes value + */ +void rtnl_neightbl_set_app_probes(struct rtnl_neightbl *ntbl, int probes) +{ + ntbl->nt_parms.ntp_app_probes = probes; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_APP_PROBES; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the number of unicast probes of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg probes new probes value + */ +void rtnl_neightbl_set_ucast_probes(struct rtnl_neightbl *ntbl, int probes) +{ + ntbl->nt_parms.ntp_ucast_probes = probes; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_UCAST_PROBES; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the number of multicast probes of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg probes new probes value + */ +void rtnl_neightbl_set_mcast_probes(struct rtnl_neightbl *ntbl, int probes) +{ + ntbl->nt_parms.ntp_mcast_probes = probes; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_MCAST_PROBES; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the base reachable time of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new base reachable time in milliseconds + */ +void rtnl_neightbl_set_base_reachable_time(struct rtnl_neightbl *ntbl, + uint64_t ms) +{ + ntbl->nt_parms.ntp_base_reachable_time = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_BASE_REACHABLE_TIME; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the retransmit time of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new retransmit time + */ +void rtnl_neightbl_set_retrans_time(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_parms.ntp_retrans_time = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_RETRANS_TIME; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the gc stale time of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new gc stale time in milliseconds + */ +void rtnl_neightbl_set_gc_stale_time(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_parms.ntp_gc_stale_time = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_GC_STALETIME; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the first probe delay time of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new first probe delay time in milliseconds + */ +void rtnl_neightbl_set_delay_probe_time(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_parms.ntp_probe_delay = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_DELAY_PROBE_TIME; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the anycast delay of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new anycast delay in milliseconds + */ +void rtnl_neightbl_set_anycast_delay(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_parms.ntp_anycast_delay = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_ANYCAST_DELAY; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the proxy delay of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new proxy delay in milliseconds + */ +void rtnl_neightbl_set_proxy_delay(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_parms.ntp_proxy_delay = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_PROXY_DELAY; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the locktime of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new locktime in milliseconds + */ +void rtnl_neightbl_set_locktime(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_parms.ntp_locktime = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_LOCKTIME; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** @} */ + +static struct nl_object_ops neightbl_obj_ops = { + .oo_name = "route/neightbl", + .oo_size = sizeof(struct rtnl_neightbl), + .oo_dump = { + [NL_DUMP_LINE] = neightbl_dump_line, + [NL_DUMP_DETAILS] = neightbl_dump_details, + [NL_DUMP_STATS] = neightbl_dump_stats, + }, + .oo_compare = neightbl_compare, +}; + +static struct nl_cache_ops rtnl_neightbl_ops = { + .co_name = "route/neightbl", + .co_hdrsize = sizeof(struct rtgenmsg), + .co_msgtypes = { + { RTM_NEWNEIGHTBL, NL_ACT_NEW, "new" }, + { RTM_SETNEIGHTBL, NL_ACT_SET, "set" }, + { RTM_GETNEIGHTBL, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_request_update = neightbl_request_update, + .co_msg_parser = neightbl_msg_parser, + .co_obj_ops = &neightbl_obj_ops, +}; + +static void _nl_init neightbl_init(void) +{ + nl_cache_mngt_register(&rtnl_neightbl_ops); +} + +static void _nl_exit neightbl_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_neightbl_ops); +} + +/** @} */ diff --git a/libnl/lib/route/netconf.c b/libnl/lib/route/netconf.c new file mode 100644 index 0000000..7863da6 --- /dev/null +++ b/libnl/lib/route/netconf.c @@ -0,0 +1,573 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2017 David Ahern + */ + +/** + * @ingroup rtnl + * @defgroup netconf Netconf + * @brief + * + * @{ + */ + +#include "nl-default.h" + +#include +#include + +#include +#include +#include +#include + +#include "nl-route.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" +#include "nl-priv-dynamic-core/object-api.h" + +/** @cond SKIP */ +#define NETCONF_ATTR_FAMILY 0x0001 +#define NETCONF_ATTR_IFINDEX 0x0002 +#define NETCONF_ATTR_RP_FILTER 0x0004 +#define NETCONF_ATTR_FWDING 0x0008 +#define NETCONF_ATTR_MC_FWDING 0x0010 +#define NETCONF_ATTR_PROXY_NEIGH 0x0020 +#define NETCONF_ATTR_IGNORE_RT_LINKDWN 0x0040 +#define NETCONF_ATTR_INPUT 0x0080 + +struct rtnl_netconf +{ + NLHDR_COMMON + + int family; + int ifindex; + int rp_filter; + int forwarding; + int mc_forwarding; + int proxy_neigh; + int ignore_routes_linkdown; + int input; +}; + +static struct nl_cache_ops rtnl_netconf_ops; +static struct nl_object_ops netconf_obj_ops; +/** @endcond */ + +static struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = { + [NETCONFA_IFINDEX] = { .type = NLA_S32 }, + [NETCONFA_FORWARDING] = { .type = NLA_S32 }, + [NETCONFA_MC_FORWARDING] = { .type = NLA_S32 }, + [NETCONFA_RP_FILTER] = { .type = NLA_S32 }, + [NETCONFA_PROXY_NEIGH] = { .type = NLA_S32 }, + [NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN] = { .type = NLA_S32 }, +}; + +static struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = { + [NETCONFA_IFINDEX] = { .type = NLA_S32 }, + [NETCONFA_FORWARDING] = { .type = NLA_S32 }, + [NETCONFA_MC_FORWARDING] = { .type = NLA_S32 }, + [NETCONFA_PROXY_NEIGH] = { .type = NLA_S32 }, + [NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN] = { .type = NLA_S32 }, +}; + +static struct nla_policy devconf_mpls_policy[NETCONFA_MAX+1] = { + [NETCONFA_IFINDEX] = { .type = NLA_S32 }, + [NETCONFA_INPUT] = { .type = NLA_S32 }, +}; + +static struct rtnl_netconf *rtnl_netconf_alloc(void) +{ + return (struct rtnl_netconf *) nl_object_alloc(&netconf_obj_ops); +} + +static int netconf_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct nlattr *tb[NETCONFA_MAX+1], *attr; + struct rtnl_netconf *nc; + struct netconfmsg *ncm; + int err; + + ncm = nlmsg_data(nlh); + switch (ncm->ncm_family) { + case AF_INET: + err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, + devconf_ipv4_policy); + if (err < 0) + return err; + break; + case AF_INET6: + err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, + devconf_ipv6_policy); + if (err < 0) + return err; + break; + case AF_MPLS: + err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, + devconf_mpls_policy); + if (err < 0) + return err; + break; + default: + printf("unexpected netconf family: %d\n", ncm->ncm_family); + return -1; + } + + if (!tb[NETCONFA_IFINDEX]) + return -1; + + nc = rtnl_netconf_alloc(); + if (!nc) + return -NLE_NOMEM; + + nc->ce_msgtype = nlh->nlmsg_type; + nc->family = ncm->ncm_family; + nc->ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); + + nc->ce_mask = NETCONF_ATTR_FAMILY | NETCONF_ATTR_IFINDEX; + + + if (tb[NETCONFA_RP_FILTER]) { + attr = tb[NETCONFA_RP_FILTER]; + nc->rp_filter = nla_get_s32(attr); + nc->ce_mask |= NETCONF_ATTR_RP_FILTER; + } + + if (tb[NETCONFA_FORWARDING]) { + attr = tb[NETCONFA_FORWARDING]; + nc->forwarding = nla_get_s32(attr); + nc->ce_mask |= NETCONF_ATTR_FWDING; + } + + if (tb[NETCONFA_MC_FORWARDING]) { + attr = tb[NETCONFA_MC_FORWARDING]; + nc->mc_forwarding = nla_get_s32(attr); + nc->ce_mask |= NETCONF_ATTR_MC_FWDING; + } + + if (tb[NETCONFA_PROXY_NEIGH]) { + attr = tb[NETCONFA_PROXY_NEIGH]; + nc->proxy_neigh = nla_get_s32(attr); + nc->ce_mask |= NETCONF_ATTR_PROXY_NEIGH; + } + + if (tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]) { + attr = tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]; + nc->ignore_routes_linkdown = nla_get_s32(attr); + nc->ce_mask |= NETCONF_ATTR_IGNORE_RT_LINKDWN; + } + + if (tb[NETCONFA_INPUT]) { + attr = tb[NETCONFA_INPUT]; + nc->input = nla_get_s32(attr); + nc->ce_mask |= NETCONF_ATTR_INPUT; + } + + err = pp->pp_cb((struct nl_object *) nc, pp); + + rtnl_netconf_put(nc); + return err; +} + +static int netconf_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + struct netconfmsg nc = { + .ncm_family = cache->c_iarg1, + }; + + return nl_send_simple(sk, RTM_GETNETCONF, NLM_F_DUMP, &nc, sizeof(nc)); +} + +static void netconf_dump_line(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_netconf *nc = (struct rtnl_netconf *) obj; + struct nl_cache *link_cache; + char buf[64]; + + switch(nc->family) { + case AF_INET: + nl_dump(p, "ipv4 "); + break; + case AF_INET6: + nl_dump(p, "ipv6 "); + break; + case AF_MPLS: + nl_dump(p, "mpls "); + break; + default: + return; + } + + switch(nc->ifindex) { + case NETCONFA_IFINDEX_ALL: + nl_dump(p, "all "); + break; + case NETCONFA_IFINDEX_DEFAULT: + nl_dump(p, "default "); + break; + default: + link_cache = nl_cache_mngt_require_safe("route/link"); + if (link_cache) { + nl_dump(p, "dev %s ", + rtnl_link_i2name(link_cache, nc->ifindex, + buf, sizeof(buf))); + nl_cache_put(link_cache); + } else + nl_dump(p, "dev %d ", nc->ifindex); + } + + if (nc->ce_mask & NETCONF_ATTR_FWDING) { + nl_dump(p, "forwarding %s ", + nc->forwarding ? "on" : "off"); + } + + if (nc->ce_mask & NETCONF_ATTR_RP_FILTER) { + if (nc->rp_filter == 0) + nl_dump(p, "rp_filter off "); + else if (nc->rp_filter == 1) + nl_dump(p, "rp_filter strict "); + else if (nc->rp_filter == 2) + nl_dump(p, "rp_filter loose "); + else + nl_dump(p, "rp_filter unknown-mode "); + } + + if (nc->ce_mask & NETCONF_ATTR_MC_FWDING) { + nl_dump(p, "mc_forwarding %s ", + nc->mc_forwarding ? "on" : "off"); + } + + if (nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH) + nl_dump(p, "proxy_neigh %d ", nc->proxy_neigh); + + if (nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN) { + nl_dump(p, "ignore_routes_with_linkdown %s ", + nc->ignore_routes_linkdown ? "on" : "off"); + } + + if (nc->ce_mask & NETCONF_ATTR_INPUT) + nl_dump(p, "input %s ", nc->input ? "on" : "off"); + + nl_dump(p, "\n"); +} + +static const struct trans_tbl netconf_attrs[] = { + __ADD(NETCONF_ATTR_FAMILY, family), + __ADD(NETCONF_ATTR_IFINDEX, ifindex), + __ADD(NETCONF_ATTR_RP_FILTER, rp_filter), + __ADD(NETCONF_ATTR_FWDING, forwarding), + __ADD(NETCONF_ATTR_MC_FWDING, mc_forwarding), + __ADD(NETCONF_ATTR_PROXY_NEIGH, proxy_neigh), + __ADD(NETCONF_ATTR_IGNORE_RT_LINKDWN, ignore_routes_with_linkdown), + __ADD(NETCONF_ATTR_INPUT, input), +}; + +static char *netconf_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, netconf_attrs, + ARRAY_SIZE(netconf_attrs)); +} + +static void netconf_keygen(struct nl_object *obj, uint32_t *hashkey, + uint32_t table_sz) +{ + struct rtnl_netconf *nc = (struct rtnl_netconf *) obj; + unsigned int nckey_sz; + struct nc_hash_key { + int nc_family; + int nc_index; + } _nl_packed nckey; + + nckey_sz = sizeof(nckey); + nckey.nc_family = nc->family; + nckey.nc_index = nc->ifindex; + + *hashkey = nl_hash(&nckey, nckey_sz, 0) % table_sz; + + NL_DBG(5, "netconf %p key (dev %d fam %d) keysz %d, hash 0x%x\n", + nc, nckey.nc_index, nckey.nc_family, nckey_sz, *hashkey); +} + +static uint64_t netconf_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct rtnl_netconf *a = (struct rtnl_netconf *) _a; + struct rtnl_netconf *b = (struct rtnl_netconf *) _b; + uint64_t diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(NETCONF_ATTR_FAMILY, a->family != b->family); + diff |= _DIFF(NETCONF_ATTR_IFINDEX, a->ifindex != b->ifindex); + diff |= _DIFF(NETCONF_ATTR_RP_FILTER, a->rp_filter != b->rp_filter); + diff |= _DIFF(NETCONF_ATTR_FWDING, a->forwarding != b->forwarding); + diff |= _DIFF(NETCONF_ATTR_MC_FWDING, + a->mc_forwarding != b->mc_forwarding); + diff |= _DIFF(NETCONF_ATTR_PROXY_NEIGH, + a->proxy_neigh != b->proxy_neigh); + diff |= _DIFF(NETCONF_ATTR_IGNORE_RT_LINKDWN, + a->ignore_routes_linkdown != b->ignore_routes_linkdown); + diff |= _DIFF(NETCONF_ATTR_INPUT, a->input != b->input); +#undef _DIFF + + return diff; +} + +static int netconf_update(struct nl_object *old_obj, struct nl_object *new_obj) +{ + struct rtnl_netconf *new_nc = (struct rtnl_netconf *) new_obj; + struct rtnl_netconf *old_nc = (struct rtnl_netconf *) old_obj; + int action = new_obj->ce_msgtype; + + switch(action) { + case RTM_NEWNETCONF: + if (new_nc->family != old_nc->family || + new_nc->ifindex != old_nc->ifindex) + return -NLE_OPNOTSUPP; + + if (new_nc->ce_mask & NETCONF_ATTR_RP_FILTER) + old_nc->rp_filter = new_nc->rp_filter; + if (new_nc->ce_mask & NETCONF_ATTR_FWDING) + old_nc->forwarding = new_nc->forwarding; + if (new_nc->ce_mask & NETCONF_ATTR_MC_FWDING) + old_nc->mc_forwarding = new_nc->mc_forwarding; + if (new_nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH) + old_nc->proxy_neigh = new_nc->proxy_neigh; + if (new_nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN) + old_nc->ignore_routes_linkdown = new_nc->ignore_routes_linkdown; + + break; + default: + return -NLE_OPNOTSUPP; + } + + return NLE_SUCCESS; +} + +/** + * @name Cache Management + * @{ + */ + +int rtnl_netconf_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&rtnl_netconf_ops, sk, result); +} + +/** + * Search netconf in cache + * @arg cache netconf cache + * @arg family Address family of interest + * @arg ifindex Interface index of interest + * + * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache() + * for given index and family + * + * The reference counter is incremented before returning the netconf entry, + * therefore the reference must be given back with rtnl_netconf_put() after + * usage. + * + * @return netconf object or NULL if no match was found. + */ +struct rtnl_netconf *rtnl_netconf_get_by_idx(struct nl_cache *cache, int family, + int ifindex) +{ + struct rtnl_netconf *nc; + + if (!ifindex || !family || cache->c_ops != &rtnl_netconf_ops) + return NULL; + + nl_list_for_each_entry(nc, &cache->c_items, ce_list) { + if (nc->ifindex == ifindex && + nc->family == family) { + nl_object_get((struct nl_object *) nc); + return nc; + } + } + + return NULL; +} + +void rtnl_netconf_put(struct rtnl_netconf *nc) +{ + nl_object_put((struct nl_object *) nc); +} + +/** + * Search netconf in cache + * @arg cache netconf cache + * @arg family Address family of interest + * + * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache() + * for "all" netconf settings for given family + * + * The reference counter is incremented before returning the netconf entry, + * therefore the reference must be given back with rtnl_netconf_put() after + * usage. + * + * @return netconf object or NULL if no match was found. + */ +struct rtnl_netconf *rtnl_netconf_get_all(struct nl_cache *cache, int family) +{ + return rtnl_netconf_get_by_idx(cache, family, NETCONFA_IFINDEX_ALL); +} + +/** + * Search netconf in cache + * @arg cache netconf cache + * @arg family Address family of interest + * + * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache() + * for "default" netconf settings for given family + * + * The reference counter is incremented before returning the netconf entry, + * therefore the reference must be given back with rtnl_netconf_put() after + * usage. + * + * @return netconf object or NULL if no match was found. + */ +struct rtnl_netconf *rtnl_netconf_get_default(struct nl_cache *cache, int family) +{ + return rtnl_netconf_get_by_idx(cache, family, NETCONFA_IFINDEX_DEFAULT); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +int rtnl_netconf_get_family(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_FAMILY)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->family; + return 0; +} +int rtnl_netconf_get_ifindex(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_IFINDEX)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->ifindex; + return 0; +} +int rtnl_netconf_get_forwarding(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_FWDING)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->forwarding; + return 0; +} +int rtnl_netconf_get_mc_forwarding(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_MC_FWDING)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->mc_forwarding; + return 0; +} +int rtnl_netconf_get_rp_filter(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_RP_FILTER)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->rp_filter; + return 0; +} +int rtnl_netconf_get_proxy_neigh(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->proxy_neigh; + return 0; +} +int rtnl_netconf_get_ignore_routes_linkdown(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->ignore_routes_linkdown; + return 0; +} +int rtnl_netconf_get_input(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_INPUT)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->input; + return 0; +} + + +/** @} */ + +static struct nl_object_ops netconf_obj_ops = { + .oo_name = "route/netconf", + .oo_size = sizeof(struct rtnl_netconf), + .oo_dump = { + [NL_DUMP_LINE] = netconf_dump_line, + [NL_DUMP_DETAILS] = netconf_dump_line, + }, + .oo_compare = netconf_compare, + .oo_keygen = netconf_keygen, + .oo_update = netconf_update, + .oo_attrs2str = netconf_attrs2str, + .oo_id_attrs = (NETCONF_ATTR_FAMILY | + NETCONF_ATTR_IFINDEX) +}; + +static struct nl_af_group netconf_groups[] = { + { AF_INET, RTNLGRP_IPV4_NETCONF }, + { AF_INET6, RTNLGRP_IPV6_NETCONF }, + { AF_MPLS, RTNLGRP_MPLS_NETCONF }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops rtnl_netconf_ops = { + .co_name = "route/netconf", + .co_hdrsize = sizeof(struct netconfmsg), + .co_msgtypes = { + { RTM_NEWNETCONF, NL_ACT_NEW, "new" }, + { RTM_DELNETCONF, NL_ACT_DEL, "del" }, + { RTM_GETNETCONF, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = netconf_groups, + .co_request_update = netconf_request_update, + .co_msg_parser = netconf_msg_parser, + .co_obj_ops = &netconf_obj_ops, +}; + +static void _nl_init netconf_init(void) +{ + nl_cache_mngt_register(&rtnl_netconf_ops); +} + +static void _nl_exit netconf_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_netconf_ops); +} + +/** @} */ diff --git a/libnl/lib/route/nexthop-encap.h b/libnl/lib/route/nexthop-encap.h new file mode 100644 index 0000000..dde1bfb --- /dev/null +++ b/libnl/lib/route/nexthop-encap.h @@ -0,0 +1,35 @@ +#ifndef NETLINK_NEXTHOP_ENCAP_H_ +#define NETLINK_NEXTHOP_ENCAP_H_ + +struct nh_encap_ops { + uint16_t encap_type; + + int (*build_msg)(struct nl_msg *msg, void *priv); + int (*parse_msg)(struct nlattr *nla, struct rtnl_nexthop *rtnh); + + int (*compare)(void *a, void *b); + + void (*dump)(void *priv, struct nl_dump_params *dp); + void (*destructor)(void *priv); +}; + +struct rtnl_nh_encap; + +/* + * generic nexthop encap + */ +void nh_set_encap(struct rtnl_nexthop *nh, struct rtnl_nh_encap *rtnh_encap); + +int nh_encap_parse_msg(struct nlattr *encap, struct nlattr *encap_type, + struct rtnl_nexthop *rtnh); +int nh_encap_build_msg(struct nl_msg *msg, struct rtnl_nh_encap *rtnh_encap); + +void nh_encap_dump(struct rtnl_nh_encap *rtnh_encap, struct nl_dump_params *dp); + +int nh_encap_compare(struct rtnl_nh_encap *a, struct rtnl_nh_encap *b); + +/* + * MPLS encap + */ +extern struct nh_encap_ops mpls_encap_ops; +#endif diff --git a/libnl/lib/route/nexthop.c b/libnl/lib/route/nexthop.c new file mode 100644 index 0000000..7e0df61 --- /dev/null +++ b/libnl/lib/route/nexthop.c @@ -0,0 +1,432 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +/** + * @ingroup route_obj + * @defgroup nexthop Nexthop + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include + +#include "nexthop-encap.h" +#include "nl-route.h" +#include "nl-priv-dynamic-core/nl-core.h" + +/** @cond SKIP */ +#define NH_ATTR_FLAGS 0x000001 +#define NH_ATTR_WEIGHT 0x000002 +#define NH_ATTR_IFINDEX 0x000004 +#define NH_ATTR_GATEWAY 0x000008 +#define NH_ATTR_REALMS 0x000010 +#define NH_ATTR_NEWDST 0x000020 +#define NH_ATTR_VIA 0x000040 +#define NH_ATTR_ENCAP 0x000080 +/** @endcond */ + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_nexthop *rtnl_route_nh_alloc(void) +{ + struct rtnl_nexthop *nh; + + nh = calloc(1, sizeof(*nh)); + if (!nh) + return NULL; + + nl_init_list_head(&nh->rtnh_list); + + return nh; +} + +struct rtnl_nexthop *rtnl_route_nh_clone(struct rtnl_nexthop *src) +{ + struct rtnl_nexthop *nh; + + nh = rtnl_route_nh_alloc(); + if (!nh) + return NULL; + + nh->rtnh_flags = src->rtnh_flags; + nh->rtnh_flag_mask = src->rtnh_flag_mask; + nh->rtnh_weight = src->rtnh_weight; + nh->rtnh_ifindex = src->rtnh_ifindex; + nh->ce_mask = src->ce_mask; + + if (src->rtnh_gateway) { + nh->rtnh_gateway = nl_addr_clone(src->rtnh_gateway); + if (!nh->rtnh_gateway) { + free(nh); + return NULL; + } + } + + if (src->rtnh_newdst) { + nh->rtnh_newdst = nl_addr_clone(src->rtnh_newdst); + if (!nh->rtnh_newdst) { + nl_addr_put(nh->rtnh_gateway); + free(nh); + return NULL; + } + } + + if (src->rtnh_via) { + nh->rtnh_via = nl_addr_clone(src->rtnh_via); + if (!nh->rtnh_via) { + nl_addr_put(nh->rtnh_gateway); + nl_addr_put(nh->rtnh_newdst); + free(nh); + return NULL; + } + } + + return nh; +} + +void rtnl_route_nh_free(struct rtnl_nexthop *nh) +{ + nl_addr_put(nh->rtnh_gateway); + nl_addr_put(nh->rtnh_newdst); + nl_addr_put(nh->rtnh_via); + if (nh->rtnh_encap) { + if (nh->rtnh_encap->ops && nh->rtnh_encap->ops->destructor) + nh->rtnh_encap->ops->destructor(nh->rtnh_encap->priv); + free(nh->rtnh_encap->priv); + free(nh->rtnh_encap); + } + free(nh); +} + +/** @} */ + +int rtnl_route_nh_compare(struct rtnl_nexthop *a, struct rtnl_nexthop *b, + uint32_t attrs, int loose) +{ + uint32_t diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(NH_ATTR_IFINDEX, a->rtnh_ifindex != b->rtnh_ifindex); + diff |= _DIFF(NH_ATTR_WEIGHT, a->rtnh_weight != b->rtnh_weight); + diff |= _DIFF(NH_ATTR_REALMS, a->rtnh_realms != b->rtnh_realms); + diff |= _DIFF(NH_ATTR_GATEWAY, + nl_addr_cmp(a->rtnh_gateway, b->rtnh_gateway)); + diff |= _DIFF(NH_ATTR_NEWDST, + nl_addr_cmp(a->rtnh_newdst, b->rtnh_newdst)); + diff |= _DIFF(NH_ATTR_VIA, nl_addr_cmp(a->rtnh_via, b->rtnh_via)); + diff |= _DIFF(NH_ATTR_ENCAP, + nh_encap_compare(a->rtnh_encap, b->rtnh_encap)); + + if (loose) + diff |= _DIFF(NH_ATTR_FLAGS, (a->rtnh_flags ^ b->rtnh_flags) & + b->rtnh_flag_mask); + else + diff |= _DIFF(NH_ATTR_FLAGS, a->rtnh_flags != b->rtnh_flags); +#undef _DIFF + + return diff; +} + +/** + * Check if the fixed attributes of two nexthops are identical, and may + * only differ in flags or weight. + * + * @arg a a nexthop + * @arg b another nexthop + * + * @return true if both nexthop have equal attributes, otherwise false. + */ +int rtnl_route_nh_identical(struct rtnl_nexthop *a, struct rtnl_nexthop *b) +{ + return !rtnl_route_nh_compare(a, b, + NH_ATTR_IFINDEX | NH_ATTR_REALMS | + NH_ATTR_GATEWAY | NH_ATTR_NEWDST | + NH_ATTR_VIA | NH_ATTR_ENCAP, 0); +} + +static void nh_dump_line(struct rtnl_nexthop *nh, struct nl_dump_params *dp) +{ + struct nl_cache *link_cache; + char buf[128]; + + link_cache = nl_cache_mngt_require_safe("route/link"); + + if (nh->ce_mask & NH_ATTR_ENCAP) + nh_encap_dump(nh->rtnh_encap, dp); + + if (nh->ce_mask & NH_ATTR_NEWDST) + nl_dump(dp, "as to %s ", + nl_addr2str(nh->rtnh_newdst, buf, sizeof(buf))); + + nl_dump(dp, "via"); + + if (nh->ce_mask & NH_ATTR_VIA) + nl_dump(dp, " %s", + nl_addr2str(nh->rtnh_via, buf, sizeof(buf))); + + if (nh->ce_mask & NH_ATTR_GATEWAY) + nl_dump(dp, " %s", nl_addr2str(nh->rtnh_gateway, + buf, sizeof(buf))); + + if(nh->ce_mask & NH_ATTR_IFINDEX) { + if (link_cache) { + nl_dump(dp, " dev %s", + rtnl_link_i2name(link_cache, + nh->rtnh_ifindex, + buf, sizeof(buf))); + } else + nl_dump(dp, " dev %d", nh->rtnh_ifindex); + } + + nl_dump(dp, " "); + + if (link_cache) + nl_cache_put(link_cache); +} + +static void nh_dump_details(struct rtnl_nexthop *nh, struct nl_dump_params *dp) +{ + struct nl_cache *link_cache; + char buf[128]; + + link_cache = nl_cache_mngt_require_safe("route/link"); + + nl_dump(dp, "nexthop"); + + if (nh->ce_mask & NH_ATTR_ENCAP) + nh_encap_dump(nh->rtnh_encap, dp); + + if (nh->ce_mask & NH_ATTR_NEWDST) + nl_dump(dp, " as to %s", + nl_addr2str(nh->rtnh_newdst, buf, sizeof(buf))); + + if (nh->ce_mask & NH_ATTR_VIA) + nl_dump(dp, " via %s", + nl_addr2str(nh->rtnh_via, buf, sizeof(buf))); + + if (nh->ce_mask & NH_ATTR_GATEWAY) + nl_dump(dp, " via %s", nl_addr2str(nh->rtnh_gateway, + buf, sizeof(buf))); + + if(nh->ce_mask & NH_ATTR_IFINDEX) { + if (link_cache) { + nl_dump(dp, " dev %s", + rtnl_link_i2name(link_cache, + nh->rtnh_ifindex, + buf, sizeof(buf))); + } else + nl_dump(dp, " dev %d", nh->rtnh_ifindex); + } + + if (nh->ce_mask & NH_ATTR_WEIGHT) + nl_dump(dp, " weight %u", nh->rtnh_weight); + + if (nh->ce_mask & NH_ATTR_REALMS) + nl_dump(dp, " realm %04x:%04x", + RTNL_REALM_FROM(nh->rtnh_realms), + RTNL_REALM_TO(nh->rtnh_realms)); + + if (nh->ce_mask & NH_ATTR_FLAGS) + nl_dump(dp, " <%s>", rtnl_route_nh_flags2str(nh->rtnh_flags, + buf, sizeof(buf))); + + if (link_cache) + nl_cache_put(link_cache); +} + +void rtnl_route_nh_dump(struct rtnl_nexthop *nh, struct nl_dump_params *dp) +{ + switch (dp->dp_type) { + case NL_DUMP_LINE: + nh_dump_line(nh, dp); + break; + + case NL_DUMP_DETAILS: + case NL_DUMP_STATS: + if (dp->dp_ivar == NH_DUMP_FROM_DETAILS) + nh_dump_details(nh, dp); + break; + + default: + break; + } +} + +void nh_set_encap(struct rtnl_nexthop *nh, struct rtnl_nh_encap *rtnh_encap) +{ + if (nh->rtnh_encap) { + if (nh->rtnh_encap->ops && nh->rtnh_encap->ops->destructor) + nh->rtnh_encap->ops->destructor(nh->rtnh_encap->priv); + free(nh->rtnh_encap->priv); + free(nh->rtnh_encap); + } + + if (rtnh_encap) { + nh->rtnh_encap = rtnh_encap; + nh->ce_mask |= NH_ATTR_ENCAP; + } else { + nh->rtnh_encap = NULL; + nh->ce_mask &= ~NH_ATTR_ENCAP; + } +} + +/** + * @name Attributes + * @{ + */ + +void rtnl_route_nh_set_weight(struct rtnl_nexthop *nh, uint8_t weight) +{ + nh->rtnh_weight = weight; + nh->ce_mask |= NH_ATTR_WEIGHT; +} + +uint8_t rtnl_route_nh_get_weight(struct rtnl_nexthop *nh) +{ + return nh->rtnh_weight; +} + +void rtnl_route_nh_set_ifindex(struct rtnl_nexthop *nh, int ifindex) +{ + nh->rtnh_ifindex = ifindex; + nh->ce_mask |= NH_ATTR_IFINDEX; +} + +int rtnl_route_nh_get_ifindex(struct rtnl_nexthop *nh) +{ + return nh->rtnh_ifindex; +} + +/* FIXME: Convert to return an int */ +void rtnl_route_nh_set_gateway(struct rtnl_nexthop *nh, struct nl_addr *addr) +{ + struct nl_addr *old = nh->rtnh_gateway; + + if (addr) { + nh->rtnh_gateway = nl_addr_get(addr); + nh->ce_mask |= NH_ATTR_GATEWAY; + } else { + nh->ce_mask &= ~NH_ATTR_GATEWAY; + nh->rtnh_gateway = NULL; + } + + if (old) + nl_addr_put(old); +} + +struct nl_addr *rtnl_route_nh_get_gateway(struct rtnl_nexthop *nh) +{ + return nh->rtnh_gateway; +} + +void rtnl_route_nh_set_flags(struct rtnl_nexthop *nh, unsigned int flags) +{ + nh->rtnh_flag_mask |= flags; + nh->rtnh_flags |= flags; + nh->ce_mask |= NH_ATTR_FLAGS; +} + +void rtnl_route_nh_unset_flags(struct rtnl_nexthop *nh, unsigned int flags) +{ + nh->rtnh_flag_mask |= flags; + nh->rtnh_flags &= ~flags; + nh->ce_mask |= NH_ATTR_FLAGS; +} + +unsigned int rtnl_route_nh_get_flags(struct rtnl_nexthop *nh) +{ + return nh->rtnh_flags; +} + +void rtnl_route_nh_set_realms(struct rtnl_nexthop *nh, uint32_t realms) +{ + nh->rtnh_realms = realms; + nh->ce_mask |= NH_ATTR_REALMS; +} + +uint32_t rtnl_route_nh_get_realms(struct rtnl_nexthop *nh) +{ + return nh->rtnh_realms; +} + +int rtnl_route_nh_set_newdst(struct rtnl_nexthop *nh, struct nl_addr *addr) +{ + struct nl_addr *old = nh->rtnh_newdst; + + if (addr) { + nh->rtnh_newdst = nl_addr_get(addr); + nh->ce_mask |= NH_ATTR_NEWDST; + } else { + nh->ce_mask &= ~NH_ATTR_NEWDST; + nh->rtnh_newdst = NULL; + } + + if (old) + nl_addr_put(old); + + return 0; +} + +struct nl_addr *rtnl_route_nh_get_newdst(struct rtnl_nexthop *nh) +{ + return nh->rtnh_newdst; +} + +int rtnl_route_nh_set_via(struct rtnl_nexthop *nh, struct nl_addr *addr) +{ + struct nl_addr *old = nh->rtnh_via; + + if (addr) { + nh->rtnh_via = nl_addr_get(addr); + nh->ce_mask |= NH_ATTR_VIA; + } else { + nh->ce_mask &= ~NH_ATTR_VIA; + nh->rtnh_via= NULL; + } + + if (old) + nl_addr_put(old); + + return 0; +} + +struct nl_addr *rtnl_route_nh_get_via(struct rtnl_nexthop *nh) +{ + return nh->rtnh_via; +} + +/** @} */ + +/** + * @name Nexthop Flags Translations + * @{ + */ + +static const struct trans_tbl nh_flags[] = { + __ADD(RTNH_F_DEAD, dead), + __ADD(RTNH_F_PERVASIVE, pervasive), + __ADD(RTNH_F_ONLINK, onlink), +}; + +char *rtnl_route_nh_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, nh_flags, ARRAY_SIZE(nh_flags)); +} + +int rtnl_route_nh_str2flags(const char *name) +{ + return __str2flags(name, nh_flags, ARRAY_SIZE(nh_flags)); +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/route/nexthop_encap.c b/libnl/lib/route/nexthop_encap.c new file mode 100644 index 0000000..226b901 --- /dev/null +++ b/libnl/lib/route/nexthop_encap.c @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +#include "nl-route.h" +#include "nexthop-encap.h" + +static struct lwtunnel_encap_type { + const char *name; + struct nh_encap_ops *ops; +} lwtunnel_encap_types[__LWTUNNEL_ENCAP_MAX] = { + [LWTUNNEL_ENCAP_NONE] = { .name = "none" }, + [LWTUNNEL_ENCAP_MPLS] = { .name = "mpls", .ops = &mpls_encap_ops }, + [LWTUNNEL_ENCAP_IP] = { .name = "ip" }, + [LWTUNNEL_ENCAP_IP6] = { .name = "ip6" }, + [LWTUNNEL_ENCAP_ILA] = { .name = "ila" }, + [LWTUNNEL_ENCAP_BPF] = { .name = "bpf" }, +}; + +static const char *nh_encap_type2str(unsigned int type) +{ + const char *name; + + if (type > LWTUNNEL_ENCAP_MAX) + return "unknown"; + + name = lwtunnel_encap_types[type].name; + + return name ? name : "unknown"; +} + +void nh_encap_dump(struct rtnl_nh_encap *rtnh_encap, struct nl_dump_params *dp) +{ + if (!rtnh_encap->ops) + return; + + nl_dump(dp, " encap %s ", + nh_encap_type2str(rtnh_encap->ops->encap_type)); + + if (rtnh_encap->ops->dump) + rtnh_encap->ops->dump(rtnh_encap->priv, dp); +} + +int nh_encap_build_msg(struct nl_msg *msg, struct rtnl_nh_encap *rtnh_encap) +{ + struct nlattr *encap; + int err; + + if (!rtnh_encap->ops || !rtnh_encap->ops->build_msg) { + NL_DBG(2, "Nexthop encap type not implemented\n"); + return -NLE_INVAL; + } + + NLA_PUT_U16(msg, RTA_ENCAP_TYPE, rtnh_encap->ops->encap_type); + + encap = nla_nest_start(msg, RTA_ENCAP); + if (!encap) + goto nla_put_failure; + + err = rtnh_encap->ops->build_msg(msg, rtnh_encap->priv); + if (err < 0) + return err; + + nla_nest_end(msg, encap); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +int nh_encap_parse_msg(struct nlattr *encap, struct nlattr *encap_type, + struct rtnl_nexthop *rtnh) +{ + uint16_t e_type = nla_get_u16(encap_type); + + if (e_type == LWTUNNEL_ENCAP_NONE) { + NL_DBG(2, "RTA_ENCAP_TYPE should not be LWTUNNEL_ENCAP_NONE\n"); + return -NLE_INVAL; + } + if (e_type > LWTUNNEL_ENCAP_MAX) { + NL_DBG(2, "Unknown RTA_ENCAP_TYPE: %d\n", e_type); + return -NLE_INVAL; + } + + if (!lwtunnel_encap_types[e_type].ops) { + NL_DBG(2, "RTA_ENCAP_TYPE %s is not implemented\n", + lwtunnel_encap_types[e_type].name); + return -NLE_MSGTYPE_NOSUPPORT; + } + + return lwtunnel_encap_types[e_type].ops->parse_msg(encap, rtnh); +} + +int nh_encap_compare(struct rtnl_nh_encap *a, struct rtnl_nh_encap *b) +{ + if (!a && !b) + return 0; + + if ((a && !b) || (!a && b) || (a->ops != b->ops)) + return 1; + + if (!a->ops || !a->ops->compare) + return 0; + + return a->ops->compare(a->priv, b->priv); +} diff --git a/libnl/lib/route/nh.c b/libnl/lib/route/nh.c new file mode 100644 index 0000000..3dfe558 --- /dev/null +++ b/libnl/lib/route/nh.c @@ -0,0 +1,590 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2022 Stanislav Zaikin + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +#include "nl-aux-route/nl-route.h" +#include "nl-route.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" + +/** @cond SKIP */ +struct rtnl_nh { + NLHDR_COMMON + + uint8_t nh_family; + uint32_t nh_flags; + + uint32_t nh_id; + uint32_t nh_group_type; + nl_nh_group_t *nh_group; + uint32_t nh_oif; + struct nl_addr *nh_gateway; +}; + +#define NH_ATTR_FLAGS (1 << 0) +#define NH_ATTR_ID (1 << 1) +#define NH_ATTR_GROUP (1 << 2) +#define NH_ATTR_FLAG_BLACKHOLE (1 << 3) +#define NH_ATTR_OIF (1 << 4) +#define NH_ATTR_GATEWAY (1 << 5) +#define NH_ATTR_FLAG_GROUPS (1 << 6) +#define NH_ATTR_FLAG_FDB (1 << 8) +/** @endcond */ + +struct nla_policy rtnl_nh_policy[NHA_MAX + 1] = { + [NHA_UNSPEC] = { .type = NLA_UNSPEC }, + [NHA_ID] = { .type = NLA_U32 }, + [NHA_GROUP] = { .type = NLA_NESTED }, + [NHA_GROUP_TYPE] = { .type = NLA_U16 }, + [NHA_BLACKHOLE] = { .type = NLA_UNSPEC }, + [NHA_OIF] = { .type = NLA_U32 }, +}; + +static struct nl_cache_ops rtnl_nh_ops; +static struct nl_object_ops nh_obj_ops; + +static nl_nh_group_t *rtnl_nh_grp_alloc(unsigned size) +{ + nl_nh_group_t *nhg; + + _nl_assert(size <= (unsigned)INT_MAX); + + if (!(nhg = calloc(1, sizeof(*nhg)))) + return NULL; + + nhg->size = size; + + if (!(nhg->entries = calloc(size, sizeof(*nhg->entries)))) { + free(nhg); + return NULL; + } + + nhg->ce_refcnt = 1; + + return nhg; +} + +static void rtnl_nh_grp_put(nl_nh_group_t *nhg) +{ + if (!nhg) + return; + + _nl_assert(nhg->ce_refcnt > 0); + + nhg->ce_refcnt--; + + if (nhg->ce_refcnt > 0) + return; + + free(nhg); +} + +static int rtnh_nh_grp_cmp(const nl_nh_group_t *a, const nl_nh_group_t *b) +{ + unsigned i; + + _NL_CMP_SELF(a, b); + _NL_CMP_DIRECT(a->size, b->size); + for (i = 0; i < a->size; i++) { + _NL_CMP_DIRECT(a->entries[i].nh_id, b->entries[i].nh_id); + _NL_CMP_DIRECT(a->entries[i].weight, b->entries[i].weight); + } + return 0; +} + +static int rtnh_nh_grp_clone(nl_nh_group_t *src, nl_nh_group_t **dst) +{ + nl_nh_group_t *ret; + unsigned i; + + ret = rtnl_nh_grp_alloc(src->size); + + if (!ret) + return -NLE_NOMEM; + + for (i = 0; i < src->size; i++) { + ret->entries[i].nh_id = src->entries[i].nh_id; + ret->entries[i].weight = src->entries[i].weight; + } + + *dst = ret; + + return NLE_SUCCESS; +} + +struct rtnl_nh *rtnl_nh_alloc(void) +{ + return (struct rtnl_nh *)nl_object_alloc(&nh_obj_ops); +} + +static int nh_clone(struct nl_object *_src, struct nl_object *_dst) +{ + struct rtnl_nh *dst = nl_object_priv(_dst); + struct rtnl_nh *src = nl_object_priv(_src); + + dst->nh_flags = src->nh_flags; + dst->nh_family = src->nh_family; + dst->nh_id = src->nh_id; + dst->nh_oif = src->nh_oif; + dst->ce_mask = src->ce_mask; + + if (src->nh_gateway) { + dst->nh_gateway = nl_addr_clone(src->nh_gateway); + if (!dst->nh_gateway) { + return -NLE_NOMEM; + } + } + + if (src->nh_group) { + if (rtnh_nh_grp_clone(src->nh_group, &dst->nh_group) < 0) { + return -NLE_NOMEM; + } + } + + return 0; +} + +static void nh_free(struct nl_object *obj) +{ + struct rtnl_nh *nh = nl_object_priv(obj); + nl_addr_put(nh->nh_gateway); + + if (nh->nh_group) + rtnl_nh_grp_put(nh->nh_group); +} + +void rtnl_nh_put(struct rtnl_nh *nh) +{ + struct nl_object *obj = (struct nl_object *)nh; + + nl_object_put(obj); +} + +static void nexthop_keygen(struct nl_object *obj, uint32_t *hashkey, + uint32_t table_sz) +{ + struct rtnl_nh *nexthop = nl_object_priv(obj); + unsigned int lkey_sz; + struct nexthop_hash_key { + uint32_t nh_id; + } _nl_packed lkey; + + lkey_sz = sizeof(lkey); + lkey.nh_id = nexthop->nh_id; + + *hashkey = nl_hash(&lkey, lkey_sz, 0) % table_sz; + + return; +} + +int rtnl_nh_set_gateway(struct rtnl_nh *nexthop, struct nl_addr *addr) +{ + if (nexthop->ce_mask & NH_ATTR_GATEWAY) { + nl_addr_put(nexthop->nh_gateway); + } + + nexthop->nh_gateway = nl_addr_clone(addr); + nexthop->ce_mask |= NH_ATTR_GATEWAY; + + return 0; +} + +struct nl_addr *rtnl_nh_get_gateway(struct rtnl_nh *nexthop) +{ + return nexthop->nh_gateway; +} + +int rtnl_nh_set_fdb(struct rtnl_nh *nexthop, int value) +{ + if (value) + nexthop->ce_mask |= NH_ATTR_FLAG_FDB; + else + nexthop->ce_mask &= ~NH_ATTR_FLAG_FDB; + + return 0; +} + +int rtnl_nh_get_oif(struct rtnl_nh *nexthop) +{ + if (nexthop->ce_mask & NH_ATTR_OIF) + return nexthop->nh_oif; + + return 0; +} + +int rtnl_nh_get_fdb(struct rtnl_nh *nexthop) +{ + return nexthop->ce_mask & NH_ATTR_FLAG_FDB; +} + +int rtnl_nh_get_group_entry(struct rtnl_nh *nexthop, int n) +{ + if (!(nexthop->ce_mask & NH_ATTR_GROUP) || !nexthop->nh_group) + return -NLE_MISSING_ATTR; + + if (n < 0 || ((unsigned)n) >= nexthop->nh_group->size) + return -NLE_INVAL; + + return nexthop->nh_group->entries[n].nh_id; +} + +int rtnl_nh_get_group_size(struct rtnl_nh *nexthop) +{ + if (!(nexthop->ce_mask & NH_ATTR_GROUP) || !nexthop->nh_group) + return -NLE_MISSING_ATTR; + + _nl_assert(nexthop->nh_group->size <= INT_MAX); + + return (int)nexthop->nh_group->size; +} + +static int rtnl_nh_grp_info(unsigned size, const struct nexthop_grp *vi, + nl_nh_group_t **nvi) +{ + nl_nh_group_t *ret; + unsigned i; + + if (!(ret = rtnl_nh_grp_alloc(size))) + return -NLE_NOMEM; + + for (i = 0; i < size; i++) { + ret->entries[i].nh_id = vi[i].id; + ret->entries[i].weight = vi[i].weight; + } + + *nvi = ret; + return NLE_SUCCESS; +} + +int rtnl_nh_get_id(struct rtnl_nh *nh) +{ + if (nh->ce_mask & NH_ATTR_ID) + return nh->nh_id; + + return -NLE_INVAL; +} + +static int nexthop_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *n, struct nl_parser_param *pp) +{ + _nl_auto_rtnl_nh struct rtnl_nh *nexthop = NULL; + struct nhmsg *ifi; + struct nlattr *tb[NHA_MAX + 1]; + int err; + int family; + + nexthop = rtnl_nh_alloc(); + if (nexthop == NULL) + return -NLE_NOMEM; + + nexthop->ce_msgtype = n->nlmsg_type; + + if (!nlmsg_valid_hdr(n, sizeof(*ifi))) + return -NLE_MSG_TOOSHORT; + + ifi = nlmsg_data(n); + family = ifi->nh_family; + nexthop->nh_family = family; + nexthop->nh_flags = ifi->nh_flags; + nexthop->ce_mask = (NH_ATTR_FLAGS); + + err = nlmsg_parse(n, sizeof(*ifi), tb, NHA_MAX, rtnl_nh_policy); + if (err < 0) + return err; + + if (tb[NHA_ID]) { + nexthop->nh_id = nla_get_u32(tb[NHA_ID]); + nexthop->ce_mask |= NH_ATTR_ID; + } + + if (tb[NHA_OIF]) { + nexthop->nh_oif = nla_get_u32(tb[NHA_OIF]); + nexthop->ce_mask |= NH_ATTR_OIF; + } + + if (tb[NHA_GATEWAY]) { + nexthop->nh_gateway = + nl_addr_alloc_attr(tb[NHA_GATEWAY], family); + nexthop->ce_mask |= NH_ATTR_GATEWAY; + } + + if (tb[NHA_BLACKHOLE]) { + nexthop->ce_mask |= NH_ATTR_FLAG_BLACKHOLE; + } + + if (tb[NHA_GROUPS]) { + nexthop->ce_mask |= NH_ATTR_FLAG_GROUPS; + } + + if (tb[NHA_FDB]) { + nexthop->ce_mask |= NH_ATTR_FLAG_FDB; + } + + if (tb[NHA_GROUP]) { + nl_nh_group_t *nh_group = NULL; + const void *data; + unsigned size; + unsigned len; + + data = nla_data(tb[NHA_GROUP]); + len = _nla_len(tb[NHA_GROUP]); + size = len / sizeof(struct nexthop_grp); + + err = rtnl_nh_grp_info(size, (const struct nexthop_grp *)data, + &nh_group); + if (err < 0) { + return err; + } + + nexthop->nh_group = nh_group; + nexthop->ce_mask |= NH_ATTR_GROUP; + } + + return pp->pp_cb((struct nl_object *)nexthop, pp); +} + +static int nexthop_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + _nl_auto_nl_msg struct nl_msg *msg = NULL; + int family = cache->c_iarg1; + struct nhmsg hdr = { .nh_family = family }; + int err; + + msg = nlmsg_alloc_simple(RTM_GETNEXTHOP, NLM_F_DUMP); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &hdr, sizeof(hdr), NLMSG_ALIGNTO) < 0) + return -NLE_MSGSIZE; + + err = nl_send_auto(sk, msg); + if (err < 0) + return err; + + return NLE_SUCCESS; +} + +static void dump_nh_group(nl_nh_group_t *group, struct nl_dump_params *dp) +{ + unsigned i; + + nl_dump(dp, " nh_grp:"); + for (i = 0; i < group->size; i++) { + nl_dump(dp, " %u", group->entries[i].nh_id); + } +} + +static void nh_dump_line(struct nl_object *obj, struct nl_dump_params *dp) +{ + struct nl_cache *cache; + char buf[128]; + struct rtnl_nh *nh = nl_object_priv(obj); + + cache = nl_cache_mngt_require_safe("route/nh"); + + if (nh->ce_mask & NH_ATTR_ID) + nl_dump(dp, "nhid %u", nh->nh_id); + + if (nh->ce_mask & NH_ATTR_OIF) + nl_dump(dp, " oif %d", nh->nh_oif); + + if (nh->ce_mask & NH_ATTR_GATEWAY) + nl_dump(dp, " via %s", + nl_addr2str(nh->nh_gateway, buf, sizeof(buf))); + + if (nh->ce_mask & NH_ATTR_FLAG_BLACKHOLE) + nl_dump(dp, " blackhole"); + + if (nh->ce_mask & NH_ATTR_FLAG_GROUPS) + nl_dump(dp, " groups"); + + if (nh->ce_mask & NH_ATTR_GROUP) + dump_nh_group(nh->nh_group, dp); + + if (nh->ce_mask & NH_ATTR_FLAG_FDB) + nl_dump(dp, " fdb"); + + nl_dump(dp, "\n"); + + if (cache) + nl_cache_put(cache); +} + +static void nh_dump_details(struct nl_object *nh, struct nl_dump_params *dp) +{ + nh_dump_line(nh, dp); +} + +static uint64_t nh_compare(struct nl_object *a, struct nl_object *b, + uint64_t attrs, int loose) +{ + int diff = 0; + struct rtnl_nh *src = nl_object_priv(a); + struct rtnl_nh *dst = nl_object_priv(b); + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(NH_ATTR_ID, src->nh_id != dst->nh_id); + diff |= _DIFF(NH_ATTR_GATEWAY, + nl_addr_cmp(src->nh_gateway, dst->nh_gateway)); + diff |= _DIFF(NH_ATTR_OIF, src->nh_oif != dst->nh_oif); + diff |= _DIFF(NH_ATTR_GROUP, + rtnh_nh_grp_cmp(src->nh_group, dst->nh_group)); + diff |= _DIFF(NH_ATTR_FLAG_FDB, false); + diff |= _DIFF(NH_ATTR_FLAG_GROUPS, false); + diff |= _DIFF(NH_ATTR_FLAG_BLACKHOLE, false); +#undef _DIFF + + return diff; +} + +struct rtnl_nh *rtnl_nh_get(struct nl_cache *cache, int nhid) +{ + struct rtnl_nh *nh; + + if (cache->c_ops != &rtnl_nh_ops) + return NULL; + + nl_list_for_each_entry(nh, &cache->c_items, ce_list) { + if (nh->nh_id == ((unsigned)nhid)) { + nl_object_get((struct nl_object *)nh); + return nh; + } + } + + return NULL; +} + +/** + * Allocate nexthop cache and fill in all configured nexthops. + * @arg sk Netnexthop socket. + * @arg family nexthop address family or AF_UNSPEC + * @arg result Pointer to store resulting cache. + * @arg flags Flags to set in nexthop cache before filling + * + * Allocates and initializes a new nexthop cache. If \c sk is valid, a netnexthop + * message is sent to the kernel requesting a full dump of all configured + * nexthops. The returned messages are parsed and filled into the cache. If + * the operation succeeds, the resulting cache will contain a nexthop object for + * each nexthop configured in the kernel. If \c sk is NULL, returns 0 but the + * cache is still empty. + * + * If \c family is set to an address family other than \c AF_UNSPEC the + * contents of the cache can be limited to a specific address family. + * Currently the following address families are supported: + * - AF_BRIDGE + * - AF_INET6 + * + * @route_doc{nexthop_list, Get List of nexthops} + * @see rtnl_nh_get() + * @see rtnl_nh_get_by_name() + * @return 0 on success or a negative error code. + */ +static int rtnl_nh_alloc_cache_flags(struct nl_sock *sk, int family, + struct nl_cache **result, + unsigned int flags) +{ + struct nl_cache *cache; + int err; + + cache = nl_cache_alloc(&rtnl_nh_ops); + if (!cache) + return -NLE_NOMEM; + + cache->c_iarg1 = family; + + if (flags) + nl_cache_set_flags(cache, flags); + + if (sk && (err = nl_cache_refill(sk, cache)) < 0) { + nl_cache_free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** + * Allocate nexthop cache and fill in all configured nexthops. + * @arg sk Netnexthop socket. + * @arg family nexthop address family or AF_UNSPEC + * @arg result Pointer to store resulting cache. + * + * Allocates and initializes a new nexthop cache. If \c sk is valid, a netnexthop + * message is sent to the kernel requesting a full dump of all configured + * nexthops. The returned messages are parsed and filled into the cache. If + * the operation succeeds, the resulting cache will contain a nexthop object for + * each nexthop configured in the kernel. If \c sk is NULL, returns 0 but the + * cache is still empty. + * + * If \c family is set to an address family other than \c AF_UNSPEC the + * contents of the cache can be limited to a specific address family. + * Currently the following address families are supported: + * - AF_BRIDGE + * - AF_INET6 + * + * @route_doc{nexthop_list, Get List of nexthops} + * @see rtnl_nh_get() + * @see rtnl_nh_get_by_name() + * @return 0 on success or a negative error code. + */ +int rtnl_nh_alloc_cache(struct nl_sock *sk, int family, + struct nl_cache **result) +{ + return rtnl_nh_alloc_cache_flags(sk, family, result, 0); +} + +static struct nl_object_ops nh_obj_ops = { + .oo_name = "route/nh", + .oo_size = sizeof(struct rtnl_nh), + .oo_free_data = nh_free, + .oo_clone = nh_clone, + .oo_dump = { + [NL_DUMP_LINE] = nh_dump_line, + [NL_DUMP_DETAILS] = nh_dump_details, + }, + .oo_compare = nh_compare, + .oo_keygen = nexthop_keygen, + .oo_attrs2str = rtnl_route_nh_flags2str, + .oo_id_attrs = NH_ATTR_ID, +}; + +static struct nl_af_group nh_groups[] = { + { AF_UNSPEC, RTNLGRP_NEXTHOP }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops rtnl_nh_ops = { + .co_name = "route/nh", + .co_hdrsize = sizeof(struct nhmsg), + .co_msgtypes = { + { RTM_NEWNEXTHOP, NL_ACT_NEW, "new" }, + { RTM_DELNEXTHOP, NL_ACT_DEL, "del" }, + { RTM_GETNEXTHOP, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = nh_groups, + .co_request_update = nexthop_request_update, + .co_msg_parser = nexthop_msg_parser, + .co_obj_ops = &nh_obj_ops, +}; + +static void _nl_init nexthop_init(void) +{ + nl_cache_mngt_register(&rtnl_nh_ops); +} + +static void _nl_exit nexthop_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_nh_ops); +} diff --git a/libnl/lib/route/nh_encap_mpls.c b/libnl/lib/route/nh_encap_mpls.c new file mode 100644 index 0000000..a780291 --- /dev/null +++ b/libnl/lib/route/nh_encap_mpls.c @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include +#include + +#include + +#include "nl-route.h" +#include "nexthop-encap.h" + +struct mpls_iptunnel_encap { + struct nl_addr *dst; + uint8_t ttl; +}; + +static void mpls_encap_dump(void *priv, struct nl_dump_params *dp) +{ + struct mpls_iptunnel_encap *encap_info = priv; + char buf[256]; + + nl_dump(dp, "%s ", nl_addr2str(encap_info->dst, buf, sizeof(buf))); + + if (encap_info->ttl) + nl_dump(dp, "ttl %u ", encap_info->ttl); +} + +static int mpls_encap_build_msg(struct nl_msg *msg, void *priv) +{ + struct mpls_iptunnel_encap *encap_info = priv; + + NLA_PUT_ADDR(msg, MPLS_IPTUNNEL_DST, encap_info->dst); + if (encap_info->ttl) + NLA_PUT_U8(msg, MPLS_IPTUNNEL_TTL, encap_info->ttl); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static void mpls_encap_destructor(void *priv) +{ + struct mpls_iptunnel_encap *encap_info = priv; + + nl_addr_put(encap_info->dst); +} + +static struct nla_policy mpls_encap_policy[MPLS_IPTUNNEL_MAX + 1] = { + [MPLS_IPTUNNEL_DST] = { .type = NLA_U32 }, + [MPLS_IPTUNNEL_TTL] = { .type = NLA_U8 }, +}; + +static int mpls_encap_parse_msg(struct nlattr *nla, struct rtnl_nexthop *nh) +{ + struct nlattr *tb[MPLS_IPTUNNEL_MAX + 1]; + struct nl_addr *labels; + uint8_t ttl = 0; + int err; + + err = nla_parse_nested(tb, MPLS_IPTUNNEL_MAX, nla, mpls_encap_policy); + if (err < 0) + return err; + + if (!tb[MPLS_IPTUNNEL_DST]) + return -NLE_INVAL; + + labels = nl_addr_alloc_attr(tb[MPLS_IPTUNNEL_DST], AF_MPLS); + if (!labels) + return -NLE_NOMEM; + + if (tb[MPLS_IPTUNNEL_TTL]) + ttl = nla_get_u8(tb[MPLS_IPTUNNEL_TTL]); + + err = rtnl_route_nh_encap_mpls(nh, labels, ttl); + + nl_addr_put(labels); + + return err; +} + +static int mpls_encap_compare(void *_a, void *_b) +{ + struct mpls_iptunnel_encap *a = _a; + struct mpls_iptunnel_encap *b = _b; + int diff = 0; + + diff |= (a->ttl != b->ttl); + diff |= nl_addr_cmp(a->dst, b->dst); + + return diff; +} + +struct nh_encap_ops mpls_encap_ops = { + .encap_type = LWTUNNEL_ENCAP_MPLS, + .build_msg = mpls_encap_build_msg, + .parse_msg = mpls_encap_parse_msg, + .compare = mpls_encap_compare, + .dump = mpls_encap_dump, + .destructor = mpls_encap_destructor, +}; + +int rtnl_route_nh_encap_mpls(struct rtnl_nexthop *nh, + struct nl_addr *addr, + uint8_t ttl) +{ + struct mpls_iptunnel_encap *mpls_encap; + struct rtnl_nh_encap *rtnh_encap; + + if (!addr) + return -NLE_INVAL; + + rtnh_encap = calloc(1, sizeof(*rtnh_encap)); + if (!rtnh_encap) + return -NLE_NOMEM; + + mpls_encap = calloc(1, sizeof(*mpls_encap)); + if (!mpls_encap) { + free(rtnh_encap); + return -NLE_NOMEM; + } + + mpls_encap->dst = nl_addr_get(addr); + mpls_encap->ttl = ttl; + + rtnh_encap->priv = mpls_encap; + rtnh_encap->ops = &mpls_encap_ops; + + nh_set_encap(nh, rtnh_encap); + + return 0; +} + +struct nl_addr *rtnl_route_nh_get_encap_mpls_dst(struct rtnl_nexthop *nh) +{ + struct mpls_iptunnel_encap *mpls_encap; + + if (!nh->rtnh_encap || nh->rtnh_encap->ops->encap_type != LWTUNNEL_ENCAP_MPLS) + return NULL; + + mpls_encap = (struct mpls_iptunnel_encap *)nh->rtnh_encap->priv; + if (!mpls_encap) + return NULL; + + return mpls_encap->dst; +} + +uint8_t rtnl_route_nh_get_encap_mpls_ttl(struct rtnl_nexthop *nh) +{ + struct mpls_iptunnel_encap *mpls_encap; + + if (!nh->rtnh_encap || nh->rtnh_encap->ops->encap_type != LWTUNNEL_ENCAP_MPLS) + return 0; + + mpls_encap = (struct mpls_iptunnel_encap *)nh->rtnh_encap->priv; + if (!mpls_encap) + return 0; + + return mpls_encap->ttl; +} diff --git a/libnl/lib/route/nl-route.h b/libnl/lib/route/nl-route.h new file mode 100644 index 0000000..53d8b9f --- /dev/null +++ b/libnl/lib/route/nl-route.h @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + * Copyright (c) 2013 Sassano Systems LLC + */ + +#ifndef __LIB_ROUTE_NL_ROUTE_H__ +#define __LIB_ROUTE_NL_ROUTE_H__ + +#include + +#include "nl-priv-dynamic-core/object-api.h" +#include "nl-priv-dynamic-route/nl-priv-dynamic-route.h" +#include "nl-aux-core/nl-core.h" + +/*****************************************************************************/ + +struct rtnl_link_map { + uint64_t lm_mem_start; + uint64_t lm_mem_end; + uint64_t lm_base_addr; + uint16_t lm_irq; + uint8_t lm_dma; + uint8_t lm_port; +}; + +#define IFQDISCSIZ 32 + +struct rtnl_link { + NLHDR_COMMON + + char l_name[IFNAMSIZ]; + uint32_t l_family; + uint32_t l_arptype; + uint32_t l_index; + uint32_t l_flags; + uint32_t l_change; + uint32_t l_mtu; + uint32_t l_link; + int32_t l_link_netnsid; + uint32_t l_txqlen; + uint32_t l_weight; + uint32_t l_master; + struct nl_addr *l_addr; + struct nl_addr *l_bcast; + struct nl_addr *l_paddr; + char l_qdisc[IFQDISCSIZ]; + struct rtnl_link_map l_map; + uint64_t l_stats[RTNL_LINK_STATS_MAX + 1]; + uint32_t l_flag_mask; + uint32_t l_num_vf; + uint8_t l_operstate; + uint8_t l_linkmode; + /* 2 byte hole */ + char *l_info_kind; + char *l_info_slave_kind; + struct rtnl_link_info_ops *l_info_ops; + void *l_af_data[AF_MAX]; + void *l_info; + char *l_ifalias; + uint32_t l_promiscuity; + uint32_t l_num_tx_queues; + uint32_t l_num_rx_queues; + uint32_t l_gso_max_segs; + uint32_t l_gso_max_size; + uint32_t l_group; + uint8_t l_carrier; + /* 3 byte hole */ + uint32_t l_carrier_changes; + struct rtnl_link_af_ops *l_af_ops; + struct nl_data *l_phys_port_id; + char l_phys_port_name[IFNAMSIZ]; + struct nl_data *l_phys_switch_id; + int l_ns_fd; + pid_t l_ns_pid; + struct rtnl_link_vf *l_vf_list; +}; + +struct rtnl_nh_encap { + struct nh_encap_ops *ops; + void *priv; /* private data for encap type */ +}; + +struct rtnl_ratespec { + uint64_t rs_rate64; + uint16_t rs_overhead; + int16_t rs_cell_align; + uint16_t rs_mpu; + uint8_t rs_cell_log; +}; + +#define TCKINDSIZ 32 + +#define NL_TC_GENERIC(pre) \ + NLHDR_COMMON \ + uint32_t pre##_family; \ + uint32_t pre##_ifindex; \ + uint32_t pre##_handle; \ + uint32_t pre##_parent; \ + uint32_t pre##_info; \ + uint32_t pre##_mtu; \ + uint32_t pre##_mpu; \ + uint32_t pre##_overhead; \ + uint32_t pre##_linktype; \ + char pre##_kind[TCKINDSIZ]; \ + struct nl_data *pre##_opts; \ + uint64_t pre##_stats[RTNL_TC_STATS_MAX + 1]; \ + struct nl_data *pre##_xstats; \ + struct nl_data *pre##_subdata; \ + struct rtnl_link *pre##_link; \ + struct rtnl_tc_ops *pre##_ops; \ + enum rtnl_tc_type pre##_type; \ + uint32_t pre##_chain + +struct rtnl_tc { + NL_TC_GENERIC(tc); +}; + +struct rtnl_qdisc { + NL_TC_GENERIC(q); +}; + +struct rtnl_act { + NL_TC_GENERIC(c); + struct rtnl_act *a_next; +}; + +/*****************************************************************************/ + +static inline void rtnl_copy_ratespec(struct rtnl_ratespec *dst, + struct tc_ratespec *src) +{ + dst->rs_cell_log = src->cell_log; + dst->rs_overhead = src->overhead; + dst->rs_cell_align = src->cell_align; + dst->rs_mpu = src->mpu; + dst->rs_rate64 = src->rate; +} + +static inline void rtnl_rcopy_ratespec(struct tc_ratespec *dst, + struct rtnl_ratespec *src) +{ + dst->cell_log = src->rs_cell_log; + dst->overhead = src->rs_overhead; + dst->cell_align = src->rs_cell_align; + dst->mpu = src->rs_mpu; + dst->rate = src->rs_rate64 > 0xFFFFFFFFull ? 0xFFFFFFFFull : + (uint32_t)src->rs_rate64; +} + +/*****************************************************************************/ + +static inline int build_sysconf_path(char **strp, const char *filename) +{ + char *sysconfdir; + + sysconfdir = getenv("NLSYSCONFDIR"); + + if (!sysconfdir) + sysconfdir = _NL_SYSCONFDIR_LIBNL; + + return asprintf(strp, "%s/%s", sysconfdir, filename); +} + +/*****************************************************************************/ + +static inline int rtnl_tc_calc_txtime64(int bufsize, uint64_t rate) +{ + return ((double)bufsize / (double)rate) * 1000000.0; +} + +static inline int rtnl_tc_calc_bufsize64(int txtime, uint64_t rate) +{ + return ((double)txtime * (double)rate) / 1000000.0; +} + +#endif /* __LIB_ROUTE_NL_ROUTE_H__ */ diff --git a/libnl/lib/route/pktloc.c b/libnl/lib/route/pktloc.c new file mode 100644 index 0000000..7f320d9 --- /dev/null +++ b/libnl/lib/route/pktloc.c @@ -0,0 +1,259 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2013 Thomas Graf + */ + +/** + * @ingroup tc + * @defgroup pktloc Packet Location Aliasing + * Packet Location Aliasing + * + * The packet location aliasing interface eases the use of offset definitions + * inside packets by allowing them to be referenced by name. Known positions + * of protocol fields are stored in a configuration file and associated with + * a name for later reference. The configuration file is distributed with the + * library and provides a well defined set of definitions for most common + * protocol fields. + * + * @section pktloc_examples Examples + * @par Example 1.1 Looking up a packet location + * @code + * struct rtnl_pktloc *loc; + * + * rtnl_pktloc_lookup("ip.src", &loc); + * @endcode + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +#include "nl-route.h" + +#include "pktloc_syntax.h" +#include "pktloc_grammar.h" + +/** @cond SKIP */ +#define PKTLOC_NAME_HT_SIZ 256 + +static struct nl_list_head pktloc_name_ht[PKTLOC_NAME_HT_SIZ]; + +/* djb2 */ +static unsigned int pktloc_hash(const char *str) +{ + unsigned long hash = 5381; + int c; + + while ((c = *str++)) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + + return hash % PKTLOC_NAME_HT_SIZ; +} + +static int __pktloc_lookup(const char *name, struct rtnl_pktloc **result) +{ + struct rtnl_pktloc *loc; + int hash; + + hash = pktloc_hash(name); + nl_list_for_each_entry(loc, &pktloc_name_ht[hash], list) { + if (!strcasecmp(loc->name, name)) { + loc->refcnt++; + *result = loc; + return 0; + } + } + + return -NLE_OBJ_NOTFOUND; +} + +extern int pktloc_parse(void *scanner); + +static void rtnl_pktloc_free(struct rtnl_pktloc *loc) +{ + if (!loc) + return; + + free(loc->name); + free(loc); +} + +static int read_pktlocs(void) +{ + YY_BUFFER_STATE buf = NULL; + yyscan_t scanner = NULL; + static time_t last_read; + struct stat st; + char *path; + int i, err; + FILE *fd; + + if (build_sysconf_path(&path, "pktloc") < 0) + return -NLE_NOMEM; + + /* if stat fails, just try to read the file */ + if (stat(path, &st) == 0) { + /* Don't re-read file if file is unchanged */ + if (last_read == st.st_mtime) { + err = 0; + goto errout; + } + } + + NL_DBG(2, "Reading packet location file \"%s\"\n", path); + + if (!(fd = fopen(path, "re"))) { + err = -NLE_PKTLOC_FILE; + goto errout; + } + + for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) { + struct rtnl_pktloc *loc, *n; + + nl_list_for_each_entry_safe(loc, n, &pktloc_name_ht[i], list) + rtnl_pktloc_put(loc); + + nl_init_list_head(&pktloc_name_ht[i]); + } + + if (pktloc_lex_init(&scanner) < 0) { + err = -NLE_FAILURE; + goto errout_close; + } + + buf = pktloc__create_buffer(fd, YY_BUF_SIZE, scanner); + pktloc__switch_to_buffer(buf, scanner); + + if ((err = pktloc_parse(scanner)) != 0) { + pktloc__delete_buffer(buf, scanner); + err = -NLE_PARSE_ERR; + goto errout_scanner; + } + + last_read = st.st_mtime; + +errout_scanner: + pktloc_lex_destroy(scanner); +errout_close: + fclose(fd); +errout: + free(path); + + return err; +} + +/** @endcond */ + +/** + * Lookup packet location alias + * @arg name Name of packet location. + * @arg result Result pointer + * + * Tries to find a matching packet location alias for the supplied + * packet location name. + * + * The file containing the packet location definitions is automatically + * re-read if its modification time has changed since the last call. + * + * The returned packet location has to be returned after use by calling + * rtnl_pktloc_put() in order to allow freeing its memory after the last + * user has abandoned it. + * + * @return 0 on success or a negative error code. + * @retval NLE_PKTLOC_FILE Unable to open packet location file. + * @retval NLE_OBJ_NOTFOUND No matching packet location alias found. + */ +int rtnl_pktloc_lookup(const char *name, struct rtnl_pktloc **result) +{ + int err; + + if ((err = read_pktlocs()) < 0) + return err; + + return __pktloc_lookup(name, result); +} + +/** + * Allocate packet location object + */ +struct rtnl_pktloc *rtnl_pktloc_alloc(void) +{ + struct rtnl_pktloc *loc; + + if (!(loc = calloc(1, sizeof(*loc)))) + return NULL; + + loc->refcnt = 1; + nl_init_list_head(&loc->list); + + return loc; +} + +/** + * Return reference of a packet location + * @arg loc packet location object. + */ +void rtnl_pktloc_put(struct rtnl_pktloc *loc) +{ + if (!loc) + return; + + loc->refcnt--; + if (loc->refcnt <= 0) + rtnl_pktloc_free(loc); +} + +/** + * Add a packet location to the hash table + * @arg loc packet location object + * + * @return 0 on success or a negative error code. + */ +int rtnl_pktloc_add(struct rtnl_pktloc *loc) +{ + struct rtnl_pktloc *l; + + if (__pktloc_lookup(loc->name, &l) == 0) { + rtnl_pktloc_put(l); + return -NLE_EXIST; + } + + NL_DBG(2, "New packet location entry \"%s\" align=%u layer=%u " + "offset=%u mask=%#x shift=%u refnt=%u\n", + loc->name, loc->align, loc->layer, loc->offset, + loc->mask, loc->shift, loc->refcnt); + + nl_list_add_tail(&loc->list, &pktloc_name_ht[pktloc_hash(loc->name)]); + + return 0; +} + +void rtnl_pktloc_foreach(void (*cb)(struct rtnl_pktloc *, void *), void *arg) +{ + struct rtnl_pktloc *loc; + int i; + + /* ignore errors */ + read_pktlocs(); + + for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) + nl_list_for_each_entry(loc, &pktloc_name_ht[i], list) + cb(loc, arg); +} + +static int _nl_init pktloc_init(void) +{ + int i; + + for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) + nl_init_list_head(&pktloc_name_ht[i]); + + return 0; +} + +/** @} */ diff --git a/libnl/lib/route/pktloc_grammar.l b/libnl/lib/route/pktloc_grammar.l new file mode 100644 index 0000000..b2a2236 --- /dev/null +++ b/libnl/lib/route/pktloc_grammar.l @@ -0,0 +1,58 @@ +%top{ + #include "nl-default.h" +} +%{ + #include + + #include + #include + #include + + #include "pktloc_syntax.h" + + int pktloc_get_column(yyscan_t); + void pktloc_set_column(int, yyscan_t); +%} + +%option 8bit +%option reentrant +%option warn +%option noyywrap +%option noinput +%option nounput +%option bison-bridge +%option bison-locations +%option prefix="pktloc_" + +%% + +[ \t\r\n]+ + +"#".* + +[[:digit:]]+ | +0[xX][[:xdigit:]]+ { + yylval->i = strtoul(yytext, NULL, 0); + return NUMBER; + } + +"+" { return yylval->i = yytext[0]; } + +[uU]8 { yylval->i = TCF_EM_ALIGN_U8; return ALIGN; } +[uU]16 { yylval->i = TCF_EM_ALIGN_U16; return ALIGN; } +[uU]32 { yylval->i = TCF_EM_ALIGN_U32; return ALIGN; } + +[lL][iI][nN][kK] | +[eE][tT][hH] { yylval->i = TCF_LAYER_LINK; return LAYER; } +[nN][eE][tT] | +[iI][pP] { yylval->i = TCF_LAYER_NETWORK; return LAYER; } +[tT][rR][aA][nN][sS][pP][oO][rR][tT] | +[tT][cC][pP] { yylval->i = TCF_LAYER_TRANSPORT; return LAYER; } + + +[^ \t\r\n+]+ { + yylval->s = strdup(yytext); + if (yylval->s == NULL) + return ERROR; + return NAME; + } diff --git a/libnl/lib/route/pktloc_syntax.y b/libnl/lib/route/pktloc_syntax.y new file mode 100644 index 0000000..505f4c8 --- /dev/null +++ b/libnl/lib/route/pktloc_syntax.y @@ -0,0 +1,106 @@ +%{ +#include "nl-default.h" + +#include +#include +#include + +#include "nl-route.h" +%} + +%locations +%error-verbose +%define api.pure +%name-prefix "pktloc_" + +%parse-param {void *scanner} +%lex-param {void *scanner} +%expect 1 + +%union { + struct rtnl_pktloc *l; + uint32_t i; + char *s; +} + +%{ +extern int pktloc_lex(YYSTYPE *, YYLTYPE *, void *); + +#define pktloc_error yyerror +static void yyerror(YYLTYPE *locp, void *scanner, const char *msg) +{ + NL_DBG(1, "Error while parsing packet location file: %s\n", msg); +} +%} + +%token ERROR NUMBER LAYER ALIGN +%token NAME + +%type mask layer align shift +%type location + +%destructor { free($$); } NAME + +%start input + +%% + +input: + /* empty */ + | location input + ; + +location: + NAME align layer NUMBER mask shift + { + struct rtnl_pktloc *loc; + + if (!(loc = rtnl_pktloc_alloc())) { + NL_DBG(1, "Allocating a packet location " + "object failed.\n"); + YYABORT; + } + + loc->name = $1; + loc->align = $2; + loc->layer = $3; + loc->offset = $4; + loc->mask = $5; + loc->shift = $6; + + if (rtnl_pktloc_add(loc) < 0) { + NL_DBG(1, "Duplicate packet location entry " + "\"%s\"\n", $1); + } + + $$ = loc; + } + ; + +align: + ALIGN + { $$ = $1; } + | NUMBER + { $$ = $1; } + ; + +layer: + /* empty */ + { $$ = TCF_LAYER_NETWORK; } + | LAYER '+' + { $$ = $1; } + ; + +mask: + /* empty */ + { $$ = 0; } + | NUMBER + { $$ = $1; } + ; + +shift: + /* empty */ + { $$ = 0; } + | NUMBER + { $$ = $1; } + ; diff --git a/libnl/lib/route/qdisc.c b/libnl/lib/route/qdisc.c new file mode 100644 index 0000000..9887e0d --- /dev/null +++ b/libnl/lib/route/qdisc.c @@ -0,0 +1,606 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup tc + * @defgroup qdisc Queueing Disciplines + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include +#include + +#include "tc-api.h" + +static struct nl_cache_ops rtnl_qdisc_ops; +static struct nl_object_ops qdisc_obj_ops; + +static int qdisc_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *n, struct nl_parser_param *pp) +{ + struct rtnl_qdisc *qdisc; + int err; + + if (!(qdisc = rtnl_qdisc_alloc())) + return -NLE_NOMEM; + + if ((err = rtnl_tc_msg_parse(n, TC_CAST(qdisc))) < 0) + goto errout; + + err = pp->pp_cb(OBJ_CAST(qdisc), pp); +errout: + rtnl_qdisc_put(qdisc); + + return err; +} + +static int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk) +{ + struct tcmsg tchdr = { + .tcm_family = AF_UNSPEC, + .tcm_ifindex = c->c_iarg1, + }; + + return nl_send_simple(sk, RTM_GETQDISC, NLM_F_DUMP, &tchdr, + sizeof(tchdr)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_qdisc *rtnl_qdisc_alloc(void) +{ + struct rtnl_tc *tc; + + tc = TC_CAST(nl_object_alloc(&qdisc_obj_ops)); + if (tc) + tc->tc_type = RTNL_TC_TYPE_QDISC; + + return (struct rtnl_qdisc *) tc; +} + +void rtnl_qdisc_put(struct rtnl_qdisc *qdisc) +{ + nl_object_put((struct nl_object *) qdisc); +} + +/** @} */ + +/** + * @name Addition / Modification / Deletion + * @{ + */ + +static int build_qdisc_msg(struct rtnl_qdisc *qdisc, int type, int flags, + struct nl_msg **result) +{ + if (!(qdisc->ce_mask & TCA_ATTR_IFINDEX)) { + APPBUG("ifindex must be specified"); + return -NLE_MISSING_ATTR; + } + + return rtnl_tc_msg_build(TC_CAST(qdisc), type, flags, result); +} + +/** + * Build a netlink message requesting the addition of a qdisc + * @arg qdisc Qdisc to add + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_qdisc_add() with + * the exception that it will not send the message but return it int the + * provided return pointer instead. + * + * @see rtnl_qdisc_add() + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags, + struct nl_msg **result) +{ + if (!(qdisc->ce_mask & (TCA_ATTR_HANDLE | TCA_ATTR_PARENT))) { + APPBUG("handle or parent must be specified"); + return -NLE_MISSING_ATTR; + } + + return build_qdisc_msg(qdisc, RTM_NEWQDISC, flags, result); +} + +/** + * Add qdisc + * @arg sk Netlink socket + * @arg qdisc Qdisc to add + * @arg flags Additional netlink message flags + * + * Builds a \c RTM_NEWQDISC netlink message requesting the addition + * of a new qdisc and sends the message to the kernel. The configuration + * of the qdisc is derived from the attributes of the specified qdisc. + * + * The following flags may be specified: + * - \c NLM_F_CREATE: Create qdisc if it does not exist, otherwise + * -NLE_OBJ_NOTFOUND is returned. + * - \c NLM_F_REPLACE: If another qdisc is already attached to the + * parent, replace it even if the handles mismatch. + * - \c NLM_F_EXCL: Return -NLE_EXISTS if a qdisc with matching + * handle exists already. + * + * Existing qdiscs with matching handles will be updated, unless the + * flag \c NLM_F_EXCL is specified. If their handles do not match, the + * error -NLE_EXISTS is returned unless the flag \c NLM_F_REPLACE is + * specified in which case the existing qdisc is replaced with the new + * one. If no matching qdisc exists, it will be created if the flag + * \c NLM_F_CREATE is set, otherwise the error -NLE_OBJ_NOTFOUND is + * returned. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build netlink message requesting the update of a qdisc + * @arg qdisc Qdisc to update + * @arg new Qdisc with updated attributes + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_qdisc_update() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_qdisc_update() + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_build_update_request(struct rtnl_qdisc *qdisc, + struct rtnl_qdisc *new, int flags, + struct nl_msg **result) +{ + if (flags & (NLM_F_CREATE | NLM_F_EXCL)) { + APPBUG("NLM_F_CREATE and NLM_F_EXCL may not be used here, " + "use rtnl_qdisc_add()"); + return -NLE_INVAL; + } + + if (!(qdisc->ce_mask & TCA_ATTR_IFINDEX)) { + APPBUG("ifindex must be specified"); + return -NLE_MISSING_ATTR; + } + + if (!(qdisc->ce_mask & (TCA_ATTR_HANDLE | TCA_ATTR_PARENT))) { + APPBUG("handle or parent must be specified"); + return -NLE_MISSING_ATTR; + } + + rtnl_tc_set_ifindex(TC_CAST(new), qdisc->q_ifindex); + + if (qdisc->ce_mask & TCA_ATTR_HANDLE) + rtnl_tc_set_handle(TC_CAST(new), qdisc->q_handle); + + if (qdisc->ce_mask & TCA_ATTR_PARENT) + rtnl_tc_set_parent(TC_CAST(new), qdisc->q_parent); + + return build_qdisc_msg(new, RTM_NEWQDISC, flags, result); +} + +/** + * Update qdisc + * @arg sk Netlink socket + * @arg qdisc Qdisc to update + * @arg new Qdisc with updated attributes + * @arg flags Additional netlink message flags + * + * Builds a \c RTM_NEWQDISC netlink message requesting the update + * of an existing qdisc and sends the message to the kernel. + * + * This function is a varation of rtnl_qdisc_add() to update qdiscs + * if the qdisc to be updated is available as qdisc object. The + * behaviour is identical to the one of rtnl_qdisc_add except that + * before constructing the message, it copies the \c ifindex, + * \c handle, and \c parent from the original \p qdisc to the \p new + * qdisc. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_update(struct nl_sock *sk, struct rtnl_qdisc *qdisc, + struct rtnl_qdisc *new, int flags) +{ + struct nl_msg *msg; + int err; + + err = rtnl_qdisc_build_update_request(qdisc, new, flags, &msg); + if (err < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build netlink message requesting the deletion of a qdisc + * @arg qdisc Qdisc to delete + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_qdisc_delete() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_qdisc_delete() + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc, + struct nl_msg **result) +{ + struct nl_msg *msg; + struct tcmsg tchdr; + uint32_t required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT; + + if ((qdisc->ce_mask & required) != required) { + APPBUG("ifindex and parent must be specified"); + return -NLE_MISSING_ATTR; + } + + if (!(msg = nlmsg_alloc_simple(RTM_DELQDISC, 0))) + return -NLE_NOMEM; + + memset(&tchdr, 0, sizeof(tchdr)); + + tchdr.tcm_family = AF_UNSPEC; + tchdr.tcm_ifindex = qdisc->q_ifindex; + tchdr.tcm_parent = qdisc->q_parent; + + if (qdisc->ce_mask & TCA_ATTR_HANDLE) + tchdr.tcm_handle = qdisc->q_handle; + + if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (qdisc->ce_mask & TCA_ATTR_KIND) + NLA_PUT_STRING(msg, TCA_KIND, qdisc->q_kind); + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +/** + * Delete qdisc + * @arg sk Netlink socket + * @arg qdisc Qdisc to add + * + * Builds a \c RTM_NEWQDISC netlink message requesting the deletion + * of a qdisc and sends the message to the kernel. + * + * The message is constructed out of the following attributes: + * - \c ifindex and \c parent + * - \c handle (optional, must match if provided) + * - \c kind (optional, must match if provided) + * + * All other qdisc attributes including all qdisc type specific + * attributes are ignored. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note It is not possible to delete default qdiscs. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** @} */ + +/** + * @name Cache Related Functions + * @{ + */ + +/** + * Allocate a cache and fill it with all configured qdiscs + * @arg sk Netlink socket + * @arg result Pointer to store the created cache + * + * Allocates a new qdisc cache and fills it with a list of all configured + * qdiscs on all network devices. Release the cache with nl_cache_free(). + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result); +} + +/** + * Search qdisc by interface index and parent + * @arg cache Qdisc cache + * @arg ifindex Interface index + * @arg parent Handle of parent qdisc + * + * Searches a qdisc cache previously allocated with rtnl_qdisc_alloc_cache() + * and searches for a qdisc matching the interface index and parent qdisc. + * + * The reference counter is incremented before returning the qdisc, therefore + * the reference must be given back with rtnl_qdisc_put() after usage. + * + * @return pointer to qdisc inside the cache or NULL if no match was found. + */ +struct rtnl_qdisc *rtnl_qdisc_get_by_parent(struct nl_cache *cache, + int ifindex, uint32_t parent) +{ + struct rtnl_qdisc *q; + + if (cache->c_ops != &rtnl_qdisc_ops) + return NULL; + + nl_list_for_each_entry(q, &cache->c_items, ce_list) { + if (q->q_parent == parent && + q->q_ifindex == ((unsigned)ifindex)) { + nl_object_get((struct nl_object *) q); + return q; + } + } + + return NULL; +} + +/** + * Search qdisc by kind + * @arg cache Qdisc cache + * @arg ifindex Interface index + * @arg kind Qdisc kind (tbf, htb, cbq, etc) + * + * Searches a qdisc cache previously allocated with rtnl_qdisc_alloc_cache() + * and searches for a qdisc matching the interface index and kind. + * + * The reference counter is incremented before returning the qdisc, therefore + * the reference must be given back with rtnl_qdisc_put() after usage. + * + * @return pointer to qdisc inside the cache or NULL if no match was found. + */ +struct rtnl_qdisc *rtnl_qdisc_get_by_kind(struct nl_cache *cache, + int ifindex, char *kind) +{ + struct rtnl_qdisc *q; + + if (cache->c_ops != &rtnl_qdisc_ops) + return NULL; + + nl_list_for_each_entry(q, &cache->c_items, ce_list) { + if ((q->q_ifindex == ((unsigned)ifindex)) && + (!strcmp(q->q_kind, kind))) { + nl_object_get((struct nl_object *) q); + return q; + } + } + + return NULL; +} + +/** + * Search qdisc by interface index and handle + * @arg cache Qdisc cache + * @arg ifindex Interface index + * @arg handle Handle + * + * Searches a qdisc cache previously allocated with rtnl_qdisc_alloc_cache() + * and searches for a qdisc matching the interface index and handle. + * + * The reference counter is incremented before returning the qdisc, therefore + * the reference must be given back with rtnl_qdisc_put() after usage. + * + * @return Qdisc or NULL if no match was found. + */ +struct rtnl_qdisc *rtnl_qdisc_get(struct nl_cache *cache, int ifindex, + uint32_t handle) +{ + struct rtnl_qdisc *q; + + if (cache->c_ops != &rtnl_qdisc_ops) + return NULL; + + nl_list_for_each_entry(q, &cache->c_items, ce_list) { + if (q->q_handle == handle && + q->q_ifindex == ((unsigned)ifindex)) { + nl_object_get((struct nl_object *) q); + return q; + } + } + + return NULL; +} + +/** @} */ + +/** + * @name Deprecated Functions + * @{ + */ + +/** + * Call a callback for each child class of a qdisc (deprecated) + * + * @deprecated Use of this function is deprecated, it does not allow + * to handle the out of memory situation that can occur. + */ +void rtnl_qdisc_foreach_child(struct rtnl_qdisc *qdisc, struct nl_cache *cache, + void (*cb)(struct nl_object *, void *), void *arg) +{ + struct rtnl_class *filter; + + filter = rtnl_class_alloc(); + if (!filter) + return; + + rtnl_tc_set_parent(TC_CAST(filter), qdisc->q_handle); + rtnl_tc_set_ifindex(TC_CAST(filter), qdisc->q_ifindex); + rtnl_tc_set_kind(TC_CAST(filter), qdisc->q_kind); + + nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg); + + rtnl_class_put(filter); +} + +/** + * Call a callback for each filter attached to the qdisc (deprecated) + * + * @deprecated Use of this function is deprecated, it does not allow + * to handle the out of memory situation that can occur. + */ +void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *qdisc, struct nl_cache *cache, + void (*cb)(struct nl_object *, void *), void *arg) +{ + struct rtnl_cls *filter; + + if (!(filter = rtnl_cls_alloc())) + return; + + rtnl_tc_set_ifindex(TC_CAST(filter), qdisc->q_ifindex); + rtnl_tc_set_parent(TC_CAST(filter), qdisc->q_parent); + + nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg); + rtnl_cls_put(filter); +} + +/** + * Build a netlink message requesting the update of a qdisc + * + * @deprecated Use of this function is deprecated in favour of + * rtnl_qdisc_build_update_request() due to the missing + * possibility of specifying additional flags. + */ +int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc, + struct rtnl_qdisc *new, + struct nl_msg **result) +{ + return rtnl_qdisc_build_update_request(qdisc, new, NLM_F_REPLACE, + result); +} + +/** + * Change attributes of a qdisc + * + * @deprecated Use of this function is deprecated in favour of + * rtnl_qdisc_update() due to the missing possibility of + * specifying additional flags. + */ +int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc, + struct rtnl_qdisc *new) +{ + return rtnl_qdisc_update(sk, qdisc, new, NLM_F_REPLACE); +} + +/** @} */ + +static void qdisc_dump_details(struct rtnl_tc *tc, struct nl_dump_params *p) +{ + struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc; + + nl_dump(p, "refcnt %u", qdisc->q_info); +} + +static struct rtnl_tc_type_ops qdisc_ops = { + .tt_type = RTNL_TC_TYPE_QDISC, + .tt_dump_prefix = "qdisc", + .tt_dump = { + [NL_DUMP_DETAILS] = qdisc_dump_details, + }, +}; + +static struct nl_cache_ops rtnl_qdisc_ops = { + .co_name = "route/qdisc", + .co_hdrsize = sizeof(struct tcmsg), + .co_msgtypes = { + { RTM_NEWQDISC, NL_ACT_NEW, "new" }, + { RTM_DELQDISC, NL_ACT_DEL, "del" }, + { RTM_GETQDISC, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = tc_groups, + .co_request_update = qdisc_request_update, + .co_msg_parser = qdisc_msg_parser, + .co_obj_ops = &qdisc_obj_ops, +}; + +static struct nl_object_ops qdisc_obj_ops = { + .oo_name = "route/qdisc", + .oo_size = sizeof(struct rtnl_qdisc), + .oo_free_data = rtnl_tc_free_data, + .oo_clone = rtnl_tc_clone, + .oo_dump = { + [NL_DUMP_LINE] = rtnl_tc_dump_line, + [NL_DUMP_DETAILS] = rtnl_tc_dump_details, + [NL_DUMP_STATS] = rtnl_tc_dump_stats, + }, + .oo_compare = rtnl_tc_compare, + .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE), +}; + +static void _nl_init qdisc_init(void) +{ + rtnl_tc_type_register(&qdisc_ops); + nl_cache_mngt_register(&rtnl_qdisc_ops); +} + +static void _nl_exit qdisc_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_qdisc_ops); + rtnl_tc_type_unregister(&qdisc_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/blackhole.c b/libnl/lib/route/qdisc/blackhole.c new file mode 100644 index 0000000..8d8f4ba --- /dev/null +++ b/libnl/lib/route/qdisc/blackhole.c @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_blackhole Blackhole + * @{ + */ + +#include "nl-default.h" + +#include + +#include "tc-api.h" + +static struct rtnl_tc_ops blackhole_ops = { + .to_kind = "blackhole", + .to_type = RTNL_TC_TYPE_QDISC, +}; + +static void _nl_init blackhole_init(void) +{ + rtnl_tc_register(&blackhole_ops); +} + +static void _nl_exit blackhole_exit(void) +{ + rtnl_tc_unregister(&blackhole_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/cbq.c b/libnl/lib/route/qdisc/cbq.c new file mode 100644 index 0000000..b9a6696 --- /dev/null +++ b/libnl/lib/route/qdisc/cbq.c @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "tc-api.h" + +struct rtnl_cbq { + struct tc_cbq_lssopt cbq_lss; + struct tc_ratespec cbq_rate; + struct tc_cbq_wrropt cbq_wrr; + struct tc_cbq_ovl cbq_ovl; + struct tc_cbq_fopt cbq_fopt; + struct tc_cbq_police cbq_police; +}; + +/** + * @ingroup qdisc + * @ingroup class + * @defgroup qdisc_cbq Class Based Queueing (CBQ) + * @{ + */ + +static const struct trans_tbl ovl_strategies[] = { + __ADD(TC_CBQ_OVL_CLASSIC,classic), + __ADD(TC_CBQ_OVL_DELAY,delay), + __ADD(TC_CBQ_OVL_LOWPRIO,lowprio), + __ADD(TC_CBQ_OVL_DROP,drop), + __ADD(TC_CBQ_OVL_RCLASSIC,rclassic), +}; + +/** + * Convert a CBQ OVL strategy to a character string + * @arg type CBQ OVL strategy + * @arg buf destination buffer + * @arg len length of destination buffer + * + * Converts a CBQ OVL strategy to a character string and stores in the + * provided buffer. Returns the destination buffer or the type + * encoded in hex if no match was found. + */ +char *nl_ovl_strategy2str(int type, char *buf, size_t len) +{ + return __type2str(type, buf, len, ovl_strategies, + ARRAY_SIZE(ovl_strategies)); +} + +/** + * Convert a string to a CBQ OVL strategy + * @arg name CBQ OVL stragegy name + * + * Converts a CBQ OVL stragegy name to it's corresponding CBQ OVL strategy + * type. Returns the type or -1 if none was found. + */ +int nl_str2ovl_strategy(const char *name) +{ + return __str2type(name, ovl_strategies, ARRAY_SIZE(ovl_strategies)); +} + +static struct nla_policy cbq_policy[TCA_CBQ_MAX+1] = { + [TCA_CBQ_LSSOPT] = { .minlen = sizeof(struct tc_cbq_lssopt) }, + [TCA_CBQ_RATE] = { .minlen = sizeof(struct tc_ratespec) }, + [TCA_CBQ_WRROPT] = { .minlen = sizeof(struct tc_cbq_wrropt) }, + [TCA_CBQ_OVL_STRATEGY] = { .minlen = sizeof(struct tc_cbq_ovl) }, + [TCA_CBQ_FOPT] = { .minlen = sizeof(struct tc_cbq_fopt) }, + [TCA_CBQ_POLICE] = { .minlen = sizeof(struct tc_cbq_police) }, +}; + +static int cbq_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct nlattr *tb[TCA_CBQ_MAX + 1]; + struct rtnl_cbq *cbq = data; + int err; + + err = tca_parse(tb, TCA_CBQ_MAX, tc, cbq_policy); + if (err < 0) + return err; + + nla_memcpy(&cbq->cbq_lss, tb[TCA_CBQ_LSSOPT], sizeof(cbq->cbq_lss)); + nla_memcpy(&cbq->cbq_rate, tb[TCA_CBQ_RATE], sizeof(cbq->cbq_rate)); + nla_memcpy(&cbq->cbq_wrr, tb[TCA_CBQ_WRROPT], sizeof(cbq->cbq_wrr)); + nla_memcpy(&cbq->cbq_fopt, tb[TCA_CBQ_FOPT], sizeof(cbq->cbq_fopt)); + nla_memcpy(&cbq->cbq_ovl, tb[TCA_CBQ_OVL_STRATEGY], + sizeof(cbq->cbq_ovl)); + nla_memcpy(&cbq->cbq_police, tb[TCA_CBQ_POLICE], + sizeof(cbq->cbq_police)); + + return 0; +} + +static void cbq_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_cbq *cbq = data; + double r, rbit; + char *ru, *rubit; + + if (!cbq) + return; + + r = nl_cancel_down_bytes(cbq->cbq_rate.rate, &ru); + rbit = nl_cancel_down_bits(cbq->cbq_rate.rate * 8, &rubit); + + nl_dump(p, " rate %.2f%s/s (%.0f%s) prio %u", + r, ru, rbit, rubit, cbq->cbq_wrr.priority); +} + +static void cbq_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_cbq *cbq = data; + char *unit, buf[32]; + double w; + uint32_t el; + + if (!cbq) + return; + + w = nl_cancel_down_bits(cbq->cbq_wrr.weight * 8, &unit); + + nl_dump(p, "avgpkt %u mpu %u cell %u allot %u weight %.0f%s\n", + cbq->cbq_lss.avpkt, + cbq->cbq_rate.mpu, + 1 << cbq->cbq_rate.cell_log, + cbq->cbq_wrr.allot, w, unit); + + el = cbq->cbq_lss.ewma_log; + nl_dump_line(p, " minidle %uus maxidle %uus offtime " + "%uus level %u ewma_log %u\n", + nl_ticks2us(cbq->cbq_lss.minidle >> el), + nl_ticks2us(cbq->cbq_lss.maxidle >> el), + nl_ticks2us(cbq->cbq_lss.offtime >> el), + cbq->cbq_lss.level, + cbq->cbq_lss.ewma_log); + + nl_dump_line(p, " penalty %uus strategy %s ", + nl_ticks2us(cbq->cbq_ovl.penalty), + nl_ovl_strategy2str(cbq->cbq_ovl.strategy, buf, sizeof(buf))); + + nl_dump(p, "split %s defmap 0x%08x ", + rtnl_tc_handle2str(cbq->cbq_fopt.split, buf, sizeof(buf)), + cbq->cbq_fopt.defmap); + + nl_dump(p, "police %s", + nl_police2str(cbq->cbq_police.police, buf, sizeof(buf))); +} + +static void cbq_dump_stats(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct tc_cbq_xstats *x; + + if (!(x = tca_xstats(tc))) + return; + + nl_dump_line(p, " borrows overact " + " avgidle undertime\n"); + nl_dump_line(p, " %10u %10u %10u %10u\n", + x->borrows, x->overactions, x->avgidle, x->undertime); +} + +static struct rtnl_tc_ops cbq_qdisc_ops = { + .to_kind = "cbq", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_cbq), + .to_msg_parser = cbq_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = cbq_dump_line, + [NL_DUMP_DETAILS] = cbq_dump_details, + [NL_DUMP_STATS] = cbq_dump_stats, + }, +}; + +static struct rtnl_tc_ops cbq_class_ops = { + .to_kind = "cbq", + .to_type = RTNL_TC_TYPE_CLASS, + .to_size = sizeof(struct rtnl_cbq), + .to_msg_parser = cbq_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = cbq_dump_line, + [NL_DUMP_DETAILS] = cbq_dump_details, + [NL_DUMP_STATS] = cbq_dump_stats, + }, +}; + +static void _nl_init cbq_init(void) +{ + rtnl_tc_register(&cbq_qdisc_ops); + rtnl_tc_register(&cbq_class_ops); +} + +static void _nl_exit cbq_exit(void) +{ + rtnl_tc_unregister(&cbq_qdisc_ops); + rtnl_tc_unregister(&cbq_class_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/dsmark.c b/libnl/lib/route/qdisc/dsmark.c new file mode 100644 index 0000000..f5718f9 --- /dev/null +++ b/libnl/lib/route/qdisc/dsmark.c @@ -0,0 +1,421 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup qdisc + * @ingroup class + * @defgroup qdisc_dsmark Differentiated Services Marker (DSMARK) + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include + +#include "tc-api.h" + +/** @cond SKIP */ +struct rtnl_dsmark_qdisc { + uint16_t qdm_indices; + uint16_t qdm_default_index; + uint32_t qdm_set_tc_index; + uint32_t qdm_mask; +}; + +struct rtnl_dsmark_class { + uint8_t cdm_bmask; + uint8_t cdm_value; + uint32_t cdm_mask; +}; + +#define SCH_DSMARK_ATTR_INDICES 0x1 +#define SCH_DSMARK_ATTR_DEFAULT_INDEX 0x2 +#define SCH_DSMARK_ATTR_SET_TC_INDEX 0x4 + +#define SCH_DSMARK_ATTR_MASK 0x1 +#define SCH_DSMARK_ATTR_VALUE 0x2 +/** @endcond */ + +static struct nla_policy dsmark_policy[TCA_DSMARK_MAX+1] = { + [TCA_DSMARK_INDICES] = { .type = NLA_U16 }, + [TCA_DSMARK_DEFAULT_INDEX] = { .type = NLA_U16 }, + [TCA_DSMARK_SET_TC_INDEX] = { .type = NLA_FLAG }, + [TCA_DSMARK_VALUE] = { .type = NLA_U8 }, + [TCA_DSMARK_MASK] = { .type = NLA_U8 }, +}; + +static int dsmark_qdisc_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_dsmark_qdisc *dsmark = data; + struct nlattr *tb[TCA_DSMARK_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_DSMARK_MAX, tc, dsmark_policy); + if (err < 0) + return err; + + if (tb[TCA_DSMARK_INDICES]) { + dsmark->qdm_indices = nla_get_u16(tb[TCA_DSMARK_INDICES]); + dsmark->qdm_mask |= SCH_DSMARK_ATTR_INDICES; + } + + if (tb[TCA_DSMARK_DEFAULT_INDEX]) { + dsmark->qdm_default_index = + nla_get_u16(tb[TCA_DSMARK_DEFAULT_INDEX]); + dsmark->qdm_mask |= SCH_DSMARK_ATTR_DEFAULT_INDEX; + } + + if (tb[TCA_DSMARK_SET_TC_INDEX]) { + dsmark->qdm_set_tc_index = 1; + dsmark->qdm_mask |= SCH_DSMARK_ATTR_SET_TC_INDEX; + } + + return 0; +} + +static int dsmark_class_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_dsmark_class *dsmark = data; + struct nlattr *tb[TCA_DSMARK_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_DSMARK_MAX, tc, dsmark_policy); + if (err < 0) + return err; + + if (tb[TCA_DSMARK_MASK]) { + dsmark->cdm_bmask = nla_get_u8(tb[TCA_DSMARK_MASK]); + dsmark->cdm_mask |= SCH_DSMARK_ATTR_MASK; + } + + if (tb[TCA_DSMARK_VALUE]) { + dsmark->cdm_value = nla_get_u8(tb[TCA_DSMARK_VALUE]); + dsmark->cdm_mask |= SCH_DSMARK_ATTR_VALUE; + } + + return 0; +} + +static void dsmark_qdisc_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_dsmark_qdisc *dsmark = data; + + if (dsmark && (dsmark->qdm_mask & SCH_DSMARK_ATTR_INDICES)) + nl_dump(p, " indices 0x%04x", dsmark->qdm_indices); +} + +static void dsmark_qdisc_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_dsmark_qdisc *dsmark = data; + + if (!dsmark) + return; + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_DEFAULT_INDEX) + nl_dump(p, " default index 0x%04x", dsmark->qdm_default_index); + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_SET_TC_INDEX) + nl_dump(p, " set-tc-index"); +} + +static void dsmark_class_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_dsmark_class *dsmark = data; + + if (!dsmark) + return; + + if (dsmark->cdm_mask & SCH_DSMARK_ATTR_VALUE) + nl_dump(p, " value 0x%02x", dsmark->cdm_value); + + if (dsmark->cdm_mask & SCH_DSMARK_ATTR_MASK) + nl_dump(p, " mask 0x%02x", dsmark->cdm_bmask); +} + +static int dsmark_qdisc_msg_fill(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) +{ + struct rtnl_dsmark_qdisc *dsmark = data; + + if (!dsmark) + return 0; + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_INDICES) + NLA_PUT_U16(msg, TCA_DSMARK_INDICES, dsmark->qdm_indices); + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_DEFAULT_INDEX) + NLA_PUT_U16(msg, TCA_DSMARK_DEFAULT_INDEX, + dsmark->qdm_default_index); + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_SET_TC_INDEX) + NLA_PUT_FLAG(msg, TCA_DSMARK_SET_TC_INDEX); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int dsmark_class_msg_fill(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) +{ + struct rtnl_dsmark_class *dsmark = data; + + if (!dsmark) + return 0; + + if (dsmark->cdm_mask & SCH_DSMARK_ATTR_MASK) + NLA_PUT_U8(msg, TCA_DSMARK_MASK, dsmark->cdm_bmask); + + if (dsmark->cdm_mask & SCH_DSMARK_ATTR_VALUE) + NLA_PUT_U8(msg, TCA_DSMARK_VALUE, dsmark->cdm_value); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +/** + * @name Class Attribute Access + * @{ + */ + +/** + * Set bitmask of DSMARK class. + * @arg class DSMARK class to be modified. + * @arg mask New bitmask. + * @return 0 on success or a negative error code. + */ +int rtnl_class_dsmark_set_bitmask(struct rtnl_class *class, uint8_t mask) +{ + struct rtnl_dsmark_class *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(class)))) + return -NLE_NOMEM; + + dsmark->cdm_bmask = mask; + dsmark->cdm_mask |= SCH_DSMARK_ATTR_MASK; + + return 0; +} + +/** + * Get bitmask of DSMARK class. + * @arg class DSMARK class. + * @return Bitmask or a negative error code. + */ +int rtnl_class_dsmark_get_bitmask(struct rtnl_class *class) +{ + struct rtnl_dsmark_class *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(class)))) + return -NLE_NOMEM; + + if (dsmark->cdm_mask & SCH_DSMARK_ATTR_MASK) + return dsmark->cdm_bmask; + else + return -NLE_NOATTR; +} + +/** + * Set value of DSMARK class. + * @arg class DSMARK class to be modified. + * @arg value New value. + * @return 0 on success or a negative errror code. + */ +int rtnl_class_dsmark_set_value(struct rtnl_class *class, uint8_t value) +{ + struct rtnl_dsmark_class *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(class)))) + return -NLE_NOMEM; + + dsmark->cdm_value = value; + dsmark->cdm_mask |= SCH_DSMARK_ATTR_VALUE; + + return 0; +} + +/** + * Get value of DSMARK class. + * @arg class DSMARK class. + * @return Value or a negative error code. + */ +int rtnl_class_dsmark_get_value(struct rtnl_class *class) +{ + struct rtnl_dsmark_class *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(class)))) + return -NLE_NOMEM; + + if (dsmark->cdm_mask & SCH_DSMARK_ATTR_VALUE) + return dsmark->cdm_value; + else + return -NLE_NOATTR; +} + +/** @} */ + +/** + * @name Qdisc Attribute Access + * @{ + */ + +/** + * Set indices of DSMARK qdisc. + * @arg qdisc DSMARK qdisc to be modified. + * @arg indices New indices. + */ +int rtnl_qdisc_dsmark_set_indices(struct rtnl_qdisc *qdisc, uint16_t indices) +{ + struct rtnl_dsmark_qdisc *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + dsmark->qdm_indices = indices; + dsmark->qdm_mask |= SCH_DSMARK_ATTR_INDICES; + + return 0; +} + +/** + * Get indices of DSMARK qdisc. + * @arg qdisc DSMARK qdisc. + * @return Indices or a negative error code. + */ +int rtnl_qdisc_dsmark_get_indices(struct rtnl_qdisc *qdisc) +{ + struct rtnl_dsmark_qdisc *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_INDICES) + return dsmark->qdm_indices; + else + return -NLE_NOATTR; +} + +/** + * Set default index of DSMARK qdisc. + * @arg qdisc DSMARK qdisc to be modified. + * @arg default_index New default index. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_dsmark_set_default_index(struct rtnl_qdisc *qdisc, + uint16_t default_index) +{ + struct rtnl_dsmark_qdisc *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + dsmark->qdm_default_index = default_index; + dsmark->qdm_mask |= SCH_DSMARK_ATTR_DEFAULT_INDEX; + + return 0; +} + +/** + * Get default index of DSMARK qdisc. + * @arg qdisc DSMARK qdisc. + * @return Default index or a negative error code. + */ +int rtnl_qdisc_dsmark_get_default_index(struct rtnl_qdisc *qdisc) +{ + struct rtnl_dsmark_qdisc *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_DEFAULT_INDEX) + return dsmark->qdm_default_index; + else + return -NLE_NOATTR; +} + +/** + * Set set-tc-index flag of DSMARK qdisc. + * @arg qdisc DSMARK qdisc to be modified. + * @arg flag Flag indicating whether to enable or disable. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_dsmark_set_set_tc_index(struct rtnl_qdisc *qdisc, int flag) +{ + struct rtnl_dsmark_qdisc *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + dsmark->qdm_set_tc_index = !!flag; + dsmark->qdm_mask |= SCH_DSMARK_ATTR_SET_TC_INDEX; + + return 0; +} + +/** + * Get set-tc-index flag of DSMARK qdisc. + * @arg qdisc DSMARK qdisc to be modified. + * @return 1 or 0 to indicate wehther the flag is enabled or a negative + * error code. + */ +int rtnl_qdisc_dsmark_get_set_tc_index(struct rtnl_qdisc *qdisc) +{ + struct rtnl_dsmark_qdisc *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_SET_TC_INDEX) + return dsmark->qdm_set_tc_index; + else + return -NLE_NOATTR; +} + +/** @} */ + +static struct rtnl_tc_ops dsmark_qdisc_ops = { + .to_kind = "dsmark", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_dsmark_qdisc), + .to_msg_parser = dsmark_qdisc_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = dsmark_qdisc_dump_line, + [NL_DUMP_DETAILS] = dsmark_qdisc_dump_details, + }, + .to_msg_fill = dsmark_qdisc_msg_fill, +}; + +static struct rtnl_tc_ops dsmark_class_ops = { + .to_kind = "dsmark", + .to_type = RTNL_TC_TYPE_CLASS, + .to_size = sizeof(struct rtnl_dsmark_class), + .to_msg_parser = dsmark_class_msg_parser, + .to_dump[NL_DUMP_LINE] = dsmark_class_dump_line, + .to_msg_fill = dsmark_class_msg_fill, +}; + +static void _nl_init dsmark_init(void) +{ + rtnl_tc_register(&dsmark_qdisc_ops); + rtnl_tc_register(&dsmark_class_ops); +} + +static void _nl_exit dsmark_exit(void) +{ + rtnl_tc_unregister(&dsmark_qdisc_ops); + rtnl_tc_unregister(&dsmark_class_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/fifo.c b/libnl/lib/route/qdisc/fifo.c new file mode 100644 index 0000000..9976803 --- /dev/null +++ b/libnl/lib/route/qdisc/fifo.c @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_fifo Packet/Bytes FIFO (pfifo/bfifo) + * @brief + * + * The FIFO qdisc comes in two flavours: + * @par bfifo (Byte FIFO) + * Allows enqueuing until the currently queued volume in bytes exceeds + * the configured limit.backlog contains currently enqueued volume in bytes. + * + * @par pfifo (Packet FIFO) + * Allows enquueing until the currently queued number of packets + * exceeds the configured limit. + * + * The configuration is exactly the same, the decision which of + * the two variations is going to be used is made based on the + * kind of the qdisc (rtnl_tc_set_kind()). + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include + +#include "tc-api.h" + +/** @cond SKIP */ +struct rtnl_fifo { + uint32_t qf_limit; + uint32_t qf_mask; +}; + +#define SCH_FIFO_ATTR_LIMIT 1 +/** @endcond */ + +static int fifo_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_fifo *fifo = data; + struct tc_fifo_qopt *opt; + + if (tc->tc_opts->d_size < sizeof(struct tc_fifo_qopt)) + return -NLE_INVAL; + + opt = (struct tc_fifo_qopt *) tc->tc_opts->d_data; + fifo->qf_limit = opt->limit; + fifo->qf_mask = SCH_FIFO_ATTR_LIMIT; + + return 0; +} + +static void pfifo_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_fifo *fifo = data; + + if (fifo) + nl_dump(p, " limit %u packets", fifo->qf_limit); +} + +static void bfifo_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_fifo *fifo = data; + char *unit; + double r; + + if (!fifo) + return; + + r = nl_cancel_down_bytes(fifo->qf_limit, &unit); + nl_dump(p, " limit %.1f%s", r, unit); +} + +static int fifo_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_fifo *fifo = data; + struct tc_fifo_qopt opts = {0}; + + if (!fifo || !(fifo->qf_mask & SCH_FIFO_ATTR_LIMIT)) + return -NLE_INVAL; + + opts.limit = fifo->qf_limit; + + return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD); +} + +/** + * @name Attribute Modification + * @{ + */ + +/** + * Set limit of FIFO qdisc. + * @arg qdisc FIFO qdisc to be modified. + * @arg limit New limit. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_fifo_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_fifo *fifo; + + if (!(fifo = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + fifo->qf_limit = limit; + fifo->qf_mask |= SCH_FIFO_ATTR_LIMIT; + + return 0; +} + +/** + * Get limit of a FIFO qdisc. + * @arg qdisc FIFO qdisc. + * @return Numeric limit or a negative error code. + */ +int rtnl_qdisc_fifo_get_limit(struct rtnl_qdisc *qdisc) +{ + struct rtnl_fifo *fifo; + + if (!(fifo = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (fifo->qf_mask & SCH_FIFO_ATTR_LIMIT) + return fifo->qf_limit; + else + return -NLE_NOATTR; +} + +/** @} */ + +static struct rtnl_tc_ops pfifo_ops = { + .to_kind = "pfifo", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_fifo), + .to_msg_parser = fifo_msg_parser, + .to_dump[NL_DUMP_LINE] = pfifo_dump_line, + .to_msg_fill = fifo_msg_fill, +}; + +static struct rtnl_tc_ops bfifo_ops = { + .to_kind = "bfifo", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_fifo), + .to_msg_parser = fifo_msg_parser, + .to_dump[NL_DUMP_LINE] = bfifo_dump_line, + .to_msg_fill = fifo_msg_fill, +}; + +static void _nl_init fifo_init(void) +{ + rtnl_tc_register(&pfifo_ops); + rtnl_tc_register(&bfifo_ops); +} + +static void _nl_exit fifo_exit(void) +{ + rtnl_tc_unregister(&pfifo_ops); + rtnl_tc_unregister(&bfifo_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/fq_codel.c b/libnl/lib/route/qdisc/fq_codel.c new file mode 100644 index 0000000..a44ab22 --- /dev/null +++ b/libnl/lib/route/qdisc/fq_codel.c @@ -0,0 +1,382 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_fq_codel Fair Queue CoDel + * @brief + * + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include + +#include "tc-api.h" + +/** @cond SKIP */ +struct rtnl_fq_codel { + int fq_limit; + uint32_t fq_target; + uint32_t fq_interval; + int fq_flows; + uint32_t fq_quantum; + int fq_ecn; + uint32_t fq_mask; +}; + +#define SCH_FQ_CODEL_ATTR_TARGET 0x1 +#define SCH_FQ_CODEL_ATTR_LIMIT 0x2 +#define SCH_FQ_CODEL_ATTR_INTERVAL 0x4 +#define SCH_FQ_CODEL_ATTR_FLOWS 0x8 +#define SCH_FQ_CODEL_ATTR_QUANTUM 0x10 +#define SCH_FQ_CODEL_ATTR_ECN 0x20 +/** @endcond */ + +static struct nla_policy fq_codel_policy[TCA_FQ_CODEL_MAX + 1] = { + [TCA_FQ_CODEL_TARGET] = { .type = NLA_U32 }, + [TCA_FQ_CODEL_LIMIT] = { .type = NLA_U32 }, + [TCA_FQ_CODEL_INTERVAL] = { .type = NLA_U32 }, + [TCA_FQ_CODEL_ECN] = { .type = NLA_U32 }, + [TCA_FQ_CODEL_FLOWS] = { .type = NLA_U32 }, + [TCA_FQ_CODEL_QUANTUM] = { .type = NLA_U32 }, +}; + +static int fq_codel_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_fq_codel *fq_codel = data; + struct nlattr *tb[TCA_FQ_CODEL_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_FQ_CODEL_MAX, tc, fq_codel_policy); + if (err < 0) + return err; + + if (tb[TCA_FQ_CODEL_TARGET]) { + fq_codel->fq_target = nla_get_u32(tb[TCA_FQ_CODEL_TARGET]); + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_TARGET; + } + + if (tb[TCA_FQ_CODEL_INTERVAL]) { + fq_codel->fq_interval = nla_get_u32(tb[TCA_FQ_CODEL_INTERVAL]); + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_INTERVAL; + } + + if (tb[TCA_FQ_CODEL_LIMIT]) { + fq_codel->fq_limit = nla_get_u32(tb[TCA_FQ_CODEL_LIMIT]); + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_LIMIT; + } + + if (tb[TCA_FQ_CODEL_QUANTUM]) { + fq_codel->fq_quantum = nla_get_u32(tb[TCA_FQ_CODEL_QUANTUM]); + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_QUANTUM; + } + + if (tb[TCA_FQ_CODEL_FLOWS]) { + fq_codel->fq_flows = nla_get_u32(tb[TCA_FQ_CODEL_FLOWS]); + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_FLOWS; + } + + if (tb[TCA_FQ_CODEL_ECN]) { + fq_codel->fq_ecn = nla_get_u32(tb[TCA_FQ_CODEL_ECN]); + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_ECN; + } + + return 0; +} + +static void fq_codel_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_fq_codel *fq_codel = data; + + if (!fq_codel) + return; + + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_LIMIT) + nl_dump(p, " limit %u packets", fq_codel->fq_limit); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_TARGET) + nl_dump(p, " target %u", fq_codel->fq_target); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_INTERVAL) + nl_dump(p, " interval %u", fq_codel->fq_interval); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_ECN) + nl_dump(p, " ecn %u", fq_codel->fq_ecn); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_FLOWS) + nl_dump(p, " flows %u", fq_codel->fq_flows); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_QUANTUM) + nl_dump(p, " quantum %u", fq_codel->fq_quantum); +} + +static int fq_codel_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_fq_codel *fq_codel = data; + + if (!fq_codel) + return -NLE_INVAL; + + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_LIMIT) + NLA_PUT_U32(msg, TCA_FQ_CODEL_LIMIT, fq_codel->fq_limit); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_INTERVAL) + NLA_PUT_U32(msg, TCA_FQ_CODEL_INTERVAL, fq_codel->fq_interval); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_TARGET) + NLA_PUT_U32(msg, TCA_FQ_CODEL_TARGET, fq_codel->fq_target); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_QUANTUM) + NLA_PUT_U32(msg, TCA_FQ_CODEL_QUANTUM, fq_codel->fq_quantum); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_FLOWS) + NLA_PUT_U32(msg, TCA_FQ_CODEL_FLOWS, fq_codel->fq_flows); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_ECN) + NLA_PUT_U32(msg, TCA_FQ_CODEL_ECN, fq_codel->fq_ecn); + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; + +} + +/** + * @name Attribute Modification + * @{ + */ + +/** + * Set limit of fq_codel qdisc. + * @arg qdisc fq_codel qdisc to be modified. + * @arg limit New limit. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_fq_codel_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_fq_codel *fq_codel; + + if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + fq_codel->fq_limit = limit; + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_LIMIT; + + return 0; +} + +/** + * Get limit of a fq_codel qdisc. + * @arg qdisc fq_codel qdisc. + * @return Numeric limit or a negative error code. + */ +int rtnl_qdisc_fq_codel_get_limit(struct rtnl_qdisc *qdisc) +{ + struct rtnl_fq_codel *fq_codel; + + if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_LIMIT) + return fq_codel->fq_limit; + else + return -NLE_NOATTR; +} + +/** + * Set target of fq_codel qdisc. + * @arg qdisc fq_codel qdisc to be modified. + * @arg target New target. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_fq_codel_set_target(struct rtnl_qdisc *qdisc, uint32_t target) +{ + struct rtnl_fq_codel *fq_codel; + + if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + fq_codel->fq_target = target; + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_TARGET; + + return 0; +} + +/** + * Get target of a fq_codel qdisc. + * @arg qdisc fq_codel qdisc. + * @return Numeric target or zero. + */ +uint32_t rtnl_qdisc_fq_codel_get_target(struct rtnl_qdisc *qdisc) +{ + struct rtnl_fq_codel *fq_codel; + + if ((fq_codel = rtnl_tc_data(TC_CAST(qdisc))) && + fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_TARGET) + return fq_codel->fq_target; + else + return 0; +} + +/** + * Set interval of fq_codel qdisc. + * @arg qdisc fq_codel qdisc to be modified. + * @arg interval New interval. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_fq_codel_set_interval(struct rtnl_qdisc *qdisc, uint32_t interval) +{ + struct rtnl_fq_codel *fq_codel; + + if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + fq_codel->fq_interval = interval; + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_INTERVAL; + + return 0; +} + +/** + * Get target of a fq_codel qdisc. + * @arg qdisc fq_codel qdisc. + * @return Numeric interval or zero. + */ +uint32_t rtnl_qdisc_fq_codel_get_interval(struct rtnl_qdisc *qdisc) +{ + struct rtnl_fq_codel *fq_codel; + + if ((fq_codel = rtnl_tc_data(TC_CAST(qdisc))) && + fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_INTERVAL) + return fq_codel->fq_interval; + else + return 0; +} + +/** + * Set quantum of fq_codel qdisc. + * @arg qdisc fq_codel qdisc to be modified. + * @arg quantum New quantum. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_fq_codel_set_quantum(struct rtnl_qdisc *qdisc, uint32_t quantum) +{ + struct rtnl_fq_codel *fq_codel; + + if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + fq_codel->fq_quantum = quantum; + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_QUANTUM; + + return 0; +} + +/** + * Get quantum of a fq_codel qdisc. + * @arg qdisc fq_codel qdisc. + * @return Numeric quantum or zero. + */ +uint32_t rtnl_qdisc_fq_codel_get_quantum(struct rtnl_qdisc *qdisc) +{ + struct rtnl_fq_codel *fq_codel; + + if ((fq_codel = rtnl_tc_data(TC_CAST(qdisc))) && + (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_QUANTUM)) + return fq_codel->fq_quantum; + else + return 0; +} + +/** + * Set flows of fq_codel qdisc. + * @arg qdisc fq_codel qdisc to be modified. + * @arg flows New flows value. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_fq_codel_set_flows(struct rtnl_qdisc *qdisc, int flows) +{ + struct rtnl_fq_codel *fq_codel; + + if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + fq_codel->fq_flows = flows; + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_FLOWS; + + return 0; +} + +/** + * Get flows of a fq_codel qdisc. + * @arg qdisc fq_codel qdisc. + * @return Numeric flows or a negative error code. + */ +int rtnl_qdisc_fq_codel_get_flows(struct rtnl_qdisc *qdisc) +{ + struct rtnl_fq_codel *fq_codel; + + if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_FLOWS) + return fq_codel->fq_flows; + else + return -NLE_NOATTR; +} +/** + * Set ecn of fq_codel qdisc. + * @arg qdisc fq_codel qdisc to be modified. + * @arg ecn New ecn value. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_fq_codel_set_ecn(struct rtnl_qdisc *qdisc, int ecn) +{ + struct rtnl_fq_codel *fq_codel; + + if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + fq_codel->fq_ecn = ecn; + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_ECN; + + return 0; +} + +/** + * Get ecn of a fq_codel qdisc. + * @arg qdisc fq_codel qdisc. + * @return Numeric ecn or a negative error code. + */ +int rtnl_qdisc_fq_codel_get_ecn(struct rtnl_qdisc *qdisc) +{ + struct rtnl_fq_codel *fq_codel; + + if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_ECN) + return fq_codel->fq_ecn; + else + return -NLE_NOATTR; +} +/** @} */ + +static struct rtnl_tc_ops fq_codel_ops = { + .to_kind = "fq_codel", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_fq_codel), + .to_msg_parser = fq_codel_msg_parser, + .to_dump[NL_DUMP_LINE] = fq_codel_dump_line, + .to_msg_fill = fq_codel_msg_fill, +}; + +static void _nl_init fq_codel_init(void) +{ + rtnl_tc_register(&fq_codel_ops); +} + +static void _nl_exit fq_codel_exit(void) +{ + rtnl_tc_unregister(&fq_codel_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/hfsc.c b/libnl/lib/route/qdisc/hfsc.c new file mode 100644 index 0000000..49c24c7 --- /dev/null +++ b/libnl/lib/route/qdisc/hfsc.c @@ -0,0 +1,358 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Cong Wang + */ + +/** + * @ingroup qdisc + * @ingroup class + * @defgroup qdisc_hfsc Hierarchical Fair Service Curve (HFSC) + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "tc-api.h" + +/** @cond SKIP */ +struct rtnl_hfsc_qdisc { + uint32_t qh_defcls; + uint32_t qh_mask; +}; + +struct rtnl_hfsc_class { + struct tc_service_curve ch_rsc; + struct tc_service_curve ch_fsc; + struct tc_service_curve ch_usc; + uint32_t ch_mask; +}; + +#define SCH_HFSC_CLS_HAS_RSC 0x001 +#define SCH_HFSC_CLS_HAS_FSC 0x002 +#define SCH_HFSC_CLS_HAS_USC 0x004 + +#define SCH_HFSC_QD_HAS_DEFCLS 0x01 +/** @endcond */ + +static struct nla_policy hfsc_policy[TCA_HFSC_MAX + 1] = { + [TCA_HFSC_RSC] = { .minlen = sizeof(struct tc_service_curve) }, + [TCA_HFSC_FSC] = { .minlen = sizeof(struct tc_service_curve) }, + [TCA_HFSC_USC] = { .minlen = sizeof(struct tc_service_curve) }, +}; + +static int hfsc_qdisc_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_hfsc_qdisc *hfsc = data; + struct tc_hfsc_qopt *opts; + + opts = (struct tc_hfsc_qopt *) tc->tc_opts->d_data; + hfsc->qh_defcls = opts->defcls; + hfsc->qh_mask |= SCH_HFSC_QD_HAS_DEFCLS; + return 0; +} + +static int hfsc_class_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct nlattr *tb[TCA_HFSC_MAX + 1]; + struct rtnl_hfsc_class *hfsc = data; + int err; + + if ((err = tca_parse(tb, TCA_HFSC_MAX, tc, hfsc_policy)) < 0) + return err; + + if (tb[TCA_HFSC_RSC]) { + struct tc_service_curve tsc; + + nla_memcpy(&tsc, tb[TCA_HFSC_RSC], sizeof(tsc)); + hfsc->ch_rsc = tsc; + hfsc->ch_mask |= SCH_HFSC_CLS_HAS_RSC; + } + + if (tb[TCA_HFSC_FSC]) { + struct tc_service_curve tsc; + + nla_memcpy(&tsc, tb[TCA_HFSC_FSC], sizeof(tsc)); + hfsc->ch_fsc = tsc; + hfsc->ch_mask |= SCH_HFSC_CLS_HAS_FSC; + } + + if (tb[TCA_HFSC_USC]) { + struct tc_service_curve tsc; + + nla_memcpy(&tsc, tb[TCA_HFSC_USC], sizeof(tsc)); + hfsc->ch_usc = tsc; + hfsc->ch_mask |= SCH_HFSC_CLS_HAS_USC; + } + + return 0; +} + +static void hfsc_qdisc_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_hfsc_qdisc *hfsc = data; + + if (!hfsc) + return; + + if (hfsc->qh_mask & SCH_HFSC_QD_HAS_DEFCLS) { + char buf[64]; + nl_dump(p, " default-class %s", + rtnl_tc_handle2str(hfsc->qh_defcls, buf, sizeof(buf))); + } +} + +static void hfsc_dump_tsc(struct nl_dump_params *p, struct tc_service_curve *tsc) +{ + nl_dump(p, " m1 %u d %u m2 %u\n", tsc->m1, tsc->d, tsc->m2); +} + +static void hfsc_class_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_hfsc_class *hfsc = data; + + if (!hfsc) + return; + if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_RSC) + hfsc_dump_tsc(p, &hfsc->ch_rsc); + if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_FSC) + hfsc_dump_tsc(p, &hfsc->ch_fsc); + if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_USC) + hfsc_dump_tsc(p, &hfsc->ch_usc); +} + +static void hfsc_class_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + return; +} + +static int hfsc_qdisc_msg_fill(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) +{ + struct rtnl_hfsc_qdisc *hfsc = data; + struct tc_hfsc_qopt opts = {0}; + + if (!hfsc) + BUG(); + + opts.defcls = hfsc->qh_defcls; + return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD); +} + +static int hfsc_class_msg_fill(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) +{ + struct rtnl_hfsc_class *hfsc = data; + struct tc_service_curve tsc; + + if (!hfsc) + BUG(); + + if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_RSC) { + tsc = hfsc->ch_rsc; + NLA_PUT(msg, TCA_HFSC_RSC, sizeof(tsc), &tsc); + } + + if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_FSC) { + tsc = hfsc->ch_fsc; + NLA_PUT(msg, TCA_HFSC_FSC, sizeof(tsc), &tsc); + } + + if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_USC) { + tsc = hfsc->ch_usc; + NLA_PUT(msg, TCA_HFSC_USC, sizeof(tsc), &tsc); + } + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static struct rtnl_tc_ops hfsc_qdisc_ops; +static struct rtnl_tc_ops hfsc_class_ops; + +static struct rtnl_hfsc_qdisc *hfsc_qdisc_data(const struct rtnl_qdisc *qdisc, int *err) +{ + return rtnl_tc_data_check(TC_CAST(qdisc), &hfsc_qdisc_ops, err); +} + +static struct rtnl_hfsc_class *hfsc_class_data(const struct rtnl_class *class, int *err) +{ + return rtnl_tc_data_check(TC_CAST(class), &hfsc_class_ops, err); +} + +/** + * @name Attribute Modifications + * @{ + */ + +/** + * Return default class of HFSC qdisc + * @arg qdisc hfsc qdisc object + * + * Returns the classid of the class where all unclassified traffic + * goes to. + * + * @return classid or TC_H_UNSPEC if unspecified. + */ +uint32_t rtnl_qdisc_hfsc_get_defcls(const struct rtnl_qdisc *qdisc) +{ + struct rtnl_hfsc_qdisc *hfsc; + + if ((hfsc = hfsc_qdisc_data(qdisc, NULL)) && + (hfsc->qh_mask & SCH_HFSC_QD_HAS_DEFCLS)) + return hfsc->qh_defcls; + + return TC_H_UNSPEC; +} + +/** + * Set default class of the hfsc qdisc to the specified value + * @arg qdisc qdisc to change + * @arg defcls new default class + */ +int rtnl_qdisc_hfsc_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls) +{ + struct rtnl_hfsc_qdisc *hfsc; + int err; + + if (!(hfsc = hfsc_qdisc_data(qdisc, &err))) + return err; + + hfsc->qh_defcls = defcls; + hfsc->qh_mask |= SCH_HFSC_QD_HAS_DEFCLS; + + return 0; +} + +int rtnl_class_hfsc_get_rsc(const struct rtnl_class *class, struct tc_service_curve *tsc) +{ + struct rtnl_hfsc_class *hfsc; + int err = -NLE_OPNOTSUPP; + + if ((hfsc = hfsc_class_data(class, &err)) && + (hfsc->ch_mask & SCH_HFSC_CLS_HAS_RSC)) { + *tsc = hfsc->ch_rsc; + return 0; + } + + return err; +} + +int rtnl_class_hfsc_set_rsc(struct rtnl_class *class, const struct tc_service_curve *tsc) +{ + struct rtnl_hfsc_class *hfsc; + int err; + + if (!(hfsc = hfsc_class_data(class, &err))) + return err; + + hfsc->ch_rsc = *tsc; + hfsc->ch_mask |= SCH_HFSC_CLS_HAS_RSC; + + return 0; +} + +int rtnl_class_hfsc_get_fsc(const struct rtnl_class *class, struct tc_service_curve *tsc) +{ + struct rtnl_hfsc_class *hfsc; + int err = -NLE_OPNOTSUPP; + + if ((hfsc = hfsc_class_data(class, &err)) && + (hfsc->ch_mask & SCH_HFSC_CLS_HAS_FSC)) { + *tsc = hfsc->ch_fsc; + return 0; + } + + return err; +} + +int rtnl_class_hfsc_set_fsc(struct rtnl_class *class, const struct tc_service_curve *tsc) +{ + struct rtnl_hfsc_class *hfsc; + int err; + + if (!(hfsc = hfsc_class_data(class, &err))) + return err; + + hfsc->ch_fsc = *tsc; + hfsc->ch_mask |= SCH_HFSC_CLS_HAS_FSC; + + return 0; +} + +int rtnl_class_hfsc_get_usc(const struct rtnl_class *class, struct tc_service_curve *tsc) +{ + struct rtnl_hfsc_class *hfsc; + int err = -NLE_OPNOTSUPP; + + if ((hfsc = hfsc_class_data(class, &err)) && + (hfsc->ch_mask & SCH_HFSC_CLS_HAS_USC)) { + *tsc = hfsc->ch_usc; + return 0; + } + + return err; +} + +int rtnl_class_hfsc_set_usc(struct rtnl_class *class, const struct tc_service_curve *tsc) +{ + struct rtnl_hfsc_class *hfsc; + int err; + + if (!(hfsc = hfsc_class_data(class, &err))) + return err; + + hfsc->ch_usc = *tsc; + hfsc->ch_mask |= SCH_HFSC_CLS_HAS_USC; + + return 0; +} + +/** @} */ + +static struct rtnl_tc_ops hfsc_qdisc_ops = { + .to_kind = "hfsc", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_hfsc_qdisc), + .to_msg_parser = hfsc_qdisc_msg_parser, + .to_dump[NL_DUMP_LINE] = hfsc_qdisc_dump_line, + .to_msg_fill = hfsc_qdisc_msg_fill, +}; + +static struct rtnl_tc_ops hfsc_class_ops = { + .to_kind = "hfsc", + .to_type = RTNL_TC_TYPE_CLASS, + .to_size = sizeof(struct rtnl_hfsc_class), + .to_msg_parser = hfsc_class_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = hfsc_class_dump_line, + [NL_DUMP_DETAILS] = hfsc_class_dump_details, + }, + .to_msg_fill = hfsc_class_msg_fill, +}; + +static void _nl_init hfsc_init(void) +{ + rtnl_tc_register(&hfsc_qdisc_ops); + rtnl_tc_register(&hfsc_class_ops); +} + +static void _nl_exit hfsc_exit(void) +{ + rtnl_tc_unregister(&hfsc_qdisc_ops); + rtnl_tc_unregister(&hfsc_class_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/htb.c b/libnl/lib/route/qdisc/htb.c new file mode 100644 index 0000000..28061b9 --- /dev/null +++ b/libnl/lib/route/qdisc/htb.c @@ -0,0 +1,761 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + * Copyright (c) 2005-2006 Petr Gotthard + * Copyright (c) 2005-2006 Siemens AG Oesterreich + */ + +/** + * @ingroup qdisc + * @ingroup class + * @defgroup qdisc_htb Hierachical Token Bucket (HTB) + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "tc-api.h" + +/** @cond SKIP */ +struct rtnl_htb_qdisc { + uint32_t qh_rate2quantum; + uint32_t qh_defcls; + uint32_t qh_mask; + uint32_t qh_direct_pkts; +}; + +struct rtnl_htb_class { + uint32_t ch_prio; + struct rtnl_ratespec ch_rate; + struct rtnl_ratespec ch_ceil; + uint32_t ch_rbuffer; + uint32_t ch_cbuffer; + uint32_t ch_quantum; + uint32_t ch_mask; + uint32_t ch_level; +}; + +#define SCH_HTB_HAS_RATE2QUANTUM 0x01 +#define SCH_HTB_HAS_DEFCLS 0x02 + +#define SCH_HTB_HAS_PRIO 0x001 +#define SCH_HTB_HAS_RATE 0x002 +#define SCH_HTB_HAS_CEIL 0x004 +#define SCH_HTB_HAS_RBUFFER 0x008 +#define SCH_HTB_HAS_CBUFFER 0x010 +#define SCH_HTB_HAS_QUANTUM 0x020 +#define SCH_HTB_HAS_LEVEL 0x040 +/** @endcond */ + +static struct nla_policy htb_policy[TCA_HTB_MAX+1] = { + [TCA_HTB_INIT] = { .minlen = sizeof(struct tc_htb_glob) }, + [TCA_HTB_PARMS] = { .minlen = sizeof(struct tc_htb_opt) }, + [TCA_HTB_RATE64] = { .minlen = sizeof(uint64_t) }, + [TCA_HTB_CEIL64] = { .minlen = sizeof(uint64_t) }, +}; + +static int htb_qdisc_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct nlattr *tb[TCA_HTB_MAX + 1]; + struct rtnl_htb_qdisc *htb = data; + int err; + + if ((err = tca_parse(tb, TCA_HTB_MAX, tc, htb_policy)) < 0) + return err; + + if (tb[TCA_HTB_INIT]) { + struct tc_htb_glob opts; + + nla_memcpy(&opts, tb[TCA_HTB_INIT], sizeof(opts)); + htb->qh_rate2quantum = opts.rate2quantum; + htb->qh_defcls = opts.defcls; + htb->qh_direct_pkts = opts.direct_pkts; + + htb->qh_mask = (SCH_HTB_HAS_RATE2QUANTUM | SCH_HTB_HAS_DEFCLS); + } + + return 0; +} + +static int htb_class_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct nlattr *tb[TCA_HTB_MAX + 1]; + struct rtnl_htb_class *htb = data; + int err; + + if ((err = tca_parse(tb, TCA_HTB_MAX, tc, htb_policy)) < 0) + return err; + + if (tb[TCA_HTB_PARMS]) { + struct tc_htb_opt opts; + + nla_memcpy(&opts, tb[TCA_HTB_PARMS], sizeof(opts)); + htb->ch_prio = opts.prio; + rtnl_copy_ratespec(&htb->ch_rate, &opts.rate); + rtnl_copy_ratespec(&htb->ch_ceil, &opts.ceil); + + if (tb[TCA_HTB_RATE64]) + nla_memcpy(&htb->ch_rate.rs_rate64, tb[TCA_HTB_RATE64], sizeof(uint64_t)); + if (tb[TCA_HTB_CEIL64]) + nla_memcpy(&htb->ch_ceil.rs_rate64, tb[TCA_HTB_CEIL64], sizeof(uint64_t)); + + htb->ch_rbuffer = rtnl_tc_calc_bufsize64(nl_ticks2us(opts.buffer), + htb->ch_rate.rs_rate64); + htb->ch_cbuffer = rtnl_tc_calc_bufsize64(nl_ticks2us(opts.cbuffer), + htb->ch_ceil.rs_rate64); + htb->ch_quantum = opts.quantum; + htb->ch_level = opts.level; + + rtnl_tc_set_mpu(tc, htb->ch_rate.rs_mpu); + rtnl_tc_set_overhead(tc, htb->ch_rate.rs_overhead); + + htb->ch_mask = (SCH_HTB_HAS_PRIO | SCH_HTB_HAS_RATE | + SCH_HTB_HAS_CEIL | SCH_HTB_HAS_RBUFFER | + SCH_HTB_HAS_CBUFFER | SCH_HTB_HAS_QUANTUM | + SCH_HTB_HAS_LEVEL); + } + + return 0; +} + +static void htb_qdisc_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_htb_qdisc *htb = data; + + if (!htb) + return; + + if (htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM) + nl_dump(p, " r2q %u", htb->qh_rate2quantum); + + if (htb->qh_mask & SCH_HTB_HAS_DEFCLS) { + char buf[64]; + nl_dump(p, " default-class %s", + rtnl_tc_handle2str(htb->qh_defcls, buf, sizeof(buf))); + } +} + +static void htb_class_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_htb_class *htb = data; + + if (!htb) + return; + + if (htb->ch_mask & SCH_HTB_HAS_RATE) { + double r, rbit; + char *ru, *rubit; + + r = nl_cancel_down_bytes(htb->ch_rate.rs_rate64, &ru); + rbit = nl_cancel_down_bits(htb->ch_rate.rs_rate64*8, &rubit); + + nl_dump(p, " rate %.2f%s/s (%.0f%s) log %u", + r, ru, rbit, rubit, 1<ch_rate.rs_cell_log); + } +} + +static void htb_class_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_htb_class *htb = data; + + if (!htb) + return; + + /* line 1 */ + if (htb->ch_mask & SCH_HTB_HAS_CEIL) { + double r, rbit; + char *ru, *rubit; + + r = nl_cancel_down_bytes(htb->ch_ceil.rs_rate64, &ru); + rbit = nl_cancel_down_bits(htb->ch_ceil.rs_rate64*8, &rubit); + + nl_dump(p, " ceil %.2f%s/s (%.0f%s) log %u", + r, ru, rbit, rubit, 1<ch_ceil.rs_cell_log); + } + + if (htb->ch_mask & SCH_HTB_HAS_PRIO) + nl_dump(p, " prio %u", htb->ch_prio); + + if (htb->ch_mask & SCH_HTB_HAS_RBUFFER) { + double b; + char *bu; + + b = nl_cancel_down_bytes(htb->ch_rbuffer, &bu); + nl_dump(p, " rbuffer %.2f%s", b, bu); + } + + if (htb->ch_mask & SCH_HTB_HAS_CBUFFER) { + double b; + char *bu; + + b = nl_cancel_down_bytes(htb->ch_cbuffer, &bu); + nl_dump(p, " cbuffer %.2f%s", b, bu); + } + + if (htb->ch_mask & SCH_HTB_HAS_QUANTUM) + nl_dump(p, " quantum %u", htb->ch_quantum); +} + +static int htb_qdisc_msg_fill(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) +{ + struct rtnl_htb_qdisc *htb = data; + struct tc_htb_glob opts = { + .version = TC_HTB_PROTOVER, + .rate2quantum = 10, + }; + + if (htb) { + if (htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM) + opts.rate2quantum = htb->qh_rate2quantum; + + if (htb->qh_mask & SCH_HTB_HAS_DEFCLS) + opts.defcls = htb->qh_defcls; + } + + return nla_put(msg, TCA_HTB_INIT, sizeof(opts), &opts); +} + +static int htb_class_msg_fill(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) +{ + struct rtnl_htb_class *htb = data; + uint32_t mtu, rtable[RTNL_TC_RTABLE_SIZE], ctable[RTNL_TC_RTABLE_SIZE]; + struct tc_htb_opt opts; + int buffer, cbuffer; + uint64_t rate64; + uint64_t ceil64; + + if (!htb || !(htb->ch_mask & SCH_HTB_HAS_RATE)) + BUG(); + + memset(&opts, 0, sizeof(opts)); + + /* if not set, zero (0) is used as priority */ + if (htb->ch_mask & SCH_HTB_HAS_PRIO) + opts.prio = htb->ch_prio; + + mtu = rtnl_tc_get_mtu(tc); + + rtnl_tc_build_rate_table(tc, &htb->ch_rate, rtable); + rtnl_rcopy_ratespec(&opts.rate, &htb->ch_rate); + rate64 = htb->ch_rate.rs_rate64; + + if (htb->ch_mask & SCH_HTB_HAS_CEIL) { + rtnl_tc_build_rate_table(tc, &htb->ch_ceil, ctable); + rtnl_rcopy_ratespec(&opts.ceil, &htb->ch_ceil); + ceil64 = htb->ch_ceil.rs_rate64; + } else { + /* + * If not set, configured rate is used as ceil, which implies + * no borrowing. + */ + memcpy(&opts.ceil, &opts.rate, sizeof(struct tc_ratespec)); + ceil64 = rate64; + } + + if (htb->ch_mask & SCH_HTB_HAS_RBUFFER) + buffer = htb->ch_rbuffer; + else + buffer = rate64 / nl_get_psched_hz() + mtu; /* XXX */ + + opts.buffer = nl_us2ticks(rtnl_tc_calc_txtime64(buffer, rate64)); + + if (htb->ch_mask & SCH_HTB_HAS_CBUFFER) + cbuffer = htb->ch_cbuffer; + else + cbuffer = ceil64 / nl_get_psched_hz() + mtu; /* XXX */ + + opts.cbuffer = nl_us2ticks(rtnl_tc_calc_txtime64(cbuffer, ceil64)); + + if (htb->ch_mask & SCH_HTB_HAS_QUANTUM) + opts.quantum = htb->ch_quantum; + + NLA_PUT(msg, TCA_HTB_PARMS, sizeof(opts), &opts); + if (rate64 > 0xFFFFFFFFull) + NLA_PUT(msg, TCA_HTB_RATE64, sizeof(uint64_t), &rate64); + if (ceil64 > 0xFFFFFFFFull) + NLA_PUT(msg, TCA_HTB_CEIL64, sizeof(uint64_t), &ceil64); + NLA_PUT(msg, TCA_HTB_RTAB, sizeof(rtable), &rtable); + NLA_PUT(msg, TCA_HTB_CTAB, sizeof(ctable), &ctable); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static struct rtnl_tc_ops htb_qdisc_ops; +static struct rtnl_tc_ops htb_class_ops; + +static struct rtnl_htb_qdisc *htb_qdisc_data(struct rtnl_qdisc *qdisc, int *err) +{ + return rtnl_tc_data_check(TC_CAST(qdisc), &htb_qdisc_ops, err); +} + +static struct rtnl_htb_class *htb_class_data(struct rtnl_class *class, int *err) +{ + return rtnl_tc_data_check(TC_CAST(class), &htb_class_ops, err); +} + +/** + * @name Attribute Modifications + * @{ + */ + +/** + * Return rate/quantum ratio of HTB qdisc + * @arg qdisc htb qdisc object + * + * @return rate/quantum ratio or 0 if unspecified + */ +uint32_t rtnl_htb_get_rate2quantum(struct rtnl_qdisc *qdisc) +{ + struct rtnl_htb_qdisc *htb; + + if ((htb = htb_qdisc_data(qdisc, NULL)) && + (htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)) + return htb->qh_rate2quantum; + + return 0; +} + +int rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum) +{ + struct rtnl_htb_qdisc *htb; + int err; + + if (!(htb = htb_qdisc_data(qdisc, &err))) + return err; + + htb->qh_rate2quantum = rate2quantum; + htb->qh_mask |= SCH_HTB_HAS_RATE2QUANTUM; + + return 0; +} + +/** + * Return default class of HTB qdisc + * @arg qdisc htb qdisc object + * + * Returns the classid of the class where all unclassified traffic + * goes to. + * + * @return classid or TC_H_UNSPEC if unspecified. + */ +uint32_t rtnl_htb_get_defcls(struct rtnl_qdisc *qdisc) +{ + struct rtnl_htb_qdisc *htb; + + if ((htb = htb_qdisc_data(qdisc, NULL)) && + htb->qh_mask & SCH_HTB_HAS_DEFCLS) + return htb->qh_defcls; + + return TC_H_UNSPEC; +} + +/** + * Set default class of the htb qdisc to the specified value + * @arg qdisc qdisc to change + * @arg defcls new default class + */ +int rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls) +{ + struct rtnl_htb_qdisc *htb; + int err; + + if (!(htb = htb_qdisc_data(qdisc, &err))) + return err; + + htb->qh_defcls = defcls; + htb->qh_mask |= SCH_HTB_HAS_DEFCLS; + + return 0; +} + +uint32_t rtnl_htb_get_prio(struct rtnl_class *class) +{ + struct rtnl_htb_class *htb; + + if ((htb = htb_class_data(class, NULL)) && + (htb->ch_mask & SCH_HTB_HAS_PRIO)) + return htb->ch_prio; + + return 0; +} + +int rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio) +{ + struct rtnl_htb_class *htb; + int err; + + if (!(htb = htb_class_data(class, &err))) + return err; + + htb->ch_prio = prio; + htb->ch_mask |= SCH_HTB_HAS_PRIO; + + return 0; +} + +/** + * Return rate of HTB class + * @arg class htb class object + * + * @return Rate in bytes/s or 0 if unspecified. If the value + * cannot be represented as 32 bit integer, (1<<32) is returned. + * Use rtnl_htb_get_rate64() instead. + */ +uint32_t rtnl_htb_get_rate(struct rtnl_class *class) +{ + struct rtnl_htb_class *htb; + + if ( !(htb = htb_class_data(class, NULL)) + || !(htb->ch_mask & SCH_HTB_HAS_RATE)) + return 0; + + if (htb->ch_rate.rs_rate64 > 0xFFFFFFFFull) + return 0xFFFFFFFFull; + + return htb->ch_rate.rs_rate64; +} + +/** + * Return rate of HTB class + * @arg class htb class object + * @arg out_rate64 on success, the set rate. + * + * @return 0 on success or a negative error code. + */ +int rtnl_htb_get_rate64(struct rtnl_class *class, uint64_t *out_rate64) +{ + struct rtnl_htb_class *htb; + + if (!(htb = htb_class_data(class, NULL))) + return -NLE_INVAL; + if (!(htb->ch_mask & SCH_HTB_HAS_RATE)) + return -NLE_NOATTR; + + *out_rate64 = htb->ch_rate.rs_rate64; + return 0; +} + +/** + * Set rate of HTB class + * @arg class htb class object + * @arg rate new rate in bytes per second + * + * @return 0 on success or a negative error code. + */ +int rtnl_htb_set_rate(struct rtnl_class *class, uint32_t rate) +{ + return rtnl_htb_set_rate64(class, rate); +} + +/** + * Set rate of HTB class + * @arg class htb class object + * @arg rate new rate in bytes per second + * + * @return 0 on success or a negative error code. + */ +int rtnl_htb_set_rate64(struct rtnl_class *class, uint64_t rate) +{ + struct rtnl_htb_class *htb; + int err; + + if (!(htb = htb_class_data(class, &err))) + return err; + + htb->ch_rate.rs_cell_log = UINT8_MAX; /* use default value */ + htb->ch_rate.rs_rate64 = rate; + htb->ch_mask |= SCH_HTB_HAS_RATE; + + return 0; +} + +/** + * Return ceil rate of HTB class + * @arg class htb class object + * + * @return Ceil rate in bytes/s or 0 if unspecified. If the value + * cannot be represented as 32 bit integer, (1<<32) is returned. + * Use rtnl_htb_get_ceil64() instead. + */ +uint32_t rtnl_htb_get_ceil(struct rtnl_class *class) +{ + struct rtnl_htb_class *htb; + + if ( !(htb = htb_class_data(class, NULL)) + || !(htb->ch_mask & SCH_HTB_HAS_CEIL)) + return 0; + + if (htb->ch_ceil.rs_rate64 > 0xFFFFFFFFull) + return 0xFFFFFFFFull; + + return htb->ch_ceil.rs_rate64; +} + +/** + * Return ceil rate of HTB class + * @arg class htb class object + * @arg out_ceil64 on success, the set ceil value. + * + * @return 0 on success or a negative error code. + */ +int rtnl_htb_get_ceil64(struct rtnl_class *class, uint64_t *out_ceil64) +{ + struct rtnl_htb_class *htb; + + if (!(htb = htb_class_data(class, NULL))) + return -NLE_INVAL; + if (!(htb->ch_mask & SCH_HTB_HAS_CEIL)) + return -NLE_NOATTR; + + *out_ceil64 = htb->ch_ceil.rs_rate64; + return 0; +} + +/** + * Set ceil rate of HTB class + * @arg class htb class object + * @arg ceil new ceil rate number of bytes per second + * + * @return 0 on success or a negative error code. + */ +int rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil) +{ + return rtnl_htb_set_ceil64(class, ceil); +} + +/** + * Set ceil rate of HTB class + * @arg class htb class object + * @arg ceil64 new ceil rate number of bytes per second + * + * @return 0 on success or a negative error code. + */ +int rtnl_htb_set_ceil64(struct rtnl_class *class, uint64_t ceil64) +{ + struct rtnl_htb_class *htb; + int err; + + if (!(htb = htb_class_data(class, &err))) + return err; + + htb->ch_ceil.rs_cell_log = UINT8_MAX; /* use default value */ + htb->ch_ceil.rs_rate64 = ceil64; + htb->ch_mask |= SCH_HTB_HAS_CEIL; + + return 0; +} + +/** + * Return burst buffer size of HTB class + * @arg class htb class object + * + * @return Burst buffer size or 0 if unspecified + */ +uint32_t rtnl_htb_get_rbuffer(struct rtnl_class *class) +{ + struct rtnl_htb_class *htb; + + if ((htb = htb_class_data(class, NULL)) && + htb->ch_mask & SCH_HTB_HAS_RBUFFER) + return htb->ch_rbuffer; + + return 0; +} + +/** + * Set size of the rate bucket of HTB class. + * @arg class HTB class to be modified. + * @arg rbuffer New size in bytes. + */ +int rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t rbuffer) +{ + struct rtnl_htb_class *htb; + int err; + + if (!(htb = htb_class_data(class, &err))) + return err; + + htb->ch_rbuffer = rbuffer; + htb->ch_mask |= SCH_HTB_HAS_RBUFFER; + + return 0; +} + +/** + * Return ceil burst buffer size of HTB class + * @arg class htb class object + * + * @return Ceil burst buffer size or 0 if unspecified + */ +uint32_t rtnl_htb_get_cbuffer(struct rtnl_class *class) +{ + struct rtnl_htb_class *htb; + + if ((htb = htb_class_data(class, NULL)) && + htb->ch_mask & SCH_HTB_HAS_CBUFFER) + return htb->ch_cbuffer; + + return 0; +} + +/** + * Set size of the ceil bucket of HTB class. + * @arg class HTB class to be modified. + * @arg cbuffer New size in bytes. + */ +int rtnl_htb_set_cbuffer(struct rtnl_class *class, uint32_t cbuffer) +{ + struct rtnl_htb_class *htb; + int err; + + if (!(htb = htb_class_data(class, &err))) + return err; + + htb->ch_cbuffer = cbuffer; + htb->ch_mask |= SCH_HTB_HAS_CBUFFER; + + return 0; +} + +/** + * Return quantum of HTB class + * @arg class htb class object + * + * See XXX[quantum def] + * + * @return Quantum or 0 if unspecified. + */ +uint32_t rtnl_htb_get_quantum(struct rtnl_class *class) +{ + struct rtnl_htb_class *htb; + + if ((htb = htb_class_data(class, NULL)) && + htb->ch_mask & SCH_HTB_HAS_QUANTUM) + return htb->ch_quantum; + + return 0; +} + +/** + * Set quantum of HTB class (overwrites value calculated based on r2q) + * @arg class htb class object + * @arg quantum new quantum in number of bytes + * + * See XXX[quantum def] + * + * @return 0 on success or a negative error code. + */ +int rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum) +{ + struct rtnl_htb_class *htb; + int err; + + if (!(htb = htb_class_data(class, &err))) + return err; + + htb->ch_quantum = quantum; + htb->ch_mask |= SCH_HTB_HAS_QUANTUM; + + return 0; +} + +/** + * Return level of HTB class + * @arg class htb class object + * + * Returns the level of the HTB class. Leaf classes are assigned level + * 0, root classes have level (TC_HTB_MAXDEPTH - 1). Interior classes + * have a level of one less than their parent. + * + * @return Level or a negative error code. + */ +int rtnl_htb_get_level(struct rtnl_class *class) +{ + struct rtnl_htb_class *htb; + int err = -NLE_OPNOTSUPP; + + if ((htb = htb_class_data(class, &err)) && + (htb->ch_mask & SCH_HTB_HAS_LEVEL)) + return htb->ch_level; + + return err; +} + +/** + * Set level of HTB class + * @arg class htb class object + * @arg level new level of HTB class + * + * Sets the level of a HTB class. Note that changing the level of a HTB + * class does not change the level of its in kernel counterpart. This + * function is provided only to create HTB objects which can be compared + * against or filtered upon. + * + * @return 0 on success or a negative error code. + */ +int rtnl_htb_set_level(struct rtnl_class *class, int level) +{ + struct rtnl_htb_class *htb; + int err; + + if (!(htb = htb_class_data(class, &err))) + return err; + + htb->ch_level = level; + htb->ch_mask |= SCH_HTB_HAS_LEVEL; + + return 0; +} + +/** @} */ + +static struct rtnl_tc_ops htb_qdisc_ops = { + .to_kind = "htb", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_htb_qdisc), + .to_msg_parser = htb_qdisc_msg_parser, + .to_dump[NL_DUMP_LINE] = htb_qdisc_dump_line, + .to_msg_fill = htb_qdisc_msg_fill, +}; + +static struct rtnl_tc_ops htb_class_ops = { + .to_kind = "htb", + .to_type = RTNL_TC_TYPE_CLASS, + .to_size = sizeof(struct rtnl_htb_class), + .to_msg_parser = htb_class_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = htb_class_dump_line, + [NL_DUMP_DETAILS] = htb_class_dump_details, + }, + .to_msg_fill = htb_class_msg_fill, +}; + +static void _nl_init htb_init(void) +{ + rtnl_tc_register(&htb_qdisc_ops); + rtnl_tc_register(&htb_class_ops); +} + +static void _nl_exit htb_exit(void) +{ + rtnl_tc_unregister(&htb_qdisc_ops); + rtnl_tc_unregister(&htb_class_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/ingress.c b/libnl/lib/route/qdisc/ingress.c new file mode 100644 index 0000000..5363c26 --- /dev/null +++ b/libnl/lib/route/qdisc/ingress.c @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_ingress Ingress qdisc + * + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include + +#include "tc-api.h" + +struct dumb { + uint32_t foo; +}; + +static int dumb_msg_parser(struct rtnl_tc *tc, void *data) +{ + return 0; +} + +static void dumb_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ +} + +static int dumb_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + return 0; +} + +static struct rtnl_tc_ops ingress_ops = { + .to_kind = "ingress", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct dumb), + .to_msg_parser = dumb_msg_parser, + .to_dump[NL_DUMP_LINE] = dumb_dump_line, + .to_msg_fill = dumb_msg_fill, +}; + +static void _nl_init ingress_init(void) +{ + rtnl_tc_register(&ingress_ops); +} + +static void _nl_exit ingress_exit(void) +{ + rtnl_tc_unregister(&ingress_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/mqprio.c b/libnl/lib/route/qdisc/mqprio.c new file mode 100644 index 0000000..5e9d08f --- /dev/null +++ b/libnl/lib/route/qdisc/mqprio.c @@ -0,0 +1,618 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2018 Volodymyr Bendiuga + */ + +#include "nl-default.h" + +#include +#include +#include +#include + +#include "tc-api.h" + +/** @cond SKIP */ +struct rtnl_mqprio { + uint8_t qm_num_tc; + uint8_t qm_prio_map[TC_QOPT_BITMASK + 1]; + uint8_t qm_hw; + uint16_t qm_count[TC_QOPT_MAX_QUEUE]; + uint16_t qm_offset[TC_QOPT_MAX_QUEUE]; + uint16_t qm_mode; + uint16_t qm_shaper; + uint64_t qm_min_rate[TC_QOPT_MAX_QUEUE]; + uint64_t qm_max_rate[TC_QOPT_MAX_QUEUE]; + uint32_t qm_mask; +}; + +#define SCH_MQPRIO_ATTR_NUMTC (1 << 0) +#define SCH_MQPRIO_ATTR_PRIOMAP (1 << 1) +#define SCH_MQPRIO_ATTR_HW (1 << 2) +#define SCH_MQPRIO_ATTR_QUEUE (1 << 3) +#define SCH_MQPRIO_ATTR_MODE (1 << 4) +#define SCH_MQPRIO_ATTR_SHAPER (1 << 5) +#define SCH_MQPRIO_ATTR_MIN_RATE (1 << 6) +#define SCH_MQPRIO_ATTR_MAX_RATE (1 << 7) +/** @endcond */ + +static struct nla_policy mqprio_policy[TCA_MQPRIO_MAX + 1] = { + [TCA_MQPRIO_MODE] = { .minlen = sizeof(uint16_t) }, + [TCA_MQPRIO_SHAPER] = { .minlen = sizeof(uint16_t) }, + [TCA_MQPRIO_MIN_RATE64] = { .type = NLA_NESTED }, + [TCA_MQPRIO_MAX_RATE64] = { .type = NLA_NESTED }, +}; + +static int mqprio_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_mqprio *mqprio = data; + struct tc_mqprio_qopt *qopt; + struct nlattr *attr; + int len, rem, i, err; + + if (tc->tc_opts->d_size < sizeof(*qopt)) + return -NLE_INVAL; + + qopt = (struct tc_mqprio_qopt *) tc->tc_opts->d_data; + mqprio->qm_num_tc = qopt->num_tc; + mqprio->qm_hw = qopt->hw; + memcpy(mqprio->qm_prio_map, qopt->prio_tc_map, + TC_QOPT_MAX_QUEUE * sizeof(uint8_t)); + memcpy(mqprio->qm_count, qopt->count, + TC_QOPT_MAX_QUEUE * sizeof(uint16_t)); + memcpy(mqprio->qm_offset, qopt->offset, + TC_QOPT_MAX_QUEUE * sizeof(uint16_t)); + mqprio->qm_mask = (SCH_MQPRIO_ATTR_NUMTC | SCH_MQPRIO_ATTR_PRIOMAP | + SCH_MQPRIO_ATTR_QUEUE | SCH_MQPRIO_ATTR_HW); + + len = tc->tc_opts->d_size - NLA_ALIGN(sizeof(*qopt)); + + if (len > 0) { + struct nlattr *tb[TCA_MQPRIO_MAX + 1]; + + err = nla_parse(tb, TCA_MQPRIO_MAX, (struct nlattr *) + ((char *) tc->tc_opts->d_data + NLA_ALIGN(sizeof(*qopt))), + len, mqprio_policy); + if (err < 0) + return err; + + if (tb[TCA_MQPRIO_MODE]) { + mqprio->qm_mode = nla_get_u16(tb[TCA_MQPRIO_MODE]); + mqprio->qm_mask |= SCH_MQPRIO_ATTR_MODE; + } + + if (tb[TCA_MQPRIO_SHAPER]) { + mqprio->qm_shaper = nla_get_u16(tb[TCA_MQPRIO_SHAPER]); + mqprio->qm_mask |= SCH_MQPRIO_ATTR_SHAPER; + } + + if (tb[TCA_MQPRIO_MIN_RATE64]) { + i = 0; + nla_for_each_nested(attr, tb[TCA_MQPRIO_MIN_RATE64], rem) { + if (nla_type(attr) != TCA_MQPRIO_MIN_RATE64) + return -EINVAL; + + if (i >= mqprio->qm_num_tc) + break; + + mqprio->qm_min_rate[i] = nla_get_u64(attr); + } + + mqprio->qm_mask |= SCH_MQPRIO_ATTR_MIN_RATE; + } + + if (tb[TCA_MQPRIO_MAX_RATE64]) { + i = 0; + nla_for_each_nested(attr, tb[TCA_MQPRIO_MAX_RATE64], rem) { + if (nla_type(attr) != TCA_MQPRIO_MAX_RATE64) + return -EINVAL; + + if (i >= mqprio->qm_num_tc) + break; + + mqprio->qm_max_rate[i] = nla_get_u64(attr); + } + + mqprio->qm_mask |= SCH_MQPRIO_ATTR_MAX_RATE; + } + } + + return 0; +} + +static int mqprio_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_mqprio *mqprio = data; + struct tc_mqprio_qopt qopt = { 0 }; + struct nlattr *nest = NULL; + int i; + + if (!mqprio || + !(mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC) || + !(mqprio->qm_mask & SCH_MQPRIO_ATTR_PRIOMAP) || + !(mqprio->qm_mask & SCH_MQPRIO_ATTR_QUEUE)) + return -NLE_INVAL; + + if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_HW)) + qopt.hw = 0; + else + qopt.hw = mqprio->qm_hw; + + qopt.num_tc = mqprio->qm_num_tc; + memcpy(qopt.count, mqprio->qm_count, TC_QOPT_MAX_QUEUE * sizeof(uint16_t)); + memcpy(qopt.offset, mqprio->qm_offset, TC_QOPT_MAX_QUEUE * sizeof(uint16_t)); + memcpy(qopt.prio_tc_map, mqprio->qm_prio_map, TC_QOPT_MAX_QUEUE * sizeof(uint8_t)); + + nlmsg_append(msg, &qopt, sizeof(qopt), NL_DONTPAD); + + if (mqprio->qm_hw) { + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MODE) + NLA_PUT_U16(msg, TCA_MQPRIO_MODE, mqprio->qm_mode); + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER) + NLA_PUT_U16(msg, TCA_MQPRIO_SHAPER, mqprio->qm_shaper); + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MIN_RATE) { + nest = nla_nest_start(msg, TCA_MQPRIO_MIN_RATE64); + if (!nest) + goto nla_put_failure; + + for (i = 0; i < mqprio->qm_num_tc; i++) { + if (nla_put(msg, TCA_MQPRIO_MIN_RATE64, + sizeof(mqprio->qm_min_rate[i]), + &mqprio->qm_min_rate[i]) < 0) + goto nla_nest_cancel; + } + nla_nest_end(msg, nest); + } + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MAX_RATE) { + nest = nla_nest_start(msg, TCA_MQPRIO_MAX_RATE64); + if (!nest) + goto nla_put_failure; + + for (i = 0; i < mqprio->qm_num_tc; i++) { + if (nla_put(msg, TCA_MQPRIO_MAX_RATE64, + sizeof(mqprio->qm_max_rate[i]), + &mqprio->qm_max_rate[i]) < 0) + goto nla_nest_cancel; + } + nla_nest_end(msg, nest); + } + } + + return 0; + +nla_nest_cancel: + nla_nest_cancel(msg, nest); + return -NLE_MSGSIZE; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static void mqprio_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_mqprio *mqprio = data; + + if (mqprio) + nl_dump(p, " num_tc %u", mqprio->qm_num_tc); +} + +static void mqprio_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_mqprio *mqprio = data; + int i; + + if (!mqprio) + return; + + nl_dump(p, "map ["); + + for (i = 0; i <= TC_QOPT_BITMASK; i++) + nl_dump(p, "%u%s", mqprio->qm_prio_map[i], + i < TC_QOPT_BITMASK ? " " : ""); + + nl_dump(p, "]\n"); + nl_new_line(p); +} + +/** + * @name Attribute Modification + * @{ + */ + +/** + * Set number of traffic classes. + * @arg qdisc MQPRIO qdisc to be modified. + * @arg num_tc Number of traffic classes to create. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_set_num_tc(struct rtnl_qdisc *qdisc, int num_tc) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + mqprio->qm_num_tc = num_tc; + mqprio->qm_mask |= SCH_MQPRIO_ATTR_NUMTC; + return 0; +} + +/** + * Get number of traffic classes of MQPRIO qdisc. + * @arg qdisc MQPRIO qdisc. + * @return Number of traffic classes or a negative error code. + */ +int rtnl_qdisc_mqprio_get_num_tc(struct rtnl_qdisc *qdisc) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc)))) + return -NLE_INVAL; + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC) + return mqprio->qm_num_tc; + else + return -NLE_MISSING_ATTR; +} + +/** + * Set priomap of the MQPRIO qdisc. + * @arg qdisc MQPRIO qdisc to be modified. + * @arg priomap New priority mapping. + * @arg len Length of priomap (# of elements). + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[], + int len) +{ + struct rtnl_mqprio *mqprio; + int i; + + if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC)) + return -NLE_MISSING_ATTR; + + if (len > TC_QOPT_BITMASK + 1) + return -NLE_RANGE; + + for (i = 0; i < len; i++) { + if (priomap[i] > mqprio->qm_num_tc) + return -NLE_RANGE; + } + + memset(mqprio->qm_prio_map, 0, sizeof(mqprio->qm_prio_map)); + memcpy(mqprio->qm_prio_map, priomap, len * sizeof(uint8_t)); + mqprio->qm_mask |= SCH_MQPRIO_ATTR_PRIOMAP; + + return 0; +} + +/** + * Get priomap of MQPRIO qdisc. + * @arg qdisc MQPRIO qdisc. + * @return Priority mapping as array of size TC_QOPT_BANDS+1 + * or NULL if an error occured. + */ +uint8_t *rtnl_qdisc_mqprio_get_priomap(struct rtnl_qdisc *qdisc) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc)))) + return NULL; + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_PRIOMAP) + return mqprio->qm_prio_map; + else + return NULL; +} + +/** + * Offload to HW or run in SW (default). + * @arg qdisc MQPRIO qdisc to be modified. + * @arg offload 1 - offload to HW, 0 - run in SW only (default). + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_hw_offload(struct rtnl_qdisc *qdisc, int offload) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + switch (offload) { + case 0: + case 1: + mqprio->qm_hw = offload; + break; + default: + return -NLE_INVAL; + } + + mqprio->qm_mask |= SCH_MQPRIO_ATTR_HW; + return 0; +} + +/** + * Check whether running in HW or SW. + * @arg qdisc MQPRIO qdisc to be modified. + * @return 0 if running in SW, otherwise 1 (HW) + */ +int rtnl_qdisc_mqprio_get_hw_offload(struct rtnl_qdisc *qdisc) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc)))) + return -NLE_INVAL; + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_HW) + return mqprio->qm_hw; + + return 0; +} + +/** + * Set tc queue of the MQPRIO qdisc. + * @arg qdisc MQPRIO qdisc to be modified. + * @arg count count of queue range for each traffic class + * @arg offset offset of queue range for each traffic class + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_set_queue(struct rtnl_qdisc *qdisc, uint16_t count[], + uint16_t offset[], int len) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC)) + return -NLE_MISSING_ATTR; + + if (len < 0 || len > TC_QOPT_MAX_QUEUE) + return -NLE_RANGE; + + memset(mqprio->qm_count, 0, sizeof(mqprio->qm_count)); + memset(mqprio->qm_offset, 0, sizeof(mqprio->qm_offset)); + memcpy(mqprio->qm_count, count, len * sizeof(uint16_t)); + memcpy(mqprio->qm_offset, offset, len * sizeof(uint16_t)); + mqprio->qm_mask |= SCH_MQPRIO_ATTR_QUEUE; + + return 0; +} + +/** + * Get tc queue of the MQPRIO qdisc. + * @arg qdisc MQPRIO qdisc to be modified. + * @arg count count of queue range for each traffic class + * @arg offset offset of queue range for each traffic class + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_get_queue(struct rtnl_qdisc *qdisc, uint16_t *count, + uint16_t *offset) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc)))) + return -NLE_INVAL; + + if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_QUEUE)) + return -NLE_MISSING_ATTR; + + memcpy(count, mqprio->qm_count, TC_QOPT_MAX_QUEUE * sizeof(uint16_t)); + memcpy(offset, mqprio->qm_offset, TC_QOPT_MAX_QUEUE * sizeof(uint16_t)); + + return 0; +} + +/** + * Set mode of mqprio Qdisc + * @arg qdisc MQPRIO qdisc to be modified. + * @arg mode one of: TC_MQPRIO_MODE_DCB, TC_MQPRIO_MODE_CHANNEL + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_set_mode(struct rtnl_qdisc *qdisc, uint16_t mode) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_HW)) + return -NLE_MISSING_ATTR; + + mqprio->qm_mode = mode; + mqprio->qm_mask |= SCH_MQPRIO_ATTR_MODE; + + return 0; +} + +/** + * Get mode of mqprio Qdisc + * @arg qdisc MQPRIO qdisc. + * @return mode on success or negative error code. + */ +int rtnl_qdisc_mqprio_get_mode(struct rtnl_qdisc *qdisc) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc)))) + return -NLE_INVAL; + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MODE) + return mqprio->qm_mode; + else + return -NLE_MISSING_ATTR; +} + +/** + * Set shaper of mqprio Qdisc + * @arg qdisc MQPRIO qdisc to be modified. + * @arg shaper one of: TC_MQPRIO_SHAPER_DCB, TC_MQPRIO_SHAPER_BW_RATE + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_set_shaper(struct rtnl_qdisc *qdisc, uint16_t shaper) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_HW)) + return -NLE_MISSING_ATTR; + + mqprio->qm_shaper = shaper; + mqprio->qm_mask |= SCH_MQPRIO_ATTR_SHAPER; + + return 0; +} + +/** + * Get shaper of mqprio Qdisc + * @arg qdisc MQPRIO qdisc. + * @return shaper on success or negative error code. + */ +int rtnl_qdisc_mqprio_get_shaper(struct rtnl_qdisc *qdisc) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc)))) + return -NLE_INVAL; + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER) + return mqprio->qm_shaper; + else + return -NLE_MISSING_ATTR; +} + +/** + * Set minimum value of bandwidth rate limit for each traffic class + * @arg qdisc MQPRIO qdisc. + * @arg min minimum rate for each traffic class + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_set_min_rate(struct rtnl_qdisc *qdisc, uint64_t min[], int len) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER)) + return -NLE_MISSING_ATTR; + + if (mqprio->qm_shaper != TC_MQPRIO_SHAPER_BW_RATE) + return -NLE_INVAL; + + if (len < 0 || len > TC_QOPT_MAX_QUEUE) + return -NLE_RANGE; + + memset(mqprio->qm_min_rate, 0, sizeof(mqprio->qm_min_rate)); + memcpy(mqprio->qm_min_rate, min, len * sizeof(uint64_t)); + mqprio->qm_mask |= SCH_MQPRIO_ATTR_MIN_RATE; + + return 0; +} + +/** + * Get minimum value of bandwidth rate limit for each traffic class + * @arg qdisc MQPRIO qdisc. + * @arg min minimum rate for each traffic class + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_get_min_rate(struct rtnl_qdisc *qdisc, uint64_t *min) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc)))) + return -NLE_INVAL; + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MIN_RATE) { + memcpy(min, mqprio->qm_min_rate, TC_QOPT_MAX_QUEUE * sizeof(uint64_t)); + return 0; + } + + return -NLE_MISSING_ATTR; +} + +/** + * Set maximum value of bandwidth rate limit for each traffic class + * @arg qdisc MQPRIO qdisc. + * @arg max maximum rate for each traffic class + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_set_max_rate(struct rtnl_qdisc *qdisc, uint64_t max[], int len) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER)) + return -NLE_MISSING_ATTR; + + if (mqprio->qm_shaper != TC_MQPRIO_SHAPER_BW_RATE) + return -NLE_INVAL; + + if (len < 0 || len > TC_QOPT_MAX_QUEUE) + return -NLE_RANGE; + + memset(mqprio->qm_max_rate, 0, sizeof(mqprio->qm_max_rate)); + memcpy(mqprio->qm_max_rate, max, len * sizeof(uint64_t)); + mqprio->qm_mask |= SCH_MQPRIO_ATTR_MAX_RATE; + + return 0; +} + +/** + * Get maximum value of bandwidth rate limit for each traffic class + * @arg qdisc MQPRIO qdisc. + * @arg min maximum rate for each traffic class + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_get_max_rate(struct rtnl_qdisc *qdisc, uint64_t *max) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc)))) + return -NLE_INVAL; + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MAX_RATE) { + memcpy(max, mqprio->qm_max_rate, TC_QOPT_MAX_QUEUE * sizeof(uint64_t)); + return 0; + } + + return -NLE_MISSING_ATTR; +} + +/** @} */ + +static struct rtnl_tc_ops mqprio_ops = { + .to_kind = "mqprio", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_mqprio), + .to_msg_parser = mqprio_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = mqprio_dump_line, + [NL_DUMP_DETAILS] = mqprio_dump_details, + }, + .to_msg_fill = mqprio_msg_fill, +}; + +static void _nl_init mqprio_init(void) +{ + rtnl_tc_register(&mqprio_ops); +} + +static void _nl_exit mqprio_exit(void) +{ + rtnl_tc_unregister(&mqprio_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/netem.c b/libnl/lib/route/qdisc/netem.c new file mode 100644 index 0000000..8ced034 --- /dev/null +++ b/libnl/lib/route/qdisc/netem.c @@ -0,0 +1,1027 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_netem Network Emulator + * @brief + * + * For further documentation see http://linux-net.osdl.org/index.php/Netem + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include + +#include "tc-api.h" +#include "nl-priv-dynamic-core/nl-core.h" + +/** @cond SKIP */ +struct rtnl_netem_corr { + uint32_t nmc_delay; + uint32_t nmc_loss; + uint32_t nmc_duplicate; +}; + +struct rtnl_netem_reo { + uint32_t nmro_probability; + uint32_t nmro_correlation; +}; + +struct rtnl_netem_crpt { + uint32_t nmcr_probability; + uint32_t nmcr_correlation; +}; + +struct rtnl_netem_dist { + int16_t *dist_data; + size_t dist_size; +}; + +struct rtnl_netem { + uint32_t qnm_latency; + uint32_t qnm_limit; + uint32_t qnm_loss; + uint32_t qnm_gap; + uint32_t qnm_duplicate; + uint32_t qnm_jitter; + uint32_t qnm_mask; + struct rtnl_netem_corr qnm_corr; + struct rtnl_netem_reo qnm_ro; + struct rtnl_netem_crpt qnm_crpt; + struct rtnl_netem_dist qnm_dist; +}; + +#define SCH_NETEM_ATTR_LATENCY 0x0001 +#define SCH_NETEM_ATTR_LIMIT 0x0002 +#define SCH_NETEM_ATTR_LOSS 0x0004 +#define SCH_NETEM_ATTR_GAP 0x0008 +#define SCH_NETEM_ATTR_DUPLICATE 0x0010 +#define SCH_NETEM_ATTR_JITTER 0x0020 +#define SCH_NETEM_ATTR_DELAY_CORR 0x0040 +#define SCH_NETEM_ATTR_LOSS_CORR 0x0080 +#define SCH_NETEM_ATTR_DUP_CORR 0x0100 +#define SCH_NETEM_ATTR_RO_PROB 0x0200 +#define SCH_NETEM_ATTR_RO_CORR 0x0400 +#define SCH_NETEM_ATTR_CORRUPT_PROB 0x0800 +#define SCH_NETEM_ATTR_CORRUPT_CORR 0x1000 +#define SCH_NETEM_ATTR_DIST 0x2000 +/** @endcond */ + +static struct nla_policy netem_policy[TCA_NETEM_MAX+1] = { + [TCA_NETEM_CORR] = { .minlen = sizeof(struct tc_netem_corr) }, + [TCA_NETEM_REORDER] = { .minlen = sizeof(struct tc_netem_reorder) }, + [TCA_NETEM_CORRUPT] = { .minlen = sizeof(struct tc_netem_corrupt) }, +}; + +static int netem_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_netem *netem = data; + struct tc_netem_qopt *opts; + int len, err = 0; + + if (tc->tc_opts->d_size < sizeof(*opts)) + return -NLE_INVAL; + + opts = (struct tc_netem_qopt *) tc->tc_opts->d_data; + netem->qnm_latency = opts->latency; + netem->qnm_limit = opts->limit; + netem->qnm_loss = opts->loss; + netem->qnm_gap = opts->gap; + netem->qnm_duplicate = opts->duplicate; + netem->qnm_jitter = opts->jitter; + + netem->qnm_mask = (SCH_NETEM_ATTR_LATENCY | SCH_NETEM_ATTR_LIMIT | + SCH_NETEM_ATTR_LOSS | SCH_NETEM_ATTR_GAP | + SCH_NETEM_ATTR_DUPLICATE | SCH_NETEM_ATTR_JITTER); + + len = tc->tc_opts->d_size - sizeof(*opts); + + if (len > 0) { + struct nlattr *tb[TCA_NETEM_MAX+1]; + + err = nla_parse(tb, TCA_NETEM_MAX, (struct nlattr *) + ((char *) tc->tc_opts->d_data + sizeof(*opts)), + len, netem_policy); + if (err < 0) { + free(netem); + return err; + } + + if (tb[TCA_NETEM_CORR]) { + struct tc_netem_corr cor; + + nla_memcpy(&cor, tb[TCA_NETEM_CORR], sizeof(cor)); + netem->qnm_corr.nmc_delay = cor.delay_corr; + netem->qnm_corr.nmc_loss = cor.loss_corr; + netem->qnm_corr.nmc_duplicate = cor.dup_corr; + + netem->qnm_mask |= (SCH_NETEM_ATTR_DELAY_CORR | + SCH_NETEM_ATTR_LOSS_CORR | + SCH_NETEM_ATTR_DUP_CORR); + } + + if (tb[TCA_NETEM_REORDER]) { + struct tc_netem_reorder ro; + + nla_memcpy(&ro, tb[TCA_NETEM_REORDER], sizeof(ro)); + netem->qnm_ro.nmro_probability = ro.probability; + netem->qnm_ro.nmro_correlation = ro.correlation; + + netem->qnm_mask |= (SCH_NETEM_ATTR_RO_PROB | + SCH_NETEM_ATTR_RO_CORR); + } + + if (tb[TCA_NETEM_CORRUPT]) { + struct tc_netem_corrupt corrupt; + + nla_memcpy(&corrupt, tb[TCA_NETEM_CORRUPT], sizeof(corrupt)); + netem->qnm_crpt.nmcr_probability = corrupt.probability; + netem->qnm_crpt.nmcr_correlation = corrupt.correlation; + + netem->qnm_mask |= (SCH_NETEM_ATTR_CORRUPT_PROB | + SCH_NETEM_ATTR_CORRUPT_CORR); + } + + /* sch_netem does not currently dump TCA_NETEM_DELAY_DIST */ + netem->qnm_dist.dist_data = NULL; + netem->qnm_dist.dist_size = 0; + } + + return 0; +} + +static void netem_free_data(struct rtnl_tc *tc, void *data) +{ + struct rtnl_netem *netem = data; + + if (!netem) + return; + + free(netem->qnm_dist.dist_data); +} + +static void netem_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_netem *netem = data; + + if (netem) { + if (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT && netem->qnm_limit > 0) + nl_dump(p, " limit %dpkts", netem->qnm_limit); + else + nl_dump(p, " no limit"); + } +} + +static void netem_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_netem *netem = data; + char buf[32]; + + if (netem) { + if (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY && netem->qnm_latency > 0) { + nl_msec2str(nl_ticks2us(netem->qnm_latency) / 1000, buf, sizeof(buf)); + nl_dump(p, " latency %s", buf); + + if (netem->qnm_mask & SCH_NETEM_ATTR_JITTER && netem->qnm_jitter > 0) { + nl_msec2str(nl_ticks2us(netem->qnm_jitter) / 1000, buf, sizeof(buf)); + nl_dump(p, " jitter %s", buf); + + if (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR && netem->qnm_corr.nmc_delay > 0) + nl_dump(p, " %d", netem->qnm_corr.nmc_delay); + } + } + + if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS && netem->qnm_loss > 0) { + nl_dump(p, " loss %d", netem->qnm_loss); + + if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR && netem->qnm_corr.nmc_loss > 0) + nl_dump(p, " %d", netem->qnm_corr.nmc_loss); + } + + if (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE && netem->qnm_duplicate > 0) { + nl_dump(p, " duplicate %d", netem->qnm_duplicate); + + if (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR && netem->qnm_corr.nmc_duplicate > 0) + nl_dump(p, " %d", netem->qnm_corr.nmc_duplicate); + } + + if (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB && netem->qnm_ro.nmro_probability > 0) { + nl_dump(p, " reorder %d", netem->qnm_ro.nmro_probability); + + if (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR && netem->qnm_ro.nmro_correlation > 0) + nl_dump(p, " %d", netem->qnm_ro.nmro_correlation); + + if (netem->qnm_mask & SCH_NETEM_ATTR_GAP && netem->qnm_gap > 0) + nl_dump(p, " gap %d", netem->qnm_gap); + } + + if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB && netem->qnm_crpt.nmcr_probability > 0) { + nl_dump(p, " reorder %d", netem->qnm_crpt.nmcr_probability); + + if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR && netem->qnm_crpt.nmcr_correlation > 0) + nl_dump(p, " %d", netem->qnm_crpt.nmcr_correlation); + } + } +} + +static int netem_msg_fill_raw(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) +{ + int err = 0; + struct tc_netem_qopt opts; + struct tc_netem_corr cor; + struct tc_netem_reorder reorder; + struct tc_netem_corrupt corrupt; + struct rtnl_netem *netem = data; + + unsigned char set_correlation = 0, set_reorder = 0; + unsigned char set_corrupt = 0, set_dist = 0; + + struct nlattr* head; + struct nlattr* tail; + int old_len; + + if (!netem) + BUG(); + + memset(&opts, 0, sizeof(opts)); + memset(&cor, 0, sizeof(cor)); + memset(&reorder, 0, sizeof(reorder)); + memset(&corrupt, 0, sizeof(corrupt)); + + msg->nm_nlh->nlmsg_flags |= NLM_F_REQUEST; + + if (netem->qnm_ro.nmro_probability != 0) { + if (netem->qnm_latency == 0) + return -NLE_MISSING_ATTR; + if (netem->qnm_gap == 0) + netem->qnm_gap = 1; + } else if (netem->qnm_gap) + return -NLE_MISSING_ATTR; + + if (netem->qnm_corr.nmc_delay != 0) { + if (netem->qnm_latency == 0 || netem->qnm_jitter == 0) + return -NLE_MISSING_ATTR; + set_correlation = 1; + } + + if (netem->qnm_corr.nmc_loss != 0) { + if (netem->qnm_loss == 0) + return -NLE_MISSING_ATTR; + set_correlation = 1; + } + + if (netem->qnm_corr.nmc_duplicate != 0) { + if (netem->qnm_duplicate == 0) + return -NLE_MISSING_ATTR; + set_correlation = 1; + } + + if (netem->qnm_ro.nmro_probability != 0) + set_reorder = 1; + else if (netem->qnm_ro.nmro_correlation != 0) + return -NLE_MISSING_ATTR; + + if (netem->qnm_crpt.nmcr_probability != 0) + set_corrupt = 1; + else if (netem->qnm_crpt.nmcr_correlation != 0) + return -NLE_MISSING_ATTR; + + if (netem->qnm_dist.dist_data && netem->qnm_dist.dist_size) { + if (netem->qnm_latency == 0 || netem->qnm_jitter == 0) + return -NLE_MISSING_ATTR; + else { + /* Resize to accomodate the large distribution table */ + int new_msg_len = msg->nm_size + netem->qnm_dist.dist_size * + sizeof(netem->qnm_dist.dist_data[0]); + struct nlmsghdr *new_nlh = realloc(msg->nm_nlh, new_msg_len); + + if (new_nlh == NULL) + return -NLE_NOMEM; + msg->nm_nlh = new_nlh; + msg->nm_size = new_msg_len; + set_dist = 1; + } + } + + opts.latency = netem->qnm_latency; + opts.limit = netem->qnm_limit ? netem->qnm_limit : 1000; + opts.loss = netem->qnm_loss; + opts.gap = netem->qnm_gap; + opts.duplicate = netem->qnm_duplicate; + opts.jitter = netem->qnm_jitter; + + NLA_PUT(msg, TCA_OPTIONS, sizeof(opts), &opts); + + if (set_correlation) { + cor.delay_corr = netem->qnm_corr.nmc_delay; + cor.loss_corr = netem->qnm_corr.nmc_loss; + cor.dup_corr = netem->qnm_corr.nmc_duplicate; + + NLA_PUT(msg, TCA_NETEM_CORR, sizeof(cor), &cor); + } + + if (set_reorder) { + reorder.probability = netem->qnm_ro.nmro_probability; + reorder.correlation = netem->qnm_ro.nmro_correlation; + + NLA_PUT(msg, TCA_NETEM_REORDER, sizeof(reorder), &reorder); + } + + if (set_corrupt) { + corrupt.probability = netem->qnm_crpt.nmcr_probability; + corrupt.correlation = netem->qnm_crpt.nmcr_correlation; + + NLA_PUT(msg, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt); + } + + if (set_dist) { + NLA_PUT(msg, TCA_NETEM_DELAY_DIST, + netem->qnm_dist.dist_size * sizeof(netem->qnm_dist.dist_data[0]), + netem->qnm_dist.dist_data); + } + + /* Length specified in the TCA_OPTIONS section must span the entire + * remainder of the message. That's just the way that sch_netem expects it. + * Maybe there's a more succinct way to do this at a higher level. + */ + head = (struct nlattr *)(((char *) NLMSG_DATA(msg->nm_nlh)) + + NLMSG_LENGTH(sizeof(struct tcmsg)) - NLMSG_ALIGNTO); + + tail = (struct nlattr *)(((char *) (msg->nm_nlh)) + + NLMSG_ALIGN(msg->nm_nlh->nlmsg_len)); + + old_len = head->nla_len; + head->nla_len = (char *)tail - (char *)head; + msg->nm_nlh->nlmsg_len += (head->nla_len - old_len); + + return err; +nla_put_failure: + return -NLE_MSGSIZE; +} + +/** + * @name Queue Limit + * @{ + */ + +/** + * Set limit of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg limit New limit in bytes. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_limit = limit; + netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT; +} + +/** + * Get limit of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Limit in bytes or a negative error code. + */ +int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT) + return netem->qnm_limit; + else + return -NLE_NOATTR; +} + +/** @} */ + +/** + * @name Packet Re-ordering + * @{ + */ + +/** + * Set re-ordering gap of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg gap New gap in number of packets. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_gap = gap; + netem->qnm_mask |= SCH_NETEM_ATTR_GAP; +} + +/** + * Get re-ordering gap of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Re-ordering gap in packets or a negative error code. + */ +int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (netem->qnm_mask & SCH_NETEM_ATTR_GAP) + return netem->qnm_gap; + else + return -NLE_NOATTR; +} + +/** + * Set re-ordering probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New re-ordering probability. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_ro.nmro_probability = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_RO_PROB; +} + +/** + * Get re-ordering probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Re-ordering probability or a negative error code. + */ +int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB) + return netem->qnm_ro.nmro_probability; + else + return -NLE_NOATTR; +} + +/** + * Set re-order correlation probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New re-ordering correlation probability. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_ro.nmro_correlation = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_RO_CORR; +} + +/** + * Get re-ordering correlation probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Re-ordering correlation probability or a negative error code. + */ +int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR) + return netem->qnm_ro.nmro_correlation; + else + return -NLE_NOATTR; +} + +/** @} */ + +/** + * @name Corruption + * @{ + */ + +/** + * Set corruption probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New corruption probability. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_corruption_probability(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_crpt.nmcr_probability = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_PROB; +} + +/** + * Get corruption probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Corruption probability or a negative error code. + */ +int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB) + return netem->qnm_crpt.nmcr_probability; + else + return -NLE_NOATTR; +} + +/** + * Set corruption correlation probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New corruption correlation probability. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_crpt.nmcr_correlation = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_CORR; +} + +/** + * Get corruption correlation probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Corruption correlation probability or a negative error code. + */ +int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR) + return netem->qnm_crpt.nmcr_correlation; + else + return -NLE_NOATTR; +} + +/** @} */ + +/** + * @name Packet Loss + * @{ + */ + +/** + * Set packet loss probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New packet loss probability. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_loss = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_LOSS; +} + +/** + * Get packet loss probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet loss probability or a negative error code. + */ +int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS) + return netem->qnm_loss; + else + return -NLE_NOATTR; +} + +/** + * Set packet loss correlation probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New packet loss correlation. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_corr.nmc_loss = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_LOSS_CORR; +} + +/** + * Get packet loss correlation probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet loss correlation probability or a negative error code. + */ +int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR) + return netem->qnm_corr.nmc_loss; + else + return -NLE_NOATTR; +} + +/** @} */ + +/** + * @name Packet Duplication + * @{ + */ + +/** + * Set packet duplication probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New packet duplication probability. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_duplicate(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_duplicate = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_DUPLICATE; +} + +/** + * Get packet duplication probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet duplication probability or a negative error code. + */ +int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE) + return netem->qnm_duplicate; + else + return -NLE_NOATTR; +} + +/** + * Set packet duplication correlation probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New packet duplication correlation probability. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_corr.nmc_duplicate = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_DUP_CORR; +} + +/** + * Get packet duplication correlation probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet duplication correlation probability or a negative error code. + */ +int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR) + return netem->qnm_corr.nmc_duplicate; + else + return -NLE_NOATTR; +} + +/** @} */ + +/** + * @name Packet Delay + * @{ + */ + +/** + * Set packet delay of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg delay New packet delay in micro seconds. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_latency = nl_us2ticks(delay); + netem->qnm_mask |= SCH_NETEM_ATTR_LATENCY; +} + +/** + * Get packet delay of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet delay in micro seconds or a negative error code. + */ +int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY) + return nl_ticks2us(netem->qnm_latency); + else + return -NLE_NOATTR; +} + +/** + * Set packet delay jitter of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg jitter New packet delay jitter in micro seconds. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_jitter = nl_us2ticks(jitter); + netem->qnm_mask |= SCH_NETEM_ATTR_JITTER; +} + +/** + * Get packet delay jitter of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet delay jitter in micro seconds or a negative error code. + */ +int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_JITTER) + return nl_ticks2us(netem->qnm_jitter); + else + return -NLE_NOATTR; +} + +/** + * Set packet delay correlation probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New packet delay correlation probability. + */ +void rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_corr.nmc_delay = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_DELAY_CORR; +} + +/** + * Get packet delay correlation probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet delay correlation probability or a negative error code. + */ +int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR) + return netem->qnm_corr.nmc_delay; + else + return -NLE_NOATTR; +} + +/** + * Get the size of the distribution table. + * @arg qdisc Netem qdisc. + * @return Distribution table size or a negative error code. + */ +int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_DIST) + return netem->qnm_dist.dist_size; + else + return -NLE_NOATTR; +} + +/** + * Get a pointer to the distribution table. + * @arg qdisc Netem qdisc. + * @arg dist_ptr The pointer to set. + * @return Negative error code on failure or 0 on success. + */ +int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_ptr) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_DIST) { + *dist_ptr = netem->qnm_dist.dist_data; + return 0; + } else + return -NLE_NOATTR; +} + +/** + * Set the delay distribution data. Latency/jitter must be set before applying. + * @arg qdisc Netem qdisc. + * @return 0 on success, error code on failure. + */ +int rtnl_netem_set_delay_distribution_data(struct rtnl_qdisc *qdisc, const int16_t *data, size_t len) { + struct rtnl_netem *netem; + int16_t *new_data; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (len > MAXDIST) + return -NLE_INVAL; + + new_data = (int16_t *) calloc(len, sizeof(int16_t)); + if (!new_data) + return -NLE_NOMEM; + + free (netem->qnm_dist.dist_data); + netem->qnm_dist.dist_data = new_data; + + memcpy(netem->qnm_dist.dist_data, data, len * sizeof(int16_t)); + + netem->qnm_dist.dist_size = len; + netem->qnm_mask |= SCH_NETEM_ATTR_DIST; + + return 0; +} + +/** + * Load the delay distribution from a file. Latency/jitter must be set before applying. + * @arg qdisc Netem qdisc. + * @arg dist_type The name of the distribution (type, file, path/file). + * @return 0 on success, error code on failure. + */ +int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type) { + FILE *f; + int n = 0; + size_t i; + size_t len = 2048; + _nl_auto_free char *line = NULL; + char name[NAME_MAX]; + char dist_suffix[] = ".dist"; + _nl_auto_free int16_t *data = NULL; + char *test_suffix; + + /* Check several locations for the dist file */ + char *test_path[] = { + "", + "./", + "/usr/lib/tc/", + "/usr/lib64/tc/", + "/usr/local/lib/tc/", + }; + + /* If the given filename already ends in .dist, don't append it later */ + test_suffix = strstr(dist_type, dist_suffix); + if (test_suffix != NULL && strlen(test_suffix) == 5) + strcpy(dist_suffix, ""); + + for (i = 0; i < ARRAY_SIZE(test_path); i++) { + snprintf(name, NAME_MAX, "%s%s%s", test_path[i], dist_type, dist_suffix); + if ((f = fopen(name, "re"))) + break; + } + + if (f == NULL) + return -nl_syserr2nlerr(errno); + + data = (int16_t *) calloc(MAXDIST, sizeof(int16_t)); + line = (char *) calloc(len + 1, sizeof(char)); + if (!data || !line) { + fclose(f); + return -NLE_NOMEM; + } + + while (getline(&line, &len, f) != -1) { + char *p, *endp; + + if (*line == '\n' || *line == '#') + continue; + + for (p = line; ; p = endp) { + long x = strtol(p, &endp, 0); + if (endp == p) break; + + if (n >= MAXDIST) { + fclose(f); + return -NLE_INVAL; + } + data[n++] = x; + } + } + + fclose(f); + i = rtnl_netem_set_delay_distribution_data(qdisc, data, n); + return i; +} + +/** @} */ + +static struct rtnl_tc_ops netem_ops = { + .to_kind = "netem", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_netem), + .to_msg_parser = netem_msg_parser, + .to_free_data = netem_free_data, + .to_dump[NL_DUMP_LINE] = netem_dump_line, + .to_dump[NL_DUMP_DETAILS] = netem_dump_details, + .to_msg_fill_raw = netem_msg_fill_raw, +}; + +static void _nl_init netem_init(void) +{ + rtnl_tc_register(&netem_ops); +} + +static void _nl_exit netem_exit(void) +{ + rtnl_tc_unregister(&netem_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/plug.c b/libnl/lib/route/qdisc/plug.c new file mode 100644 index 0000000..f010802 --- /dev/null +++ b/libnl/lib/route/qdisc/plug.c @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2012 Shriram Rajagopalan + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_plug Plug/Unplug Traffic (PLUG) + * @brief + * + * Queue traffic until an explicit release command. + * + * There are two ways to use this qdisc: + * 1. A simple "instantaneous" plug/unplug operation, by issuing an alternating + * sequence of TCQ_PLUG_BUFFER & TCQ_PLUG_RELEASE_INDEFINITE commands. + * + * 2. For network output buffering (a.k.a output commit) functionality. + * Output commit property is commonly used by applications using checkpoint + * based fault-tolerance to ensure that the checkpoint from which a system + * is being restored is consistent w.r.t outside world. + * + * Consider for e.g. Remus - a Virtual Machine checkpointing system, + * wherein a VM is checkpointed, say every 50ms. The checkpoint is replicated + * asynchronously to the backup host, while the VM continues executing the + * next epoch speculatively. + * + * The following is a typical sequence of output buffer operations: + * 1.At epoch i, start_buffer(i) + * 2. At end of epoch i (i.e. after 50ms): + * 2.1 Stop VM and take checkpoint(i). + * 2.2 start_buffer(i+1) and Resume VM + * 3. While speculatively executing epoch(i+1), asynchronously replicate + * checkpoint(i) to backup host. + * 4. When checkpoint_ack(i) is received from backup, release_buffer(i) + * Thus, this Qdisc would receive the following sequence of commands: + * TCQ_PLUG_BUFFER (epoch i) + * .. TCQ_PLUG_BUFFER (epoch i+1) + * ....TCQ_PLUG_RELEASE_ONE (epoch i) + * ......TCQ_PLUG_BUFFER (epoch i+2) + * ........ + * + * + * State of the queue, when used for network output buffering: + * + * plug(i+1) plug(i) head + * ------------------+--------------------+----------------> + * | | + * | | + * pkts_current_epoch| pkts_last_epoch |pkts_to_release + * ----------------->|<--------+--------->|+---------------> + * v v + * + * + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include + +#include "tc-api.h" + +struct rtnl_plug { + int action; + uint32_t limit; +}; + +static int plug_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_plug *plug = data; + struct tc_plug_qopt opts; + + if (!plug) + return -NLE_INVAL; + + opts.action = plug->action; + opts.limit = plug->limit; + + return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD); +} + +/** + * @name Attribute Modification + * @{ + */ + +/** + * Insert a plug into the qdisc and buffer any incoming + * network traffic. + * @arg qdisc PLUG qdisc to be modified. + */ +int rtnl_qdisc_plug_buffer(struct rtnl_qdisc *qdisc) +{ + struct rtnl_plug *plug; + + if (!(plug = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + plug->action = TCQ_PLUG_BUFFER; + return 0; +} + +/** + * Unplug the qdisc, releasing packets from queue head + * to the last complete buffer, while new traffic + * continues to be buffered. + * @arg qdisc PLUG qdisc to be modified. + */ +int rtnl_qdisc_plug_release_one(struct rtnl_qdisc *qdisc) +{ + struct rtnl_plug *plug; + + if (!(plug = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + plug->action = TCQ_PLUG_RELEASE_ONE; + return 0; +} + +/** + * Indefinitely unplug the qdisc, releasing all packets. + * Network traffic will not be buffered until the next + * buffer command is issued. + * @arg qdisc PLUG qdisc to be modified. + */ +int rtnl_qdisc_plug_release_indefinite(struct rtnl_qdisc *qdisc) +{ + struct rtnl_plug *plug; + + if (!(plug = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + plug->action = TCQ_PLUG_RELEASE_INDEFINITE; + return 0; +} + +/** + * Set limit of PLUG qdisc. + * @arg qdisc PLUG qdisc to be modified. + * @arg limit New limit. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_plug_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_plug *plug; + + if (!(plug = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + plug->action = TCQ_PLUG_LIMIT; + plug->limit = limit; + + return 0; +} + +/** @} */ + +static struct rtnl_tc_ops plug_ops = { + .to_kind = "plug", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_plug), + .to_msg_fill = plug_msg_fill, +}; + +static void _nl_init plug_init(void) +{ + rtnl_tc_register(&plug_ops); +} + +static void _nl_exit plug_exit(void) +{ + rtnl_tc_unregister(&plug_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/prio.c b/libnl/lib/route/qdisc/prio.c new file mode 100644 index 0000000..e733845 --- /dev/null +++ b/libnl/lib/route/qdisc/prio.c @@ -0,0 +1,295 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_prio (Fast) Prio + * @brief + * + * @par 1) Typical PRIO configuration + * @code + * // Specify the maximal number of bands to be used for this PRIO qdisc. + * rtnl_qdisc_prio_set_bands(qdisc, QDISC_PRIO_DEFAULT_BANDS); + * + * // Provide a map assigning each priority to a band number. + * uint8_t map[] = QDISC_PRIO_DEFAULT_PRIOMAP; + * rtnl_qdisc_prio_set_priomap(qdisc, map, sizeof(map)); + * @endcode + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include + +#include "tc-api.h" + +/** @cond SKIP */ +struct rtnl_prio { + uint32_t qp_bands; + uint8_t qp_priomap[TC_PRIO_MAX + 1]; + uint32_t qp_mask; +}; + +#define SCH_PRIO_ATTR_BANDS 1 +#define SCH_PRIO_ATTR_PRIOMAP 2 +/** @endcond */ + +static int prio_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_prio *prio = data; + struct tc_prio_qopt *opt; + + if (tc->tc_opts->d_size < sizeof(*opt)) + return -NLE_INVAL; + + opt = (struct tc_prio_qopt *) tc->tc_opts->d_data; + prio->qp_bands = opt->bands; + memcpy(prio->qp_priomap, opt->priomap, sizeof(prio->qp_priomap)); + prio->qp_mask = (SCH_PRIO_ATTR_BANDS | SCH_PRIO_ATTR_PRIOMAP); + + return 0; +} + +static void prio_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_prio *prio = data; + + if (prio) + nl_dump(p, " bands %u", prio->qp_bands); +} + +static void prio_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_prio *prio = data; + int i, hp; + + if (!prio) + return; + + nl_dump(p, "priomap ["); + + for (i = 0; i <= TC_PRIO_MAX; i++) + nl_dump(p, "%u%s", prio->qp_priomap[i], + i < TC_PRIO_MAX ? " " : ""); + + nl_dump(p, "]\n"); + nl_new_line(p); + + hp = (((TC_PRIO_MAX/2) + 1) & ~1); + + for (i = 0; i < hp; i++) { + char a[32]; + nl_dump(p, " %18s => %u", + rtnl_prio2str(i, a, sizeof(a)), + prio->qp_priomap[i]); + if (hp+i <= TC_PRIO_MAX) { + nl_dump(p, " %18s => %u", + rtnl_prio2str(hp+i, a, sizeof(a)), + prio->qp_priomap[hp+i]); + if (i < (hp - 1)) { + nl_dump(p, "\n"); + nl_new_line(p); + } + } + } +} + +static int prio_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_prio *prio = data; + struct tc_prio_qopt opts; + + if (!prio || !(prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP)) + BUG(); + + opts.bands = prio->qp_bands; + memcpy(opts.priomap, prio->qp_priomap, sizeof(opts.priomap)); + + return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD); +} + +/** + * @name Attribute Modification + * @{ + */ + +/** + * Set number of bands of PRIO qdisc. + * @arg qdisc PRIO qdisc to be modified. + * @arg bands New number of bands. + * @return 0 on success or a negative error code. + */ +void rtnl_qdisc_prio_set_bands(struct rtnl_qdisc *qdisc, int bands) +{ + struct rtnl_prio *prio; + + if (!(prio = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + prio->qp_bands = bands; + prio->qp_mask |= SCH_PRIO_ATTR_BANDS; +} + +/** + * Get number of bands of PRIO qdisc. + * @arg qdisc PRIO qdisc. + * @return Number of bands or a negative error code. + */ +int rtnl_qdisc_prio_get_bands(struct rtnl_qdisc *qdisc) +{ + struct rtnl_prio *prio; + + if (!(prio = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (prio->qp_mask & SCH_PRIO_ATTR_BANDS) + return prio->qp_bands; + else + return -NLE_NOMEM; +} + +/** + * Set priomap of the PRIO qdisc. + * @arg qdisc PRIO qdisc to be modified. + * @arg priomap New priority mapping. + * @arg len Length of priomap (# of elements). + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_prio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[], + int len) +{ + struct rtnl_prio *prio; + int i; + + if (!(prio = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (!(prio->qp_mask & SCH_PRIO_ATTR_BANDS)) + return -NLE_MISSING_ATTR; + + if ((len / sizeof(uint8_t)) > (TC_PRIO_MAX+1)) + return -NLE_RANGE; + + for (i = 0; i <= TC_PRIO_MAX; i++) { + if (priomap[i] > prio->qp_bands) + return -NLE_RANGE; + } + + memcpy(prio->qp_priomap, priomap, len); + prio->qp_mask |= SCH_PRIO_ATTR_PRIOMAP; + + return 0; +} + +/** + * Get priomap of a PRIO qdisc. + * @arg qdisc PRIO qdisc. + * @return Priority mapping as array of size TC_PRIO_MAX+1 + * or NULL if an error occured. + */ +uint8_t *rtnl_qdisc_prio_get_priomap(struct rtnl_qdisc *qdisc) +{ + struct rtnl_prio *prio; + + if (!(prio = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP) + return prio->qp_priomap; + else + return NULL; +} + +/** @} */ + +/** + * @name Priority Band Translations + * @{ + */ + +static const struct trans_tbl prios[] = { + __ADD(TC_PRIO_BESTEFFORT,besteffort), + __ADD(TC_PRIO_FILLER,filler), + __ADD(TC_PRIO_BULK,bulk), + __ADD(TC_PRIO_INTERACTIVE_BULK,interactive_bulk), + __ADD(TC_PRIO_INTERACTIVE,interactive), + __ADD(TC_PRIO_CONTROL,control), +}; + +/** + * Convert priority to character string. + * @arg prio Priority. + * @arg buf Destination buffer + * @arg size Size of destination buffer. + * + * Converts a priority to a character string and stores the result in + * the specified destination buffer. + * + * @return Name of priority as character string. + */ +char * rtnl_prio2str(int prio, char *buf, size_t size) +{ + return __type2str(prio, buf, size, prios, ARRAY_SIZE(prios)); +} + +/** + * Convert character string to priority. + * @arg name Name of priority. + * + * Converts the provided character string specifying a priority + * to the corresponding numeric value. + * + * @return Numeric priority or a negative value if no match was found. + */ +int rtnl_str2prio(const char *name) +{ + return __str2type(name, prios, ARRAY_SIZE(prios)); +} + +/** @} */ + +static struct rtnl_tc_ops prio_ops = { + .to_kind = "prio", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_prio), + .to_msg_parser = prio_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = prio_dump_line, + [NL_DUMP_DETAILS] = prio_dump_details, + }, + .to_msg_fill = prio_msg_fill, +}; + +static struct rtnl_tc_ops pfifo_fast_ops = { + .to_kind = "pfifo_fast", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_prio), + .to_msg_parser = prio_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = prio_dump_line, + [NL_DUMP_DETAILS] = prio_dump_details, + }, + .to_msg_fill = prio_msg_fill, +}; + +static void _nl_init prio_init(void) +{ + rtnl_tc_register(&prio_ops); + rtnl_tc_register(&pfifo_fast_ops); +} + +static void _nl_exit prio_exit(void) +{ + rtnl_tc_unregister(&prio_ops); + rtnl_tc_unregister(&pfifo_fast_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/red.c b/libnl/lib/route/qdisc/red.c new file mode 100644 index 0000000..e793b78 --- /dev/null +++ b/libnl/lib/route/qdisc/red.c @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_red Random Early Detection (RED) + * @brief + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include + +#include "tc-api.h" + +/** @cond SKIP */ +struct rtnl_red { + uint32_t qr_limit; + uint32_t qr_qth_min; + uint32_t qr_qth_max; + uint8_t qr_flags; + uint8_t qr_wlog; + uint8_t qr_plog; + uint8_t qr_scell_log; + uint32_t qr_mask; +}; + +#define RED_ATTR_LIMIT 0x01 +#define RED_ATTR_QTH_MIN 0x02 +#define RED_ATTR_QTH_MAX 0x04 +#define RED_ATTR_FLAGS 0x08 +#define RED_ATTR_WLOG 0x10 +#define RED_ATTR_PLOG 0x20 +#define RED_ATTR_SCELL_LOG 0x40 +/** @endcond */ + +static struct nla_policy red_policy[TCA_RED_MAX+1] = { + [TCA_RED_PARMS] = { .minlen = sizeof(struct tc_red_qopt) }, +}; + +static int red_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct nlattr *tb[TCA_RED_MAX+1]; + struct rtnl_red *red = data; + struct tc_red_qopt *opts; + int err; + + if (!(tc->ce_mask & TCA_ATTR_OPTS)) + return 0; + + err = tca_parse(tb, TCA_RED_MAX, tc, red_policy); + if (err < 0) + return err; + + if (!tb[TCA_RED_PARMS]) + return -NLE_MISSING_ATTR; + + opts = nla_data(tb[TCA_RED_PARMS]); + + red->qr_limit = opts->limit; + red->qr_qth_min = opts->qth_min; + red->qr_qth_max = opts->qth_max; + red->qr_flags = opts->flags; + red->qr_wlog = opts->Wlog; + red->qr_plog = opts->Plog; + red->qr_scell_log = opts->Scell_log; + + red->qr_mask = (RED_ATTR_LIMIT | RED_ATTR_QTH_MIN | RED_ATTR_QTH_MAX | + RED_ATTR_FLAGS | RED_ATTR_WLOG | RED_ATTR_PLOG | + RED_ATTR_SCELL_LOG); + + return 0; +} + +static void red_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_red *red = data; + + if (red) { + /* XXX: limit, min, max, flags */ + } +} + +static void red_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_red *red = data; + + if (red) { + /* XXX: wlog, plog, scell_log */ + } +} + +static void red_dump_stats(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_red *red = data; + + if (red) { + /* XXX: xstats */ + } +} + +static int red_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_red *red = data; + + if (!red) + BUG(); + +#if 0 + memset(&opts, 0, sizeof(opts)); + opts.quantum = sfq->qs_quantum; + opts.perturb_period = sfq->qs_perturb; + opts.limit = sfq->qs_limit; + + if (nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD) < 0) + goto errout; +#endif + + return -NLE_OPNOTSUPP; +} + +/** + * @name Attribute Access + * @{ + */ + +/** + * Set limit of RED qdisc. + * @arg qdisc RED qdisc to be modified. + * @arg limit New limit in number of packets. + * @return 0 on success or a negative error code. + */ +void rtnl_red_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_red *red; + + if (!(red = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + red->qr_limit = limit; + red->qr_mask |= RED_ATTR_LIMIT; +} + +/** + * Get limit of RED qdisc. + * @arg qdisc RED qdisc. + * @return Limit or a negative error code. + */ +int rtnl_red_get_limit(struct rtnl_qdisc *qdisc) +{ + struct rtnl_red *red; + + if (!(red = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (red->qr_mask & RED_ATTR_LIMIT) + return red->qr_limit; + else + return -NLE_NOATTR; +} + +/** @} */ + +static struct rtnl_tc_ops red_ops = { + .to_kind = "red", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_red), + .to_msg_parser = red_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = red_dump_line, + [NL_DUMP_DETAILS] = red_dump_details, + [NL_DUMP_STATS] = red_dump_stats, + }, + .to_msg_fill = red_msg_fill, +}; + +static void _nl_init red_init(void) +{ + rtnl_tc_register(&red_ops); +} + +static void _nl_exit red_exit(void) +{ + rtnl_tc_unregister(&red_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/sfq.c b/libnl/lib/route/qdisc/sfq.c new file mode 100644 index 0000000..4379889 --- /dev/null +++ b/libnl/lib/route/qdisc/sfq.c @@ -0,0 +1,260 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_sfq Stochastic Fairness Queueing (SFQ) + * @brief + * + * @par Parameter Description + * - \b Quantum: Number of bytes to send out per slot and round. + * - \b Perturbation: Timer period between changing the hash function. + * - \b Limit: Upper limit of queue in number of packets before SFQ starts + * dropping packets. + * - \b Divisor: Hash table divisor, i.e. size of hash table. + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include + +#include "tc-api.h" + +/** @cond SKIP */ +struct rtnl_sfq { + uint32_t qs_quantum; + uint32_t qs_perturb; + uint32_t qs_limit; + uint32_t qs_divisor; + uint32_t qs_flows; + uint32_t qs_mask; +}; + +#define SCH_SFQ_ATTR_QUANTUM 0x01 +#define SCH_SFQ_ATTR_PERTURB 0x02 +#define SCH_SFQ_ATTR_LIMIT 0x04 +#define SCH_SFQ_ATTR_DIVISOR 0x08 +#define SCH_SFQ_ATTR_FLOWS 0x10 +/** @endcond */ + +static int sfq_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_sfq *sfq = data; + struct tc_sfq_qopt *opts; + + if (!(tc->ce_mask & TCA_ATTR_OPTS)) + return 0; + + if (tc->tc_opts->d_size < sizeof(*opts)) + return -NLE_INVAL; + + opts = (struct tc_sfq_qopt *) tc->tc_opts->d_data; + + sfq->qs_quantum = opts->quantum; + sfq->qs_perturb = opts->perturb_period; + sfq->qs_limit = opts->limit; + sfq->qs_divisor = opts->divisor; + sfq->qs_flows = opts->flows; + + sfq->qs_mask = (SCH_SFQ_ATTR_QUANTUM | SCH_SFQ_ATTR_PERTURB | + SCH_SFQ_ATTR_LIMIT | SCH_SFQ_ATTR_DIVISOR | + SCH_SFQ_ATTR_FLOWS); + + return 0; +} + +static void sfq_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_sfq *sfq = data; + + if (sfq) + nl_dump(p, " quantum %u perturb %us", sfq->qs_quantum, + sfq->qs_perturb); +} + +static void sfq_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_sfq *sfq = data; + + if (sfq) + nl_dump(p, "limit %u divisor %u", + sfq->qs_limit, sfq->qs_divisor); +} + +static int sfq_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_sfq *sfq = data; + struct tc_sfq_qopt opts = {0}; + + if (!sfq) + BUG(); + + opts.quantum = sfq->qs_quantum; + opts.perturb_period = sfq->qs_perturb; + opts.limit = sfq->qs_limit; + + return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD); +} + +/** + * @name Attribute Access + * @{ + */ + +/** + * Set quantum of SFQ qdisc. + * @arg qdisc SFQ qdisc to be modified. + * @arg quantum New quantum in bytes. + * @return 0 on success or a negative error code. + */ +void rtnl_sfq_set_quantum(struct rtnl_qdisc *qdisc, int quantum) +{ + struct rtnl_sfq *sfq; + + if (!(sfq = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + sfq->qs_quantum = quantum; + sfq->qs_mask |= SCH_SFQ_ATTR_QUANTUM; +} + +/** + * Get quantum of SFQ qdisc. + * @arg qdisc SFQ qdisc. + * @return Quantum in bytes or a negative error code. + */ +int rtnl_sfq_get_quantum(struct rtnl_qdisc *qdisc) +{ + struct rtnl_sfq *sfq; + + if (!(sfq = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (sfq->qs_mask & SCH_SFQ_ATTR_QUANTUM) + return sfq->qs_quantum; + else + return -NLE_NOATTR; +} + +/** + * Set limit of SFQ qdisc. + * @arg qdisc SFQ qdisc to be modified. + * @arg limit New limit in number of packets. + * @return 0 on success or a negative error code. + */ +void rtnl_sfq_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_sfq *sfq; + + if (!(sfq = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + sfq->qs_limit = limit; + sfq->qs_mask |= SCH_SFQ_ATTR_LIMIT; +} + +/** + * Get limit of SFQ qdisc. + * @arg qdisc SFQ qdisc. + * @return Limit or a negative error code. + */ +int rtnl_sfq_get_limit(struct rtnl_qdisc *qdisc) +{ + struct rtnl_sfq *sfq; + + if (!(sfq = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (sfq->qs_mask & SCH_SFQ_ATTR_LIMIT) + return sfq->qs_limit; + else + return -NLE_NOATTR; +} + +/** + * Set perturbation interval of SFQ qdisc. + * @arg qdisc SFQ qdisc to be modified. + * @arg perturb New perturbation interval in seconds. + * @note A value of 0 disables perturbation altogether. + * @return 0 on success or a negative error code. + */ +void rtnl_sfq_set_perturb(struct rtnl_qdisc *qdisc, int perturb) +{ + struct rtnl_sfq *sfq; + + if (!(sfq = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + sfq->qs_perturb = perturb; + sfq->qs_mask |= SCH_SFQ_ATTR_PERTURB; +} + +/** + * Get perturbation interval of SFQ qdisc. + * @arg qdisc SFQ qdisc. + * @return Perturbation interval in seconds or a negative error code. + */ +int rtnl_sfq_get_perturb(struct rtnl_qdisc *qdisc) +{ + struct rtnl_sfq *sfq; + + if (!(sfq = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (sfq->qs_mask & SCH_SFQ_ATTR_PERTURB) + return sfq->qs_perturb; + else + return -NLE_NOATTR; +} + +/** + * Get divisor of SFQ qdisc. + * @arg qdisc SFQ qdisc. + * @return Divisor in number of entries or a negative error code. + */ +int rtnl_sfq_get_divisor(struct rtnl_qdisc *qdisc) +{ + struct rtnl_sfq *sfq; + + if (!(sfq = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (sfq->qs_mask & SCH_SFQ_ATTR_DIVISOR) + return sfq->qs_divisor; + else + return -NLE_NOATTR; +} + +/** @} */ + +static struct rtnl_tc_ops sfq_ops = { + .to_kind = "sfq", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_sfq), + .to_msg_parser = sfq_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = sfq_dump_line, + [NL_DUMP_DETAILS] = sfq_dump_details, + }, + .to_msg_fill = sfq_msg_fill, +}; + +static void _nl_init sfq_init(void) +{ + rtnl_tc_register(&sfq_ops); +} + +static void _nl_exit sfq_exit(void) +{ + rtnl_tc_unregister(&sfq_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/tbf.c b/libnl/lib/route/qdisc/tbf.c new file mode 100644 index 0000000..a054a14 --- /dev/null +++ b/libnl/lib/route/qdisc/tbf.c @@ -0,0 +1,466 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_tbf Token Bucket Filter (TBF) + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "tc-api.h" + +/** @cond SKIP */ +struct rtnl_tbf { + uint32_t qt_limit; + struct rtnl_ratespec qt_rate; + uint32_t qt_rate_bucket; + uint32_t qt_rate_txtime; + struct rtnl_ratespec qt_peakrate; + uint32_t qt_peakrate_bucket; + uint32_t qt_peakrate_txtime; + uint32_t qt_mask; +}; + +#define TBF_ATTR_LIMIT 0x01 +#define TBF_ATTR_RATE 0x02 +#define TBF_ATTR_PEAKRATE 0x10 +/** @endcond */ + +static struct nla_policy tbf_policy[TCA_TBF_MAX+1] = { + [TCA_TBF_PARMS] = { .minlen = sizeof(struct tc_tbf_qopt) }, +}; + +static int tbf_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct nlattr *tb[TCA_TBF_MAX + 1]; + struct rtnl_tbf *tbf = data; + int err; + + if ((err = tca_parse(tb, TCA_TBF_MAX, tc, tbf_policy)) < 0) + return err; + + if (tb[TCA_TBF_PARMS]) { + struct tc_tbf_qopt opts; + int bufsize; + + nla_memcpy(&opts, tb[TCA_TBF_PARMS], sizeof(opts)); + tbf->qt_limit = opts.limit; + + rtnl_copy_ratespec(&tbf->qt_rate, &opts.rate); + tbf->qt_rate_txtime = opts.buffer; + bufsize = rtnl_tc_calc_bufsize64(nl_ticks2us(opts.buffer), + tbf->qt_rate.rs_rate64); + tbf->qt_rate_bucket = bufsize; + + rtnl_copy_ratespec(&tbf->qt_peakrate, &opts.peakrate); + tbf->qt_peakrate_txtime = opts.mtu; + bufsize = rtnl_tc_calc_bufsize64(nl_ticks2us(opts.mtu), + tbf->qt_peakrate.rs_rate64); + tbf->qt_peakrate_bucket = bufsize; + + rtnl_tc_set_mpu(tc, tbf->qt_rate.rs_mpu); + rtnl_tc_set_overhead(tc, tbf->qt_rate.rs_overhead); + + tbf->qt_mask = (TBF_ATTR_LIMIT | TBF_ATTR_RATE | TBF_ATTR_PEAKRATE); + } + + return 0; +} + +static void tbf_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + double r, rbit, lim; + char *ru, *rubit, *limu; + struct rtnl_tbf *tbf = data; + + if (!tbf) + return; + + r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate64, &ru); + rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate64*8, &rubit); + lim = nl_cancel_down_bytes(tbf->qt_limit, &limu); + + nl_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s", + r, ru, rbit, rubit, lim, limu); +} + +static void tbf_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_tbf *tbf = data; + + if (!tbf) + return; + + if (1) { + char *bu, *cu; + double bs = nl_cancel_down_bytes(tbf->qt_rate_bucket, &bu); + double cl = nl_cancel_down_bytes(1 << tbf->qt_rate.rs_cell_log, + &cu); + + nl_dump(p, "rate-bucket-size %1.f%s " + "rate-cell-size %.1f%s\n", + bs, bu, cl, cu); + + } + + if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { + char *pru, *prbu, *bsu, *clu; + double pr, prb, bs, cl; + + pr = nl_cancel_down_bytes(tbf->qt_peakrate.rs_rate64, &pru); + prb = nl_cancel_down_bits(tbf->qt_peakrate.rs_rate64 * 8, &prbu); + bs = nl_cancel_down_bits(tbf->qt_peakrate_bucket, &bsu); + cl = nl_cancel_down_bits(1 << tbf->qt_peakrate.rs_cell_log, + &clu); + + nl_dump_line(p, + " peak-rate %.2f%s/s (%.0f%s) " + "bucket-size %.1f%s cell-size %.1f%s", + pr, pru, prb, prbu, bs, bsu, cl, clu); + } +} + +static int tbf_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + uint32_t rtab[RTNL_TC_RTABLE_SIZE], ptab[RTNL_TC_RTABLE_SIZE]; + struct tc_tbf_qopt opts; + struct rtnl_tbf *tbf = data; + const uint32_t REQUIRED = TBF_ATTR_RATE | TBF_ATTR_LIMIT; + + if ((tbf->qt_mask & REQUIRED) != REQUIRED) + return -NLE_MISSING_ATTR; + + memset(&opts, 0, sizeof(opts)); + opts.limit = tbf->qt_limit; + opts.buffer = tbf->qt_rate_txtime; + + rtnl_tc_build_rate_table(tc, &tbf->qt_rate, rtab); + rtnl_rcopy_ratespec(&opts.rate, &tbf->qt_rate); + + if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { + opts.mtu = tbf->qt_peakrate_txtime; + rtnl_tc_build_rate_table(tc, &tbf->qt_peakrate, ptab); + rtnl_rcopy_ratespec(&opts.peakrate, &tbf->qt_peakrate); + + } + + NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opts), &opts); + NLA_PUT(msg, TCA_TBF_RTAB, sizeof(rtab), rtab); + + if (tbf->qt_mask & TBF_ATTR_PEAKRATE) + NLA_PUT(msg, TCA_TBF_PTAB, sizeof(ptab), ptab); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +/** + * @name Attribute Access + * @{ + */ + +/** + * Set limit of TBF qdisc. + * @arg qdisc TBF qdisc to be modified. + * @arg limit New limit in bytes. + * @return 0 on success or a negative error code. + */ +void rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_tbf *tbf; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + tbf->qt_limit = limit; + tbf->qt_mask |= TBF_ATTR_LIMIT; +} + +static inline double calc_limit(struct rtnl_ratespec *spec, int latency, + int bucket) +{ + double limit; + + limit = (double) spec->rs_rate64 * ((double) latency / 1000000.); + limit += bucket; + + return limit; +} + +/** + * Set limit of TBF qdisc by latency. + * @arg qdisc TBF qdisc to be modified. + * @arg latency Latency in micro seconds. + * + * Calculates and sets the limit based on the desired latency and the + * configured rate and peak rate. In order for this operation to succeed, + * the rate and if required the peak rate must have been set in advance. + * + * @f[ + * limit_n = \frac{{rate_n} \times {latency}}{10^6}+{bucketsize}_n + * @f] + * @f[ + * limit = min(limit_{rate},limit_{peak}) + * @f] + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency) +{ + struct rtnl_tbf *tbf; + double limit, limit2; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (!(tbf->qt_mask & TBF_ATTR_RATE)) + return -NLE_MISSING_ATTR; + + limit = calc_limit(&tbf->qt_rate, latency, tbf->qt_rate_bucket); + + if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { + limit2 = calc_limit(&tbf->qt_peakrate, latency, + tbf->qt_peakrate_bucket); + + if (limit2 < limit) + limit = limit2; + } + + rtnl_qdisc_tbf_set_limit(qdisc, (int) limit); + + return 0; +} + +/** + * Get limit of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Limit in bytes or a negative error code. + */ +int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (tbf->qt_mask & TBF_ATTR_LIMIT) + return tbf->qt_limit; + else + return -NLE_NOATTR; +} + +static inline int calc_cell_log(int cell, int bucket) +{ + cell = rtnl_tc_calc_cell_log(cell); + return cell; +} + +/** + * Set rate of TBF qdisc. + * @arg qdisc TBF qdisc to be modified. + * @arg rate New rate in bytes per second. + * @arg bucket Size of bucket in bytes. + * @arg cell Size of a rate cell or 0 to get default value. + * @return 0 on success or a negative error code. + */ +void rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket, + int cell) +{ + struct rtnl_tbf *tbf; + int cell_log; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (!cell) + cell_log = UINT8_MAX; + else + cell_log = rtnl_tc_calc_cell_log(cell); + + tbf->qt_rate.rs_rate64 = (uint32_t)rate; + tbf->qt_rate_bucket = bucket; + tbf->qt_rate.rs_cell_log = cell_log; + tbf->qt_rate_txtime = nl_us2ticks(rtnl_tc_calc_txtime64(bucket, tbf->qt_rate.rs_rate64)); + tbf->qt_mask |= TBF_ATTR_RATE; +} + +/** + * Get rate of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Rate in bytes per seconds or a negative error code. + */ +int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (tbf->qt_mask & TBF_ATTR_RATE) + return tbf->qt_rate.rs_rate64; + else + return -1; +} + +/** + * Get rate bucket size of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Size of rate bucket or a negative error code. + */ +int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (tbf->qt_mask & TBF_ATTR_RATE) + return tbf->qt_rate_bucket; + else + return -1; +} + +/** + * Get rate cell size of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Size of rate cell in bytes or a negative error code. + */ +int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (tbf->qt_mask & TBF_ATTR_RATE) + return (1 << tbf->qt_rate.rs_cell_log); + else + return -1; +} + +/** + * Set peak rate of TBF qdisc. + * @arg qdisc TBF qdisc to be modified. + * @arg rate New peak rate in bytes per second. + * @arg bucket Size of peakrate bucket. + * @arg cell Size of a peakrate cell or 0 to get default value. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket, + int cell) +{ + struct rtnl_tbf *tbf; + int cell_log; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + cell_log = calc_cell_log(cell, bucket); + if (cell_log < 0) + return cell_log; + + tbf->qt_peakrate.rs_rate64 = (uint32_t)rate; + tbf->qt_peakrate_bucket = bucket; + tbf->qt_peakrate.rs_cell_log = cell_log; + tbf->qt_peakrate_txtime = nl_us2ticks(rtnl_tc_calc_txtime64(bucket, tbf->qt_peakrate.rs_rate64)); + + tbf->qt_mask |= TBF_ATTR_PEAKRATE; + + return 0; +} + +/** + * Get peak rate of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Peak rate in bytes per seconds or a negative error code. + */ +int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (tbf->qt_mask & TBF_ATTR_PEAKRATE) + return tbf->qt_peakrate.rs_rate64; + else + return -1; +} + +/** + * Get peak rate bucket size of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Size of peak rate bucket or a negative error code. + */ +int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (tbf->qt_mask & TBF_ATTR_PEAKRATE) + return tbf->qt_peakrate_bucket; + else + return -1; +} + +/** + * Get peak rate cell size of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Size of peak rate cell in bytes or a negative error code. + */ +int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (tbf->qt_mask & TBF_ATTR_PEAKRATE) + return (1 << tbf->qt_peakrate.rs_cell_log); + else + return -1; +} + +/** @} */ + +static struct rtnl_tc_ops tbf_tc_ops = { + .to_kind = "tbf", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_tbf), + .to_msg_parser = tbf_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = tbf_dump_line, + [NL_DUMP_DETAILS] = tbf_dump_details, + }, + .to_msg_fill = tbf_msg_fill, +}; + +static void _nl_init tbf_init(void) +{ + rtnl_tc_register(&tbf_tc_ops); +} + +static void _nl_exit tbf_exit(void) +{ + rtnl_tc_unregister(&tbf_tc_ops); +} + +/** @} */ diff --git a/libnl/lib/route/route.c b/libnl/lib/route/route.c new file mode 100644 index 0000000..0a16ff4 --- /dev/null +++ b/libnl/lib/route/route.c @@ -0,0 +1,233 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup route Routing + * @brief + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" +#include "nl-aux-route/nl-route.h" + +static struct nl_cache_ops rtnl_route_ops; + +static int route_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct rtnl_route *route; + int err; + + if ((err = rtnl_route_parse(nlh, &route)) < 0) + return err; + + err = pp->pp_cb((struct nl_object *) route, pp); + + rtnl_route_put(route); + return err; +} + +static int route_request_update(struct nl_cache *c, struct nl_sock *h) +{ + struct rtmsg rhdr = { + .rtm_family = c->c_iarg1, + }; + + if (c->c_iarg2 & ROUTE_CACHE_CONTENT) + rhdr.rtm_flags |= RTM_F_CLONED; + + return nl_send_simple(h, RTM_GETROUTE, NLM_F_DUMP, &rhdr, sizeof(rhdr)); +} + +/** + * @name Cache Management + * @{ + */ + +/** + * Build a route cache holding all routes currently configured in the kernel + * @arg sk Netlink socket. + * @arg family Address family of routes to cover or AF_UNSPEC + * @arg flags Flags + * @arg result Result pointer + * + * Allocates a new cache, initializes it properly and updates it to + * contain all routes currently configured in the kernel. + * + * Valid flags: + * * ROUTE_CACHE_CONTENT - Cache will contain contents of routing cache + * instead of actual routes. + * + * @note The caller is responsible for destroying and freeing the + * cache after using it. + * @return 0 on success or a negative error code. + */ +int rtnl_route_alloc_cache(struct nl_sock *sk, int family, int flags, + struct nl_cache **result) +{ + struct nl_cache *cache; + int err; + + if (!(cache = nl_cache_alloc(&rtnl_route_ops))) + return -NLE_NOMEM; + + cache->c_iarg1 = family; + cache->c_iarg2 = flags; + + if (sk && (err = nl_cache_refill(sk, cache)) < 0) { + free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** @} */ + +/** + * @name Route Addition + * @{ + */ + +static int build_route_msg(struct rtnl_route *tmpl, int cmd, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + int err; + + if (!(msg = nlmsg_alloc_simple(cmd, flags))) + return -NLE_NOMEM; + + if ((err = rtnl_route_build_msg(msg, tmpl)) < 0) { + nlmsg_free(msg); + return err; + } + + *result = msg; + return 0; +} + +int rtnl_route_build_add_request(struct rtnl_route *tmpl, int flags, + struct nl_msg **result) +{ + return build_route_msg(tmpl, RTM_NEWROUTE, NLM_F_CREATE | flags, + result); +} + +int rtnl_route_lookup(struct nl_sock *sk, struct nl_addr *dst, + struct rtnl_route **result) +{ + _nl_auto_nl_msg struct nl_msg *msg = NULL; + _nl_auto_rtnl_route struct rtnl_route *tmpl = NULL; + struct nl_object *obj; + int err; + + tmpl = rtnl_route_alloc(); + rtnl_route_set_dst(tmpl, dst); + err = build_route_msg(tmpl, RTM_GETROUTE, 0, &msg); + if (err < 0) + return err; + + err = nl_send_auto(sk, msg); + if (err < 0) + return err; + + if ((err = nl_pickup(sk, route_msg_parser, &obj)) < 0) + return err; + + *result = (struct rtnl_route *)obj; + wait_for_ack(sk); + return 0; +} + +int rtnl_route_add(struct nl_sock *sk, struct rtnl_route *route, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_route_build_add_request(route, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +int rtnl_route_build_del_request(struct rtnl_route *tmpl, int flags, + struct nl_msg **result) +{ + return build_route_msg(tmpl, RTM_DELROUTE, flags, result); +} + +int rtnl_route_delete(struct nl_sock *sk, struct rtnl_route *route, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_route_build_del_request(route, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** @} */ + +static struct nl_af_group route_groups[] = { + { AF_INET, RTNLGRP_IPV4_ROUTE }, + { AF_INET6, RTNLGRP_IPV6_ROUTE }, + { AF_MPLS, RTNLGRP_MPLS_ROUTE }, + { AF_DECnet, RTNLGRP_DECnet_ROUTE }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops rtnl_route_ops = { + .co_name = "route/route", + .co_hdrsize = sizeof(struct rtmsg), + .co_msgtypes = { + { RTM_NEWROUTE, NL_ACT_NEW, "new" }, + { RTM_DELROUTE, NL_ACT_DEL, "del" }, + { RTM_GETROUTE, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = route_groups, + .co_request_update = route_request_update, + .co_msg_parser = route_msg_parser, + .co_obj_ops = &route_obj_ops, +}; + +static void _nl_init route_init(void) +{ + nl_cache_mngt_register(&rtnl_route_ops); +} + +static void _nl_exit route_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_route_ops); +} + +/** @} */ diff --git a/libnl/lib/route/route_obj.c b/libnl/lib/route/route_obj.c new file mode 100644 index 0000000..094ae53 --- /dev/null +++ b/libnl/lib/route/route_obj.c @@ -0,0 +1,1567 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +/** + * @ingroup route + * @defgroup route_obj Route Object + * + * @par Attributes + * @code + * Name Default + * ------------------------------------------------------------- + * routing table RT_TABLE_MAIN + * scope RT_SCOPE_NOWHERE + * tos 0 + * protocol RTPROT_STATIC + * prio 0 + * family AF_UNSPEC + * type RTN_UNICAST + * iif NULL + * @endcode + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nl-route.h" +#include "nl-aux-route/nl-route.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nexthop-encap.h" + +/** @cond SKIP */ +struct rtnl_route { + NLHDR_COMMON + + uint8_t rt_family; + uint8_t rt_tos; + uint8_t rt_protocol; + uint8_t rt_scope; + uint8_t rt_type; + uint8_t rt_nmetrics; + uint8_t rt_ttl_propagate; + uint32_t rt_flags; + struct nl_addr *rt_dst; + struct nl_addr *rt_src; + uint32_t rt_table; + uint32_t rt_iif; + uint32_t rt_prio; + uint32_t rt_metrics[RTAX_MAX]; + uint32_t rt_metrics_mask; + uint32_t rt_nr_nh; + uint32_t rt_nhid; + struct nl_addr *rt_pref_src; + struct nl_list_head rt_nexthops; + struct rtnl_rtcacheinfo rt_cacheinfo; + uint32_t rt_flag_mask; +}; + +#define ROUTE_ATTR_FAMILY 0x000001 +#define ROUTE_ATTR_TOS 0x000002 +#define ROUTE_ATTR_TABLE 0x000004 +#define ROUTE_ATTR_PROTOCOL 0x000008 +#define ROUTE_ATTR_SCOPE 0x000010 +#define ROUTE_ATTR_TYPE 0x000020 +#define ROUTE_ATTR_FLAGS 0x000040 +#define ROUTE_ATTR_DST 0x000080 +#define ROUTE_ATTR_SRC 0x000100 +#define ROUTE_ATTR_IIF 0x000200 +#define ROUTE_ATTR_OIF 0x000400 +#define ROUTE_ATTR_GATEWAY 0x000800 +#define ROUTE_ATTR_PRIO 0x001000 +#define ROUTE_ATTR_PREF_SRC 0x002000 +#define ROUTE_ATTR_METRICS 0x004000 +#define ROUTE_ATTR_MULTIPATH 0x008000 +#define ROUTE_ATTR_REALMS 0x010000 +#define ROUTE_ATTR_CACHEINFO 0x020000 +#define ROUTE_ATTR_TTL_PROPAGATE 0x040000 +#define ROUTE_ATTR_NHID 0x080000 +/** @endcond */ + +static void route_constructor(struct nl_object *c) +{ + struct rtnl_route *r = (struct rtnl_route *) c; + + r->rt_family = AF_UNSPEC; + r->rt_scope = RT_SCOPE_NOWHERE; + r->rt_table = RT_TABLE_MAIN; + r->rt_protocol = RTPROT_STATIC; + r->rt_type = RTN_UNICAST; + r->rt_prio = 0; + + nl_init_list_head(&r->rt_nexthops); +} + +static void route_free_data(struct nl_object *c) +{ + struct rtnl_route *r = (struct rtnl_route *) c; + struct rtnl_nexthop *nh, *tmp; + + if (r == NULL) + return; + + nl_addr_put(r->rt_dst); + nl_addr_put(r->rt_src); + nl_addr_put(r->rt_pref_src); + + nl_list_for_each_entry_safe(nh, tmp, &r->rt_nexthops, rtnh_list) { + rtnl_route_remove_nexthop(r, nh); + rtnl_route_nh_free(nh); + } +} + +static int route_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_route *dst = (struct rtnl_route *) _dst; + struct rtnl_route *src = (struct rtnl_route *) _src; + struct rtnl_nexthop *nh, *new; + + dst->rt_dst = NULL; + dst->rt_src = NULL; + dst->rt_pref_src = NULL; + nl_init_list_head(&dst->rt_nexthops); + dst->rt_nr_nh = 0; + + if (src->rt_dst) { + if (!(dst->rt_dst = nl_addr_clone(src->rt_dst))) + return -NLE_NOMEM; + } + + if (src->rt_src) { + if (!(dst->rt_src = nl_addr_clone(src->rt_src))) + return -NLE_NOMEM; + } + + if (src->rt_pref_src) { + if (!(dst->rt_pref_src = nl_addr_clone(src->rt_pref_src))) + return -NLE_NOMEM; + } + + nl_list_for_each_entry(nh, &src->rt_nexthops, rtnh_list) { + new = rtnl_route_nh_clone(nh); + if (!new) + return -NLE_NOMEM; + + rtnl_route_add_nexthop(dst, new); + } + + return 0; +} + +static void route_dump_line(struct nl_object *a, struct nl_dump_params *p) +{ + struct rtnl_route *r = (struct rtnl_route *) a; + int cache = 0, flags; + char buf[64]; + + if (r->rt_flags & RTM_F_CLONED) + cache = 1; + + nl_dump_line(p, "%s ", nl_af2str(r->rt_family, buf, sizeof(buf))); + + if (cache) + nl_dump(p, "cache "); + + if (!(r->ce_mask & ROUTE_ATTR_DST) || + (nl_addr_get_prefixlen(r->rt_dst) == 0 && + nl_addr_get_len(r->rt_dst) > 0 && nl_addr_iszero(r->rt_dst))) + nl_dump(p, "default "); + else + nl_dump(p, "%s ", nl_addr2str(r->rt_dst, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_TABLE && !cache) + nl_dump(p, "table %s ", + rtnl_route_table2str(r->rt_table, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_TYPE) + nl_dump(p, "type %s ", + nl_rtntype2str(r->rt_type, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_TOS && r->rt_tos != 0) + nl_dump(p, "tos %#x ", r->rt_tos); + + if (r->ce_mask & ROUTE_ATTR_NHID) + nl_dump(p, "nhid %u ", r->rt_nhid); + + if (r->ce_mask & ROUTE_ATTR_MULTIPATH) { + struct rtnl_nexthop *nh; + + nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) { + p->dp_ivar = NH_DUMP_FROM_ONELINE; + rtnl_route_nh_dump(nh, p); + } + } + + flags = r->rt_flags & ~(RTM_F_CLONED); + if (r->ce_mask & ROUTE_ATTR_FLAGS && flags) { + + nl_dump(p, "<"); + +#define PRINT_FLAG(f) if (flags & RTNH_F_##f) { \ + flags &= ~RTNH_F_##f; nl_dump(p, #f "%s", flags ? "," : ""); } + PRINT_FLAG(DEAD); + PRINT_FLAG(ONLINK); + PRINT_FLAG(PERVASIVE); +#undef PRINT_FLAG + +#define PRINT_FLAG(f) if (flags & RTM_F_##f) { \ + flags &= ~RTM_F_##f; nl_dump(p, #f "%s", flags ? "," : ""); } + PRINT_FLAG(NOTIFY); + PRINT_FLAG(EQUALIZE); + PRINT_FLAG(PREFIX); +#undef PRINT_FLAG + +#define PRINT_FLAG(f) if (flags & RTCF_##f) { \ + flags &= ~RTCF_##f; nl_dump(p, #f "%s", flags ? "," : ""); } + PRINT_FLAG(NOTIFY); + PRINT_FLAG(REDIRECTED); + PRINT_FLAG(DOREDIRECT); + PRINT_FLAG(DIRECTSRC); + PRINT_FLAG(DNAT); + PRINT_FLAG(BROADCAST); + PRINT_FLAG(MULTICAST); + PRINT_FLAG(LOCAL); +#undef PRINT_FLAG + + nl_dump(p, ">"); + } + + nl_dump(p, "\n"); +} + +static void route_dump_details(struct nl_object *a, struct nl_dump_params *p) +{ + _nl_auto_nl_cache struct nl_cache *link_cache = NULL; + struct rtnl_route *r = (struct rtnl_route *) a; + char buf[256]; + int i; + + link_cache = nl_cache_mngt_require_safe("route/link"); + + route_dump_line(a, p); + nl_dump_line(p, " "); + + if (r->ce_mask & ROUTE_ATTR_PREF_SRC) + nl_dump(p, "preferred-src %s ", + nl_addr2str(r->rt_pref_src, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_SCOPE && r->rt_scope != RT_SCOPE_NOWHERE) + nl_dump(p, "scope %s ", + rtnl_scope2str(r->rt_scope, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_PRIO) + nl_dump(p, "priority %#x ", r->rt_prio); + + if (r->ce_mask & ROUTE_ATTR_PROTOCOL) + nl_dump(p, "protocol %s ", + rtnl_route_proto2str(r->rt_protocol, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_IIF) { + if (link_cache) { + nl_dump(p, "iif %s ", + rtnl_link_i2name(link_cache, r->rt_iif, + buf, sizeof(buf))); + } else + nl_dump(p, "iif %d ", r->rt_iif); + } + + if (r->ce_mask & ROUTE_ATTR_SRC) + nl_dump(p, "src %s ", nl_addr2str(r->rt_src, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_TTL_PROPAGATE) { + nl_dump(p, " ttl-propagate %s", + r->rt_ttl_propagate ? "enabled" : "disabled"); + } + + if (r->ce_mask & ROUTE_ATTR_NHID) + nl_dump(p, "nhid %u ", r->rt_nhid); + + nl_dump(p, "\n"); + + if (r->ce_mask & ROUTE_ATTR_MULTIPATH) { + struct rtnl_nexthop *nh; + + nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) { + nl_dump_line(p, " "); + p->dp_ivar = NH_DUMP_FROM_DETAILS; + rtnl_route_nh_dump(nh, p); + nl_dump(p, "\n"); + } + } + + if ((r->ce_mask & ROUTE_ATTR_CACHEINFO) && r->rt_cacheinfo.rtci_error) { + nl_dump_line(p, " cacheinfo error %d (%s)\n", + r->rt_cacheinfo.rtci_error, + nl_strerror_l(-r->rt_cacheinfo.rtci_error)); + } + + if (r->ce_mask & ROUTE_ATTR_METRICS) { + nl_dump_line(p, " metrics ["); + for (i = 0; i < RTAX_MAX; i++) + if (r->rt_metrics_mask & (1 << i)) + nl_dump(p, "%s %u ", + rtnl_route_metric2str(i+1, + buf, sizeof(buf)), + r->rt_metrics[i]); + nl_dump(p, "]\n"); + } +} + +static void route_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_route *route = (struct rtnl_route *) obj; + + route_dump_details(obj, p); + + if (route->ce_mask & ROUTE_ATTR_CACHEINFO) { + struct rtnl_rtcacheinfo *ci = &route->rt_cacheinfo; + + nl_dump_line(p, " used %u refcnt %u last-use %us " + "expires %us\n", + ci->rtci_used, ci->rtci_clntref, + ci->rtci_last_use / nl_get_user_hz(), + ci->rtci_expires / nl_get_user_hz()); + } +} + +static void route_keygen(struct nl_object *obj, uint32_t *hashkey, + uint32_t table_sz) +{ + struct rtnl_route *route = (struct rtnl_route *) obj; + unsigned int rkey_sz; + struct nl_addr *addr = NULL; + _nl_auto_free struct route_hash_key { + uint8_t rt_family; + uint8_t rt_tos; + uint32_t rt_table; + uint32_t rt_prio; + char rt_addr[0]; + } _nl_packed *rkey = NULL; + char buf[INET6_ADDRSTRLEN+5]; + + if (route->rt_dst) + addr = route->rt_dst; + + rkey_sz = sizeof(*rkey); + if (addr) + rkey_sz += nl_addr_get_len(addr); + rkey = calloc(1, rkey_sz); + if (!rkey) { + NL_DBG(2, "Warning: calloc failed for %d bytes...\n", rkey_sz); + *hashkey = 0; + return; + } + rkey->rt_family = route->rt_family; + rkey->rt_tos = route->rt_tos; + rkey->rt_table = route->rt_table; + rkey->rt_prio = route->rt_prio; + if (addr) + memcpy(rkey->rt_addr, nl_addr_get_binary_addr(addr), + nl_addr_get_len(addr)); + + *hashkey = nl_hash(rkey, rkey_sz, 0) % table_sz; + + NL_DBG(5, + "route %p key (fam %d tos %d table %d prio %d addr %s) keysz %d hash 0x%x\n", + route, rkey->rt_family, rkey->rt_tos, rkey->rt_table, + rkey->rt_prio, nl_addr2str(addr, buf, sizeof(buf)), rkey_sz, + *hashkey); + + return; +} + +static uint32_t route_id_attrs_get(struct nl_object *obj) +{ + struct rtnl_route *route = (struct rtnl_route *)obj; + struct nl_object_ops *ops = obj->ce_ops; + uint32_t rv = ops->oo_id_attrs; + + /* MPLS address family does not allow RTA_PRIORITY to be set */ + if (route->rt_family == AF_MPLS) + rv &= ~ROUTE_ATTR_PRIO; + + return rv; +} + +static uint64_t route_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct rtnl_route *a = (struct rtnl_route *) _a; + struct rtnl_route *b = (struct rtnl_route *) _b; + struct rtnl_nexthop *nh_a, *nh_b; + int i, found; + uint64_t diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(ROUTE_ATTR_FAMILY, a->rt_family != b->rt_family); + diff |= _DIFF(ROUTE_ATTR_TOS, a->rt_tos != b->rt_tos); + diff |= _DIFF(ROUTE_ATTR_TABLE, a->rt_table != b->rt_table); + diff |= _DIFF(ROUTE_ATTR_PROTOCOL, a->rt_protocol != b->rt_protocol); + diff |= _DIFF(ROUTE_ATTR_SCOPE, a->rt_scope != b->rt_scope); + diff |= _DIFF(ROUTE_ATTR_TYPE, a->rt_type != b->rt_type); + diff |= _DIFF(ROUTE_ATTR_PRIO, a->rt_prio != b->rt_prio); + diff |= _DIFF(ROUTE_ATTR_DST, nl_addr_cmp(a->rt_dst, b->rt_dst)); + diff |= _DIFF(ROUTE_ATTR_SRC, nl_addr_cmp(a->rt_src, b->rt_src)); + diff |= _DIFF(ROUTE_ATTR_IIF, a->rt_iif != b->rt_iif); + diff |= _DIFF(ROUTE_ATTR_PREF_SRC, + nl_addr_cmp(a->rt_pref_src, b->rt_pref_src)); + diff |= _DIFF(ROUTE_ATTR_TTL_PROPAGATE, + a->rt_ttl_propagate != b->rt_ttl_propagate); + diff |= _DIFF(ROUTE_ATTR_NHID, a->rt_nhid != b->rt_nhid); + + if (flags & LOOSE_COMPARISON) { + nl_list_for_each_entry(nh_b, &b->rt_nexthops, rtnh_list) { + found = 0; + nl_list_for_each_entry(nh_a, &a->rt_nexthops, + rtnh_list) { + if (!rtnl_route_nh_compare(nh_a, nh_b, + nh_b->ce_mask, 1)) { + found = 1; + break; + } + } + + if (!found) + goto nh_mismatch; + } + + for (i = 0; i < RTAX_MAX - 1; i++) { + if (a->rt_metrics_mask & (1 << i) && + (!(b->rt_metrics_mask & (1 << i)) || + a->rt_metrics[i] != b->rt_metrics[i])) + diff |= _DIFF(ROUTE_ATTR_METRICS, 1); + } + + diff |= _DIFF(ROUTE_ATTR_FLAGS, + (a->rt_flags ^ b->rt_flags) & b->rt_flag_mask); + } else { + if (a->rt_nr_nh != b->rt_nr_nh) + goto nh_mismatch; + + /* search for a dup in each nh of a */ + nl_list_for_each_entry(nh_a, &a->rt_nexthops, rtnh_list) { + found = 0; + nl_list_for_each_entry(nh_b, &b->rt_nexthops, + rtnh_list) { + if (rtnl_route_nh_identical(nh_a, nh_b)) { + found = 1; + break; + } + } + if (!found) + goto nh_mismatch; + } + + /* search for a dup in each nh of b, covers case where a has + * dupes itself */ + nl_list_for_each_entry(nh_b, &b->rt_nexthops, rtnh_list) { + found = 0; + nl_list_for_each_entry(nh_a, &a->rt_nexthops, + rtnh_list) { + if (rtnl_route_nh_identical(nh_a, nh_b)) { + found = 1; + break; + } + } + if (!found) + goto nh_mismatch; + } + + for (i = 0; i < RTAX_MAX - 1; i++) { + if ((a->rt_metrics_mask & (1 << i)) ^ + (b->rt_metrics_mask & (1 << i))) + diff |= _DIFF(ROUTE_ATTR_METRICS, 1); + else + diff |= _DIFF(ROUTE_ATTR_METRICS, + a->rt_metrics[i] != b->rt_metrics[i]); + } + + diff |= _DIFF(ROUTE_ATTR_FLAGS, a->rt_flags != b->rt_flags); + } + +out: + return diff; + +nh_mismatch: + diff |= _DIFF(ROUTE_ATTR_MULTIPATH, 1); + goto out; +#undef _DIFF +} + +static int route_update(struct nl_object *old_obj, struct nl_object *new_obj) +{ + struct rtnl_route *new_route = (struct rtnl_route *) new_obj; + struct rtnl_route *old_route = (struct rtnl_route *) old_obj; + struct rtnl_nexthop *new_nh; + int action = new_obj->ce_msgtype; + char buf[INET6_ADDRSTRLEN+5]; + + /* + * ipv6 ECMP route notifications from the kernel come as + * separate notifications, one for every nexthop. This update + * function collapses such route msgs into a single + * route with multiple nexthops. The resulting object looks + * similar to a ipv4 ECMP route + */ + if (new_route->rt_family != AF_INET6 || + new_route->rt_table == RT_TABLE_LOCAL) + return -NLE_OPNOTSUPP; + + /* + * For routes that are already multipath, + * or dont have a nexthop dont do anything + */ + if (rtnl_route_get_nnexthops(new_route) != 1) + return -NLE_OPNOTSUPP; + + /* + * Get the only nexthop entry from the new route. For + * IPv6 we always get a route with a 0th NH + * filled or nothing at all + */ + new_nh = rtnl_route_nexthop_n(new_route, 0); + if (!new_nh || !rtnl_route_nh_get_gateway(new_nh)) + return -NLE_OPNOTSUPP; + + switch(action) { + case RTM_NEWROUTE : { + struct rtnl_nexthop *cloned_nh; + struct rtnl_nexthop *old_nh; + + /* + * Do not add the nexthop to old route if it was already added before + */ + nl_list_for_each_entry(old_nh, &old_route->rt_nexthops, rtnh_list) { + if (rtnl_route_nh_identical(old_nh, new_nh)) { + return 0; + } + } + + /* + * Add the nexthop to old route + */ + cloned_nh = rtnl_route_nh_clone(new_nh); + if (!cloned_nh) + return -NLE_NOMEM; + rtnl_route_add_nexthop(old_route, cloned_nh); + + NL_DBG(2, "Route obj %p updated. Added " + "nexthop %p via %s\n", old_route, cloned_nh, + nl_addr2str(cloned_nh->rtnh_gateway, buf, + sizeof(buf))); + } + break; + case RTM_DELROUTE : { + struct rtnl_nexthop *old_nh; + + /* + * Only take care of nexthop deletes and not + * route deletes. So, if there is only one nexthop + * quite likely we did not update it. So dont do + * anything and return + */ + if (rtnl_route_get_nnexthops(old_route) <= 1) + return -NLE_OPNOTSUPP; + + /* + * Find the next hop in old route and delete it + */ + nl_list_for_each_entry(old_nh, &old_route->rt_nexthops, + rtnh_list) { + if (rtnl_route_nh_identical(old_nh, new_nh)) { + + rtnl_route_remove_nexthop(old_route, old_nh); + + NL_DBG(2, "Route obj %p updated. Removed " + "nexthop %p via %s\n", old_route, + old_nh, + nl_addr2str(old_nh->rtnh_gateway, buf, + sizeof(buf))); + + rtnl_route_nh_free(old_nh); + break; + } + } + } + break; + default: + NL_DBG(2, "Unknown action associated " + "to object %p during route update\n", new_obj); + return -NLE_OPNOTSUPP; + } + + return NLE_SUCCESS; +} + +static const struct trans_tbl route_attrs[] = { + __ADD(ROUTE_ATTR_FAMILY, family), + __ADD(ROUTE_ATTR_TOS, tos), + __ADD(ROUTE_ATTR_TABLE, table), + __ADD(ROUTE_ATTR_PROTOCOL, protocol), + __ADD(ROUTE_ATTR_SCOPE, scope), + __ADD(ROUTE_ATTR_TYPE, type), + __ADD(ROUTE_ATTR_FLAGS, flags), + __ADD(ROUTE_ATTR_DST, dst), + __ADD(ROUTE_ATTR_SRC, src), + __ADD(ROUTE_ATTR_IIF, iif), + __ADD(ROUTE_ATTR_OIF, oif), + __ADD(ROUTE_ATTR_GATEWAY, gateway), + __ADD(ROUTE_ATTR_PRIO, prio), + __ADD(ROUTE_ATTR_PREF_SRC, pref_src), + __ADD(ROUTE_ATTR_METRICS, metrics), + __ADD(ROUTE_ATTR_MULTIPATH, multipath), + __ADD(ROUTE_ATTR_REALMS, realms), + __ADD(ROUTE_ATTR_CACHEINFO, cacheinfo), + __ADD(ROUTE_ATTR_TTL_PROPAGATE, ttl_propagate), + __ADD(ROUTE_ATTR_NHID, nhid), +}; + +static char *route_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, route_attrs, + ARRAY_SIZE(route_attrs)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_route *rtnl_route_alloc(void) +{ + return (struct rtnl_route *) nl_object_alloc(&route_obj_ops); +} + +void rtnl_route_get(struct rtnl_route *route) +{ + nl_object_get((struct nl_object *) route); +} + +void rtnl_route_put(struct rtnl_route *route) +{ + nl_object_put((struct nl_object *) route); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void rtnl_route_set_table(struct rtnl_route *route, uint32_t table) +{ + route->rt_table = table; + route->ce_mask |= ROUTE_ATTR_TABLE; +} + +uint32_t rtnl_route_get_table(struct rtnl_route *route) +{ + return route->rt_table; +} + +void rtnl_route_set_scope(struct rtnl_route *route, uint8_t scope) +{ + route->rt_scope = scope; + route->ce_mask |= ROUTE_ATTR_SCOPE; +} + +uint8_t rtnl_route_get_scope(struct rtnl_route *route) +{ + return route->rt_scope; +} + +void rtnl_route_set_tos(struct rtnl_route *route, uint8_t tos) +{ + route->rt_tos = tos; + route->ce_mask |= ROUTE_ATTR_TOS; +} + +uint8_t rtnl_route_get_tos(struct rtnl_route *route) +{ + return route->rt_tos; +} + +void rtnl_route_set_protocol(struct rtnl_route *route, uint8_t protocol) +{ + route->rt_protocol = protocol; + route->ce_mask |= ROUTE_ATTR_PROTOCOL; +} + +uint8_t rtnl_route_get_protocol(struct rtnl_route *route) +{ + return route->rt_protocol; +} + +void rtnl_route_set_priority(struct rtnl_route *route, uint32_t prio) +{ + route->rt_prio = prio; + route->ce_mask |= ROUTE_ATTR_PRIO; +} + +uint32_t rtnl_route_get_priority(struct rtnl_route *route) +{ + return route->rt_prio; +} + +int rtnl_route_set_family(struct rtnl_route *route, uint8_t family) +{ + switch(family) { + case AF_INET: + case AF_INET6: + case AF_DECnet: + case AF_MPLS: + route->rt_family = family; + route->ce_mask |= ROUTE_ATTR_FAMILY; + return 0; + } + + return -NLE_AF_NOSUPPORT; +} + +uint8_t rtnl_route_get_family(struct rtnl_route *route) +{ + return route->rt_family; +} + +int rtnl_route_set_dst(struct rtnl_route *route, struct nl_addr *addr) +{ + if (route->ce_mask & ROUTE_ATTR_FAMILY) { + if (addr->a_family != route->rt_family) + return -NLE_AF_MISMATCH; + } else + route->rt_family = addr->a_family; + + if (route->rt_dst) + nl_addr_put(route->rt_dst); + + nl_addr_get(addr); + route->rt_dst = addr; + + route->ce_mask |= (ROUTE_ATTR_DST | ROUTE_ATTR_FAMILY); + + return 0; +} + +struct nl_addr *rtnl_route_get_dst(struct rtnl_route *route) +{ + return route->rt_dst; +} + +int rtnl_route_set_src(struct rtnl_route *route, struct nl_addr *addr) +{ + if (addr->a_family == AF_INET) + return -NLE_SRCRT_NOSUPPORT; + + if (route->ce_mask & ROUTE_ATTR_FAMILY) { + if (addr->a_family != route->rt_family) + return -NLE_AF_MISMATCH; + } else + route->rt_family = addr->a_family; + + if (route->rt_src) + nl_addr_put(route->rt_src); + + nl_addr_get(addr); + route->rt_src = addr; + route->ce_mask |= (ROUTE_ATTR_SRC | ROUTE_ATTR_FAMILY); + + return 0; +} + +struct nl_addr *rtnl_route_get_src(struct rtnl_route *route) +{ + return route->rt_src; +} + +int rtnl_route_set_type(struct rtnl_route *route, uint8_t type) +{ + if (type > RTN_MAX) + return -NLE_RANGE; + + route->rt_type = type; + route->ce_mask |= ROUTE_ATTR_TYPE; + + return 0; +} + +uint8_t rtnl_route_get_type(struct rtnl_route *route) +{ + return route->rt_type; +} + +void rtnl_route_set_flags(struct rtnl_route *route, uint32_t flags) +{ + route->rt_flag_mask |= flags; + route->rt_flags |= flags; + route->ce_mask |= ROUTE_ATTR_FLAGS; +} + +void rtnl_route_unset_flags(struct rtnl_route *route, uint32_t flags) +{ + route->rt_flag_mask |= flags; + route->rt_flags &= ~flags; + route->ce_mask |= ROUTE_ATTR_FLAGS; +} + +uint32_t rtnl_route_get_flags(struct rtnl_route *route) +{ + return route->rt_flags; +} + +int rtnl_route_set_metric(struct rtnl_route *route, int metric, uint32_t value) +{ + if (metric > RTAX_MAX || metric < 1) + return -NLE_RANGE; + + route->rt_metrics[metric - 1] = value; + + if (!(route->rt_metrics_mask & (1 << (metric - 1)))) { + route->rt_nmetrics++; + route->rt_metrics_mask |= (1 << (metric - 1)); + } + + route->ce_mask |= ROUTE_ATTR_METRICS; + + return 0; +} + +int rtnl_route_unset_metric(struct rtnl_route *route, int metric) +{ + if (metric > RTAX_MAX || metric < 1) + return -NLE_RANGE; + + if (route->rt_metrics_mask & (1 << (metric - 1))) { + route->rt_nmetrics--; + route->rt_metrics_mask &= ~(1 << (metric - 1)); + } + + return 0; +} + +int rtnl_route_get_metric(struct rtnl_route *route, int metric, uint32_t *value) +{ + if (metric > RTAX_MAX || metric < 1) + return -NLE_RANGE; + + if (!(route->rt_metrics_mask & (1 << (metric - 1)))) + return -NLE_OBJ_NOTFOUND; + + if (value) + *value = route->rt_metrics[metric - 1]; + + return 0; +} + +int rtnl_route_set_pref_src(struct rtnl_route *route, struct nl_addr *addr) +{ + if (route->ce_mask & ROUTE_ATTR_FAMILY) { + if (addr->a_family != route->rt_family) + return -NLE_AF_MISMATCH; + } else + route->rt_family = addr->a_family; + + if (route->rt_pref_src) + nl_addr_put(route->rt_pref_src); + + nl_addr_get(addr); + route->rt_pref_src = addr; + route->ce_mask |= (ROUTE_ATTR_PREF_SRC | ROUTE_ATTR_FAMILY); + + return 0; +} + +struct nl_addr *rtnl_route_get_pref_src(struct rtnl_route *route) +{ + return route->rt_pref_src; +} + +void rtnl_route_set_iif(struct rtnl_route *route, int ifindex) +{ + route->rt_iif = ifindex; + route->ce_mask |= ROUTE_ATTR_IIF; +} + +int rtnl_route_get_iif(struct rtnl_route *route) +{ + return route->rt_iif; +} + +void rtnl_route_add_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh) +{ + nl_list_add_tail(&nh->rtnh_list, &route->rt_nexthops); + route->rt_nr_nh++; + route->ce_mask |= ROUTE_ATTR_MULTIPATH; +} + +void rtnl_route_remove_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh) +{ + if (route->ce_mask & ROUTE_ATTR_MULTIPATH) { + route->rt_nr_nh--; + nl_list_del(&nh->rtnh_list); + } +} + +struct nl_list_head *rtnl_route_get_nexthops(struct rtnl_route *route) +{ + if (route->ce_mask & ROUTE_ATTR_MULTIPATH) + return &route->rt_nexthops; + + return NULL; +} + +int rtnl_route_get_nnexthops(struct rtnl_route *route) +{ + if (route->ce_mask & ROUTE_ATTR_MULTIPATH) + return route->rt_nr_nh; + + return 0; +} + +void rtnl_route_foreach_nexthop(struct rtnl_route *r, + void (*cb)(struct rtnl_nexthop *, void *), + void *arg) +{ + struct rtnl_nexthop *nh; + + if (r->ce_mask & ROUTE_ATTR_MULTIPATH) { + nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) { + cb(nh, arg); + } + } +} + +struct rtnl_nexthop *rtnl_route_nexthop_n(struct rtnl_route *r, int n) +{ + struct rtnl_nexthop *nh; + + if (r->ce_mask & ROUTE_ATTR_MULTIPATH && n >= 0 && + ((unsigned)n) < r->rt_nr_nh) { + int i; + + i = 0; + nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) { + if (i == n) return nh; + i++; + } + } + return NULL; +} + +void rtnl_route_set_ttl_propagate(struct rtnl_route *route, uint8_t ttl_prop) +{ + route->rt_ttl_propagate = ttl_prop; + route->ce_mask |= ROUTE_ATTR_TTL_PROPAGATE; +} + +int rtnl_route_get_ttl_propagate(struct rtnl_route *route) +{ + if (!route) + return -NLE_INVAL; + if (!(route->ce_mask & ROUTE_ATTR_TTL_PROPAGATE)) + return -NLE_MISSING_ATTR; + return route->rt_ttl_propagate; +} + +void rtnl_route_set_nhid(struct rtnl_route *route, uint32_t nhid) +{ + route->rt_nhid = nhid; + + if (nhid > 0) + route->ce_mask |= ROUTE_ATTR_NHID; + else + route->ce_mask &= ~ROUTE_ATTR_NHID; +} + +uint32_t rtnl_route_get_nhid(struct rtnl_route *route) +{ + return route->rt_nhid; +} + +/** @} */ + +/** + * @name Utilities + * @{ + */ + +/** + * Guess scope of a route object. + * @arg route Route object. + * + * Guesses the scope of a route object, based on the following rules: + * @code + * 1) Local route -> local scope + * 2) At least one nexthop not directly connected -> universe scope + * 3) All others -> link scope + * @endcode + * + * @return Scope value. + */ +int rtnl_route_guess_scope(struct rtnl_route *route) +{ + if (route->rt_type == RTN_LOCAL) + return RT_SCOPE_HOST; + + if (route->rt_family == AF_MPLS) + return RT_SCOPE_UNIVERSE; + + if (!nl_list_empty(&route->rt_nexthops)) { + struct rtnl_nexthop *nh; + + /* + * Use scope uiniverse if there is at least one nexthop which + * is not directly connected + */ + nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) { + if (nh->rtnh_gateway || nh->rtnh_via) + return RT_SCOPE_UNIVERSE; + } + } + + return RT_SCOPE_LINK; +} + +/** @} */ + +static struct nl_addr *rtnl_route_parse_via(struct nlattr *nla) +{ + int alen = nla_len(nla) - offsetof(struct rtvia, rtvia_addr); + struct rtvia *via = nla_data(nla); + + return nl_addr_build(via->rtvia_family, via->rtvia_addr, alen); +} + +static int rtnl_route_put_via(struct nl_msg *msg, struct nl_addr *addr) +{ + unsigned int alen = nl_addr_get_len(addr); + struct nlattr *nla; + struct rtvia *via; + + nla = nla_reserve(msg, RTA_VIA, alen + sizeof(*via)); + if (!nla) + return -EMSGSIZE; + + via = nla_data(nla); + via->rtvia_family = nl_addr_get_family(addr); + memcpy(via->rtvia_addr, nl_addr_get_binary_addr(addr), alen); + + return 0; +} + +static struct nla_policy route_policy[RTA_MAX+1] = { + [RTA_IIF] = { .type = NLA_U32 }, + [RTA_OIF] = { .type = NLA_U32 }, + [RTA_PRIORITY] = { .type = NLA_U32 }, + [RTA_FLOW] = { .type = NLA_U32 }, + [RTA_CACHEINFO] = { .minlen = sizeof(struct rta_cacheinfo) }, + [RTA_METRICS] = { .type = NLA_NESTED }, + [RTA_MULTIPATH] = { .type = NLA_NESTED }, + [RTA_TTL_PROPAGATE] = { .type = NLA_U8 }, + [RTA_ENCAP] = { .type = NLA_NESTED }, + [RTA_ENCAP_TYPE] = { .type = NLA_U16 }, + [RTA_NH_ID] = { .type = NLA_U32 }, +}; + +static int parse_multipath(struct rtnl_route *route, struct nlattr *attr) +{ + struct rtnexthop *rtnh = nla_data(attr); + size_t tlen = nla_len(attr); + int err; + + while (tlen >= sizeof(*rtnh) && tlen >= rtnh->rtnh_len) { + _nl_auto_rtnl_nexthop struct rtnl_nexthop *nh = NULL; + + nh = rtnl_route_nh_alloc(); + if (!nh) + return -NLE_NOMEM; + + rtnl_route_nh_set_weight(nh, rtnh->rtnh_hops); + rtnl_route_nh_set_ifindex(nh, rtnh->rtnh_ifindex); + rtnl_route_nh_set_flags(nh, rtnh->rtnh_flags); + + if (rtnh->rtnh_len > sizeof(*rtnh)) { + struct nlattr *ntb[RTA_MAX + 1]; + + err = nla_parse(ntb, RTA_MAX, (struct nlattr *) + RTNH_DATA(rtnh), + rtnh->rtnh_len - sizeof(*rtnh), + route_policy); + if (err < 0) + return err; + + if (ntb[RTA_GATEWAY]) { + _nl_auto_nl_addr struct nl_addr *addr = NULL; + + addr = nl_addr_alloc_attr(ntb[RTA_GATEWAY], + route->rt_family); + if (!addr) + return -NLE_NOMEM; + + rtnl_route_nh_set_gateway(nh, addr); + } + + if (ntb[RTA_FLOW]) { + uint32_t realms; + + realms = nla_get_u32(ntb[RTA_FLOW]); + rtnl_route_nh_set_realms(nh, realms); + } + + if (ntb[RTA_NEWDST]) { + _nl_auto_nl_addr struct nl_addr *addr = NULL; + + addr = nl_addr_alloc_attr(ntb[RTA_NEWDST], + route->rt_family); + if (!addr) + return -NLE_NOMEM; + + err = rtnl_route_nh_set_newdst(nh, addr); + if (err < 0) + return err; + } + + if (ntb[RTA_VIA]) { + _nl_auto_nl_addr struct nl_addr *addr = NULL; + + addr = rtnl_route_parse_via(ntb[RTA_VIA]); + if (!addr) + return -NLE_NOMEM; + + err = rtnl_route_nh_set_via(nh, addr); + if (err < 0) + return err; + } + + if (ntb[RTA_ENCAP] && ntb[RTA_ENCAP_TYPE]) { + err = nh_encap_parse_msg(ntb[RTA_ENCAP], + ntb[RTA_ENCAP_TYPE], + nh); + if (err < 0) + return err; + } + } + + rtnl_route_add_nexthop(route, _nl_steal_pointer(&nh)); + tlen -= RTNH_ALIGN(rtnh->rtnh_len); + rtnh = RTNH_NEXT(rtnh); + } + + return 0; +} + +int rtnl_route_parse(struct nlmsghdr *nlh, struct rtnl_route **result) +{ + _nl_auto_rtnl_route struct rtnl_route *route = NULL; + _nl_auto_rtnl_nexthop struct rtnl_nexthop *old_nh = NULL; + _nl_auto_nl_addr struct nl_addr *src = NULL; + _nl_auto_nl_addr struct nl_addr *dst = NULL; + struct nlattr *tb[RTA_MAX + 1]; + struct rtmsg *rtm; + int family; + int err; + + route = rtnl_route_alloc(); + if (!route) + return -NLE_NOMEM; + + route->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX, route_policy); + if (err < 0) + return err; + + rtm = nlmsg_data(nlh); + route->rt_family = family = rtm->rtm_family; + route->rt_tos = rtm->rtm_tos; + route->rt_table = rtm->rtm_table; + route->rt_type = rtm->rtm_type; + route->rt_scope = rtm->rtm_scope; + route->rt_protocol = rtm->rtm_protocol; + route->rt_flags = rtm->rtm_flags; + route->rt_prio = 0; + + route->ce_mask |= ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS | + ROUTE_ATTR_TABLE | ROUTE_ATTR_TYPE | + ROUTE_ATTR_SCOPE | ROUTE_ATTR_PROTOCOL | + ROUTE_ATTR_FLAGS; + + /* right now MPLS does not allow rt_prio to be set, so don't + * assume it is unless it comes from an attribute + */ + if (family != AF_MPLS) + route->ce_mask |= ROUTE_ATTR_PRIO; + + if (tb[RTA_DST]) { + if (!(dst = nl_addr_alloc_attr(tb[RTA_DST], family))) + return -NLE_NOMEM; + } else { + int len; + + switch (family) { + case AF_INET: + len = 4; + break; + + case AF_INET6: + len = 16; + break; + default: + len = 0; + break; + } + + if (!(dst = nl_addr_build(family, NULL, len))) + return -NLE_NOMEM; + } + + nl_addr_set_prefixlen(dst, rtm->rtm_dst_len); + err = rtnl_route_set_dst(route, dst); + if (err < 0) + return err; + + if (tb[RTA_SRC]) { + if (!(src = nl_addr_alloc_attr(tb[RTA_SRC], family))) + return -NLE_NOMEM; + } else if (rtm->rtm_src_len) + if (!(src = nl_addr_alloc(0))) + return -NLE_NOMEM; + + if (src) { + nl_addr_set_prefixlen(src, rtm->rtm_src_len); + rtnl_route_set_src(route, src); + } + + if (tb[RTA_TABLE]) + rtnl_route_set_table(route, nla_get_u32(tb[RTA_TABLE])); + + if (tb[RTA_IIF]) + rtnl_route_set_iif(route, nla_get_u32(tb[RTA_IIF])); + + if (tb[RTA_PRIORITY]) + rtnl_route_set_priority(route, nla_get_u32(tb[RTA_PRIORITY])); + + if (tb[RTA_PREFSRC]) { + _nl_auto_nl_addr struct nl_addr *addr = NULL; + + if (!(addr = nl_addr_alloc_attr(tb[RTA_PREFSRC], family))) + return -NLE_NOMEM; + rtnl_route_set_pref_src(route, addr); + } + + if (tb[RTA_METRICS]) { + struct nlattr *mtb[RTAX_MAX + 1]; + int i; + + err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL); + if (err < 0) + return err; + + for (i = 1; i <= RTAX_MAX; i++) { + if (mtb[i] && _nla_len(mtb[i]) >= sizeof(uint32_t)) { + uint32_t m = nla_get_u32(mtb[i]); + + err = rtnl_route_set_metric(route, i, m); + if (err < 0) + return err; + } + } + } + + if (tb[RTA_MULTIPATH]) { + if ((err = parse_multipath(route, tb[RTA_MULTIPATH])) < 0) + return err; + } + + if (tb[RTA_CACHEINFO]) { + nla_memcpy(&route->rt_cacheinfo, tb[RTA_CACHEINFO], + sizeof(route->rt_cacheinfo)); + route->ce_mask |= ROUTE_ATTR_CACHEINFO; + } + + if (tb[RTA_OIF]) { + if (!old_nh && !(old_nh = rtnl_route_nh_alloc())) + return -NLE_NOMEM; + + rtnl_route_nh_set_ifindex(old_nh, nla_get_u32(tb[RTA_OIF])); + } + + if (tb[RTA_GATEWAY]) { + _nl_auto_nl_addr struct nl_addr *addr = NULL; + + if (!old_nh && !(old_nh = rtnl_route_nh_alloc())) + return -NLE_NOMEM; + + if (!(addr = nl_addr_alloc_attr(tb[RTA_GATEWAY], family))) + return -NLE_NOMEM; + + rtnl_route_nh_set_gateway(old_nh, addr); + } + + if (tb[RTA_FLOW]) { + if (!old_nh && !(old_nh = rtnl_route_nh_alloc())) + return -NLE_NOMEM; + + rtnl_route_nh_set_realms(old_nh, nla_get_u32(tb[RTA_FLOW])); + } + + if (tb[RTA_NEWDST]) { + _nl_auto_nl_addr struct nl_addr *addr = NULL; + + if (!old_nh && !(old_nh = rtnl_route_nh_alloc())) + return -NLE_NOMEM; + + addr = nl_addr_alloc_attr(tb[RTA_NEWDST], route->rt_family); + if (!addr) + return -NLE_NOMEM; + + err = rtnl_route_nh_set_newdst(old_nh, addr); + if (err < 0) + return err; + } + + if (tb[RTA_VIA]) { + int alen = nla_len(tb[RTA_VIA]) - offsetof(struct rtvia, rtvia_addr); + _nl_auto_nl_addr struct nl_addr *addr = NULL; + struct rtvia *via = nla_data(tb[RTA_VIA]); + + if (!old_nh && !(old_nh = rtnl_route_nh_alloc())) + return -NLE_NOMEM; + + addr = nl_addr_build(via->rtvia_family, via->rtvia_addr, alen); + if (!addr) + return -NLE_NOMEM; + + err = rtnl_route_nh_set_via(old_nh, addr); + if (err < 0) + return err; + } + + if (tb[RTA_TTL_PROPAGATE]) { + rtnl_route_set_ttl_propagate(route, + nla_get_u8(tb[RTA_TTL_PROPAGATE])); + } + + if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE]) { + if (!old_nh && !(old_nh = rtnl_route_nh_alloc())) + return -NLE_NOMEM; + + err = nh_encap_parse_msg(tb[RTA_ENCAP], + tb[RTA_ENCAP_TYPE], old_nh); + if (err < 0) + return err; + } + + if (tb[RTA_NH_ID]) { + rtnl_route_set_nhid(route, nla_get_u32(tb[RTA_NH_ID])); + } + + if (old_nh) { + rtnl_route_nh_set_flags(old_nh, rtm->rtm_flags & 0xff); + if (route->rt_nr_nh == 0) { + /* If no nexthops have been provided via RTA_MULTIPATH + * we add it as regular nexthop to maintain backwards + * compatibility */ + rtnl_route_add_nexthop(route, _nl_steal_pointer(&old_nh)); + } else { + /* Kernel supports new style nexthop configuration, + * verify that it is a duplicate and discard nexthop. */ + struct rtnl_nexthop *first; + + first = nl_list_first_entry(&route->rt_nexthops, + struct rtnl_nexthop, + rtnh_list); + if (!first) + BUG(); + + if (rtnl_route_nh_compare(old_nh, first, + old_nh->ce_mask, 0)) { + return -NLE_INVAL; + } + } + } + + *result = _nl_steal_pointer(&route); + return 0; +} + +int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route) +{ + int i; + struct nlattr *metrics; + struct rtmsg rtmsg = { + .rtm_family = route->rt_family, + .rtm_tos = route->rt_tos, + .rtm_table = route->rt_table, + .rtm_protocol = route->rt_protocol, + .rtm_scope = route->rt_scope, + .rtm_type = route->rt_type, + .rtm_flags = route->rt_flags, + }; + + if (route->rt_dst == NULL) + return -NLE_MISSING_ATTR; + + rtmsg.rtm_dst_len = nl_addr_get_prefixlen(route->rt_dst); + if (route->rt_src) + rtmsg.rtm_src_len = nl_addr_get_prefixlen(route->rt_src); + + if (!(route->ce_mask & ROUTE_ATTR_SCOPE)) + rtmsg.rtm_scope = rtnl_route_guess_scope(route); + + if (rtnl_route_get_nnexthops(route) == 1) { + struct rtnl_nexthop *nh; + nh = rtnl_route_nexthop_n(route, 0); + rtmsg.rtm_flags |= nh->rtnh_flags; + } + + if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + /* Additional table attribute replacing the 8bit in the header, was + * required to allow more than 256 tables. MPLS does not allow the + * table attribute to be set + */ + if (route->rt_family != AF_MPLS) + NLA_PUT_U32(msg, RTA_TABLE, route->rt_table); + + if (nl_addr_get_len(route->rt_dst)) + NLA_PUT_ADDR(msg, RTA_DST, route->rt_dst); + + if (route->ce_mask & ROUTE_ATTR_PRIO) + NLA_PUT_U32(msg, RTA_PRIORITY, route->rt_prio); + + if (route->ce_mask & ROUTE_ATTR_SRC) + NLA_PUT_ADDR(msg, RTA_SRC, route->rt_src); + + if (route->ce_mask & ROUTE_ATTR_PREF_SRC) + NLA_PUT_ADDR(msg, RTA_PREFSRC, route->rt_pref_src); + + if (route->ce_mask & ROUTE_ATTR_IIF) + NLA_PUT_U32(msg, RTA_IIF, route->rt_iif); + + if (route->ce_mask & ROUTE_ATTR_TTL_PROPAGATE) + NLA_PUT_U8(msg, RTA_TTL_PROPAGATE, route->rt_ttl_propagate); + + if (route->rt_nmetrics > 0) { + uint32_t val; + + metrics = nla_nest_start(msg, RTA_METRICS); + if (metrics == NULL) + goto nla_put_failure; + + for (i = 1; i <= RTAX_MAX; i++) { + if (!rtnl_route_get_metric(route, i, &val)) + NLA_PUT_U32(msg, i, val); + } + + nla_nest_end(msg, metrics); + } + + /* Nexthop specification and nexthop id are mutually exclusive */ + if (route->ce_mask & ROUTE_ATTR_NHID) { + NLA_PUT_U32(msg, RTA_NH_ID, route->rt_nhid); + } else if (rtnl_route_get_nnexthops(route) == 1) { + struct rtnl_nexthop *nh; + + nh = rtnl_route_nexthop_n(route, 0); + if (nh->rtnh_gateway) + NLA_PUT_ADDR(msg, RTA_GATEWAY, nh->rtnh_gateway); + if (nh->rtnh_ifindex) + NLA_PUT_U32(msg, RTA_OIF, nh->rtnh_ifindex); + if (nh->rtnh_realms) + NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms); + if (nh->rtnh_newdst) + NLA_PUT_ADDR(msg, RTA_NEWDST, nh->rtnh_newdst); + if (nh->rtnh_via && rtnl_route_put_via(msg, nh->rtnh_via) < 0) + goto nla_put_failure; + if (nh->rtnh_encap && + nh_encap_build_msg(msg, nh->rtnh_encap) < 0) + goto nla_put_failure; + } else if (rtnl_route_get_nnexthops(route) > 1) { + struct nlattr *multipath; + struct rtnl_nexthop *nh; + + if (!(multipath = nla_nest_start(msg, RTA_MULTIPATH))) + goto nla_put_failure; + + nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) { + struct rtnexthop *rtnh; + + rtnh = nlmsg_reserve(msg, sizeof(*rtnh), NLMSG_ALIGNTO); + if (!rtnh) + goto nla_put_failure; + + rtnh->rtnh_flags = nh->rtnh_flags; + rtnh->rtnh_hops = nh->rtnh_weight; + rtnh->rtnh_ifindex = nh->rtnh_ifindex; + + if (nh->rtnh_gateway) + NLA_PUT_ADDR(msg, RTA_GATEWAY, + nh->rtnh_gateway); + + if (nh->rtnh_newdst) + NLA_PUT_ADDR(msg, RTA_NEWDST, nh->rtnh_newdst); + + if (nh->rtnh_via && + rtnl_route_put_via(msg, nh->rtnh_via) < 0) + goto nla_put_failure; + + if (nh->rtnh_realms) + NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms); + + if (nh->rtnh_encap && + nh_encap_build_msg(msg, nh->rtnh_encap) < 0) + goto nla_put_failure; + + rtnh->rtnh_len = (char *) nlmsg_tail(msg->nm_nlh) - + (char *) rtnh; + } + + nla_nest_end(msg, multipath); + } + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +/** @cond SKIP */ +struct nl_object_ops route_obj_ops = { + .oo_name = "route/route", + .oo_size = sizeof(struct rtnl_route), + .oo_constructor = route_constructor, + .oo_free_data = route_free_data, + .oo_clone = route_clone, + .oo_dump = { + [NL_DUMP_LINE] = route_dump_line, + [NL_DUMP_DETAILS] = route_dump_details, + [NL_DUMP_STATS] = route_dump_stats, + }, + .oo_compare = route_compare, + .oo_keygen = route_keygen, + .oo_update = route_update, + .oo_attrs2str = route_attrs2str, + .oo_id_attrs = (ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS | + ROUTE_ATTR_TABLE | ROUTE_ATTR_DST | + ROUTE_ATTR_PRIO), + .oo_id_attrs_get = route_id_attrs_get, +}; +/** @endcond */ + +/** @} */ diff --git a/libnl/lib/route/route_utils.c b/libnl/lib/route/route_utils.c new file mode 100644 index 0000000..04e52b4 --- /dev/null +++ b/libnl/lib/route/route_utils.c @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup route + * @defgroup route_utils Utilities + * @brief Routing Utility Functions + * + * + * @par 1) Translating Routing Table Names + * @code + * // libnl is only aware of the de facto standard routing table names. + * // Additional name <-> identifier associations have to be read in via + * // a configuration file, f.e. /etc/iproute2/rt_tables + * err = rtnl_route_read_table_names("/etc/iproute2/rt_tables"); + * + * // Translating a table name to its idenfier + * int table = rtnl_route_str2table("main"); + * + * // ... and the other way around. + * char buf[32]; + * printf("Name: %s\n", + * rtnl_route_table2str(table, buf, sizeof(buf))); + * @endcode + * + * + * + * + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include +#include + +#include "nl-priv-dynamic-core/nl-core.h" + +/** + * @name Routing Table Identifier Translations + * @{ + */ + +static NL_LIST_HEAD(table_names); + +static int add_routing_table_name(long id, const char *name) +{ + return __trans_list_add(id, name, &table_names); +} + +static void _nl_init init_routing_table_names(void) +{ + add_routing_table_name(RT_TABLE_UNSPEC, "unspec"); + add_routing_table_name(RT_TABLE_COMPAT, "compat"); + add_routing_table_name(RT_TABLE_DEFAULT, "default"); + add_routing_table_name(RT_TABLE_MAIN, "main"); + add_routing_table_name(RT_TABLE_LOCAL, "local"); +} + +static void _nl_exit release_routing_table_names(void) +{ + __trans_list_clear(&table_names); +} + +int rtnl_route_read_table_names(const char *path) +{ + __trans_list_clear(&table_names); + + return __nl_read_num_str_file(path, &add_routing_table_name); +} + +char *rtnl_route_table2str(int table, char *buf, size_t size) +{ + return __list_type2str(table, buf, size, &table_names); +} + +int rtnl_route_str2table(const char *name) +{ + return __list_str2type(name, &table_names); +} + + +/** @} */ + +/** + * @name Routing Protocol Translations + * @{ + */ + +static NL_LIST_HEAD(proto_names); + +static int add_proto_name(long id, const char *name) +{ + return __trans_list_add(id, name, &proto_names); +} + +static void _nl_init init_proto_names(void) +{ + add_proto_name(RTPROT_UNSPEC, "unspec"); + add_proto_name(RTPROT_REDIRECT, "redirect"); + add_proto_name(RTPROT_KERNEL, "kernel"); + add_proto_name(RTPROT_BOOT, "boot"); + add_proto_name(RTPROT_STATIC, "static"); +} + +static void _nl_exit release_proto_names(void) +{ + __trans_list_clear(&proto_names); +} + +int rtnl_route_read_protocol_names(const char *path) +{ + __trans_list_clear(&proto_names); + + return __nl_read_num_str_file(path, &add_proto_name); +} + +char *rtnl_route_proto2str(int proto, char *buf, size_t size) +{ + return __list_type2str(proto, buf, size, &proto_names); +} + +int rtnl_route_str2proto(const char *name) +{ + return __list_str2type(name, &proto_names); +} + +/** @} */ + +/** + * @name Routing Metrices Translations + * @{ + */ + +static const struct trans_tbl route_metrices[] = { + __ADD(RTAX_UNSPEC, unspec), + __ADD(RTAX_LOCK, lock), + __ADD(RTAX_MTU, mtu), + __ADD(RTAX_WINDOW, window), + __ADD(RTAX_RTT, rtt), + __ADD(RTAX_RTTVAR, rttvar), + __ADD(RTAX_SSTHRESH, ssthresh), + __ADD(RTAX_CWND, cwnd), + __ADD(RTAX_ADVMSS, advmss), + __ADD(RTAX_REORDERING, reordering), + __ADD(RTAX_HOPLIMIT, hoplimit), + __ADD(RTAX_INITCWND, initcwnd), + __ADD(RTAX_FEATURES, features), +}; + +char *rtnl_route_metric2str(int metric, char *buf, size_t size) +{ + return __type2str(metric, buf, size, route_metrices, + ARRAY_SIZE(route_metrices)); +} + +int rtnl_route_str2metric(const char *name) +{ + return __str2type(name, route_metrices, ARRAY_SIZE(route_metrices)); +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/route/rtnl.c b/libnl/lib/route/rtnl.c new file mode 100644 index 0000000..142c4c4 --- /dev/null +++ b/libnl/lib/route/rtnl.c @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @defgroup rtnl Routing Library (libnl-route) + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include + +#include "nl-priv-dynamic-core/nl-core.h" + +/** + * @name Sending + * @{ + */ + +/** + * Send routing netlink request message + * @arg sk Netlink socket. + * @arg type Netlink message type. + * @arg family Address family. + * @arg flags Additional netlink message flags. + * + * Fills out a routing netlink request message and sends it out + * using nl_send_simple(). + * + * @return 0 on success or a negative error code. Due to a bug in older + * version of the library, this function returned the number of bytes sent. + * Treat any non-negative number as success. + */ +int nl_rtgen_request(struct nl_sock *sk, int type, int family, int flags) +{ + int err; + struct rtgenmsg gmsg = { + .rtgen_family = family, + }; + + err = nl_send_simple(sk, type, flags, &gmsg, sizeof(gmsg)); + + return err >= 0 ? 0 : err; +} + +/** @} */ + +/** + * @name Routing Type Translations + * @{ + */ + +static const struct trans_tbl rtntypes[] = { + __ADD(RTN_UNSPEC,unspec), + __ADD(RTN_UNICAST,unicast), + __ADD(RTN_LOCAL,local), + __ADD(RTN_BROADCAST,broadcast), + __ADD(RTN_ANYCAST,anycast), + __ADD(RTN_MULTICAST,multicast), + __ADD(RTN_BLACKHOLE,blackhole), + __ADD(RTN_UNREACHABLE,unreachable), + __ADD(RTN_PROHIBIT,prohibit), + __ADD(RTN_THROW,throw), + __ADD(RTN_NAT,nat), + __ADD(RTN_XRESOLVE,xresolve), +}; + +char *nl_rtntype2str(int type, char *buf, size_t size) +{ + return __type2str(type, buf, size, rtntypes, ARRAY_SIZE(rtntypes)); +} + +int nl_str2rtntype(const char *name) +{ + return __str2type(name, rtntypes, ARRAY_SIZE(rtntypes)); +} + +/** @} */ + +/** + * @name Scope Translations + * @{ + */ + +static const struct trans_tbl scopes[] = { + __ADD(255,nowhere), + __ADD(254,host), + __ADD(253,link), + __ADD(200,site), + __ADD(0,global), +}; + +char *rtnl_scope2str(int scope, char *buf, size_t size) +{ + return __type2str(scope, buf, size, scopes, ARRAY_SIZE(scopes)); +} + +int rtnl_str2scope(const char *name) +{ + return __str2type(name, scopes, ARRAY_SIZE(scopes)); +} + +/** @} */ + +/** + * @name Realms Translations + * @{ + */ + +char * rtnl_realms2str(uint32_t realms, char *buf, size_t len) +{ + int from = RTNL_REALM_FROM(realms); + int to = RTNL_REALM_TO(realms); + + snprintf(buf, len, "%d/%d", from, to); + + return buf; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/route/rule.c b/libnl/lib/route/rule.c new file mode 100644 index 0000000..6e7c084 --- /dev/null +++ b/libnl/lib/route/rule.c @@ -0,0 +1,1021 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2010 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup rule Routing Rules + * @brief + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include + +#include "nl-route.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" + +/** @cond SKIP */ +struct rtnl_rule { + NLHDR_COMMON + uint8_t r_family; + uint8_t r_action; + uint8_t r_dsfield; /* ipv4 only */ + uint8_t r_l3mdev; + uint8_t r_protocol; /* protocol that installed rule */ + uint8_t r_ip_proto; /* IP/IPv6 protocol */ + uint32_t r_table; + uint32_t r_flags; + uint32_t r_prio; + uint32_t r_mark; + uint32_t r_mask; + uint32_t r_goto; + uint32_t r_flow; /* ipv4 only */ + struct nl_addr *r_src; + struct nl_addr *r_dst; + char r_iifname[IFNAMSIZ]; + char r_oifname[IFNAMSIZ]; + + struct fib_rule_port_range r_sport; + struct fib_rule_port_range r_dport; +}; + +#define RULE_ATTR_FAMILY 0x000001 +#define RULE_ATTR_TABLE 0x000002 +#define RULE_ATTR_ACTION 0x000004 +#define RULE_ATTR_FLAGS 0x000008 +#define RULE_ATTR_IIFNAME 0x000010 +#define RULE_ATTR_OIFNAME 0x000020 +#define RULE_ATTR_PRIO 0x000040 +#define RULE_ATTR_MARK 0x000080 +#define RULE_ATTR_MASK 0x000100 +#define RULE_ATTR_GOTO 0x000200 +#define RULE_ATTR_SRC 0x000400 +#define RULE_ATTR_DST 0x000800 +#define RULE_ATTR_DSFIELD 0x001000 +#define RULE_ATTR_FLOW 0x002000 +#define RULE_ATTR_L3MDEV 0x004000 +#define RULE_ATTR_PROTOCOL 0x008000 +#define RULE_ATTR_IP_PROTO 0x010000 +#define RULE_ATTR_SPORT 0x020000 +#define RULE_ATTR_DPORT 0x040000 + +static struct nl_cache_ops rtnl_rule_ops; +static struct nl_object_ops rule_obj_ops; +/** @endcond */ + +static void rule_free_data(struct nl_object *c) +{ + struct rtnl_rule *rule = nl_object_priv(c); + + if (!rule) + return; + + nl_addr_put(rule->r_src); + nl_addr_put(rule->r_dst); +} + +static int rule_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_rule *dst = nl_object_priv(_dst); + struct rtnl_rule *src = nl_object_priv(_src); + + dst->r_src = NULL; + dst->r_dst = NULL; + + if (src->r_src) + if (!(dst->r_src = nl_addr_clone(src->r_src))) + return -NLE_NOMEM; + + if (src->r_dst) + if (!(dst->r_dst = nl_addr_clone(src->r_dst))) + return -NLE_NOMEM; + + return 0; +} + +static struct nla_policy rule_policy[FRA_MAX+1] = { + [FRA_TABLE] = { .type = NLA_U32 }, + [FRA_IIFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, + [FRA_OIFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, + [FRA_PRIORITY] = { .type = NLA_U32 }, + [FRA_FWMARK] = { .type = NLA_U32 }, + [FRA_FWMASK] = { .type = NLA_U32 }, + [FRA_GOTO] = { .type = NLA_U32 }, + [FRA_FLOW] = { .type = NLA_U32 }, + [FRA_L3MDEV] = { .type = NLA_U8 }, + [FRA_PROTOCOL] = { .type = NLA_U8 }, + [FRA_IP_PROTO] = { .type = NLA_U8 }, + [FRA_SPORT_RANGE] = { .minlen = sizeof(struct fib_rule_port_range), + .maxlen = sizeof(struct fib_rule_port_range) }, + [FRA_DPORT_RANGE] = { .minlen = sizeof(struct fib_rule_port_range), + .maxlen = sizeof(struct fib_rule_port_range) }, +}; + +static int rule_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *n, struct nl_parser_param *pp) +{ + struct rtnl_rule *rule; + struct fib_rule_hdr *frh; + struct nlattr *tb[FRA_MAX+1]; + int err = 1, family; + + rule = rtnl_rule_alloc(); + if (!rule) { + err = -NLE_NOMEM; + goto errout; + } + + rule->ce_msgtype = n->nlmsg_type; + frh = nlmsg_data(n); + + err = nlmsg_parse(n, sizeof(*frh), tb, FRA_MAX, rule_policy); + if (err < 0) + goto errout; + + rule->r_family = family = frh->family; + rule->r_table = frh->table; + rule->r_action = frh->action; + rule->r_flags = frh->flags; + + rule->ce_mask = (RULE_ATTR_FAMILY | RULE_ATTR_ACTION | RULE_ATTR_FLAGS); + if (rule->r_table) + rule->ce_mask |= RULE_ATTR_TABLE; + + /* ipv4 only */ + if (frh->tos) { + rule->r_dsfield = frh->tos; + rule->ce_mask |= RULE_ATTR_DSFIELD; + } + + if (tb[FRA_TABLE]) { + rule->r_table = nla_get_u32(tb[FRA_TABLE]); + if (rule->r_table) + rule->ce_mask |= RULE_ATTR_TABLE; + } + + if (tb[FRA_IIFNAME]) { + nla_strlcpy(rule->r_iifname, tb[FRA_IIFNAME], IFNAMSIZ); + rule->ce_mask |= RULE_ATTR_IIFNAME; + } + + if (tb[FRA_OIFNAME]) { + nla_strlcpy(rule->r_oifname, tb[FRA_OIFNAME], IFNAMSIZ); + rule->ce_mask |= RULE_ATTR_OIFNAME; + } + + if (tb[FRA_PRIORITY]) { + rule->r_prio = nla_get_u32(tb[FRA_PRIORITY]); + rule->ce_mask |= RULE_ATTR_PRIO; + } + + if (tb[FRA_FWMARK]) { + rule->r_mark = nla_get_u32(tb[FRA_FWMARK]); + rule->ce_mask |= RULE_ATTR_MARK; + } + + if (tb[FRA_FWMASK]) { + rule->r_mask = nla_get_u32(tb[FRA_FWMASK]); + rule->ce_mask |= RULE_ATTR_MASK; + } + + if (tb[FRA_GOTO]) { + rule->r_goto = nla_get_u32(tb[FRA_GOTO]); + rule->ce_mask |= RULE_ATTR_GOTO; + } + + if (tb[FRA_SRC]) { + if (!(rule->r_src = nl_addr_alloc_attr(tb[FRA_SRC], family))) + goto errout_enomem; + + nl_addr_set_prefixlen(rule->r_src, frh->src_len); + rule->ce_mask |= RULE_ATTR_SRC; + } + + if (tb[FRA_DST]) { + if (!(rule->r_dst = nl_addr_alloc_attr(tb[FRA_DST], family))) + goto errout_enomem; + nl_addr_set_prefixlen(rule->r_dst, frh->dst_len); + rule->ce_mask |= RULE_ATTR_DST; + } + + /* ipv4 only */ + if (tb[FRA_FLOW]) { + rule->r_flow = nla_get_u32(tb[FRA_FLOW]); + rule->ce_mask |= RULE_ATTR_FLOW; + } + + if (tb[FRA_L3MDEV]) { + rule->r_l3mdev = nla_get_u8(tb[FRA_L3MDEV]); + rule->ce_mask |= RULE_ATTR_L3MDEV; + } + + if (tb[FRA_PROTOCOL]) { + rule->r_protocol = nla_get_u8(tb[FRA_PROTOCOL]); + rule->ce_mask |= RULE_ATTR_PROTOCOL; + } + + if (tb[FRA_IP_PROTO]) { + rule->r_ip_proto = nla_get_u8(tb[FRA_IP_PROTO]); + rule->ce_mask |= RULE_ATTR_IP_PROTO; + } + + if (tb[FRA_SPORT_RANGE]) { + struct fib_rule_port_range *pr; + + pr = nla_data(tb[FRA_SPORT_RANGE]); + rule->r_sport = *pr; + rule->ce_mask |= RULE_ATTR_SPORT; + } + + if (tb[FRA_DPORT_RANGE]) { + struct fib_rule_port_range *pr; + + pr = nla_data(tb[FRA_DPORT_RANGE]); + rule->r_dport = *pr; + rule->ce_mask |= RULE_ATTR_DPORT; + } + + err = pp->pp_cb((struct nl_object *) rule, pp); +errout: + rtnl_rule_put(rule); + return err; + +errout_enomem: + err = -NLE_NOMEM; + goto errout; +} + +static int rule_request_update(struct nl_cache *c, struct nl_sock *h) +{ + return nl_rtgen_request(h, RTM_GETRULE, AF_UNSPEC, NLM_F_DUMP); +} + +static void rule_dump_line(struct nl_object *o, struct nl_dump_params *p) +{ + struct rtnl_rule *r = (struct rtnl_rule *) o; + char buf[128]; + + nl_dump_line(p, "%8d ", (r->ce_mask & RULE_ATTR_PRIO) ? r->r_prio : 0); + nl_dump(p, "%s ", nl_af2str(r->r_family, buf, sizeof(buf))); + + if (r->ce_mask & RULE_ATTR_SRC) + nl_dump(p, "from %s ", + nl_addr2str(r->r_src, buf, sizeof(buf))); + + if (r->ce_mask & RULE_ATTR_DST) + nl_dump(p, "to %s ", + nl_addr2str(r->r_dst, buf, sizeof(buf))); + + if (r->ce_mask & RULE_ATTR_DSFIELD) + nl_dump(p, "tos %u ", r->r_dsfield); + + if (r->ce_mask & (RULE_ATTR_MARK | RULE_ATTR_MASK)) + nl_dump(p, "mark %#x/%#x", r->r_mark, r->r_mask); + + if (r->ce_mask & RULE_ATTR_IIFNAME) + nl_dump(p, "iif %s ", r->r_iifname); + + if (r->ce_mask & RULE_ATTR_OIFNAME) + nl_dump(p, "oif %s ", r->r_oifname); + + if (r->ce_mask & RULE_ATTR_TABLE) + nl_dump(p, "lookup %s ", + rtnl_route_table2str(r->r_table, buf, sizeof(buf))); + + if (r->ce_mask & RULE_ATTR_L3MDEV) + nl_dump(p, "lookup [l3mdev-table] "); + + if (r->ce_mask & RULE_ATTR_IP_PROTO) + nl_dump(p, "ipproto %s ", + nl_ip_proto2str(r->r_ip_proto, buf, sizeof(buf))); + + if (r->ce_mask & RULE_ATTR_SPORT) { + if (r->r_sport.start == r->r_sport.end) + nl_dump(p, "sport %u ", r->r_sport.start); + else + nl_dump(p, "sport %u-%u ", + r->r_sport.start, r->r_sport.end); + } + + if (r->ce_mask & RULE_ATTR_DPORT) { + if (r->r_dport.start == r->r_dport.end) + nl_dump(p, "dport %u ", r->r_dport.start); + else + nl_dump(p, "dport %u-%u ", + r->r_dport.start, r->r_dport.end); + } + + if (r->ce_mask & RULE_ATTR_PROTOCOL) + nl_dump(p, "protocol %s ", + rtnl_route_proto2str(r->r_protocol, buf, sizeof(buf))); + + if (r->ce_mask & RULE_ATTR_FLOW) + nl_dump(p, "flow %s ", + rtnl_realms2str(r->r_flow, buf, sizeof(buf))); + + if (r->ce_mask & RULE_ATTR_GOTO) + nl_dump(p, "goto %u ", r->r_goto); + + if (r->ce_mask & RULE_ATTR_ACTION) + nl_dump(p, "action %s", + nl_rtntype2str(r->r_action, buf, sizeof(buf))); + + nl_dump(p, "\n"); +} + +static void rule_dump_details(struct nl_object *obj, struct nl_dump_params *p) +{ + rule_dump_line(obj, p); +} + +static void rule_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + rule_dump_details(obj, p); +} + +static uint64_t rule_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct rtnl_rule *a = (struct rtnl_rule *) _a; + struct rtnl_rule *b = (struct rtnl_rule *) _b; + uint64_t diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(RULE_ATTR_FAMILY, a->r_family != b->r_family); + diff |= _DIFF(RULE_ATTR_TABLE, a->r_table != b->r_table); + diff |= _DIFF(RULE_ATTR_ACTION, a->r_action != b->r_action); + diff |= _DIFF(RULE_ATTR_IIFNAME, strcmp(a->r_iifname, b->r_iifname)); + diff |= _DIFF(RULE_ATTR_OIFNAME, strcmp(a->r_oifname, b->r_oifname)); + diff |= _DIFF(RULE_ATTR_PRIO, a->r_prio != b->r_prio); + diff |= _DIFF(RULE_ATTR_MARK, a->r_mark != b->r_mark); + diff |= _DIFF(RULE_ATTR_MASK, a->r_mask != b->r_mask); + diff |= _DIFF(RULE_ATTR_GOTO, a->r_goto != b->r_goto); + diff |= _DIFF(RULE_ATTR_SRC, nl_addr_cmp(a->r_src, b->r_src)); + diff |= _DIFF(RULE_ATTR_DST, nl_addr_cmp(a->r_dst, b->r_dst)); + diff |= _DIFF(RULE_ATTR_DSFIELD, a->r_dsfield != b->r_dsfield); + diff |= _DIFF(RULE_ATTR_FLOW, a->r_flow != b->r_flow); +#undef _DIFF + + return diff; +} + +static const struct trans_tbl rule_attrs[] = { + __ADD(RULE_ATTR_FAMILY, family), + __ADD(RULE_ATTR_TABLE, table), + __ADD(RULE_ATTR_ACTION, action), + __ADD(RULE_ATTR_IIFNAME, iifname), + __ADD(RULE_ATTR_OIFNAME, oifname), + __ADD(RULE_ATTR_PRIO, prio), + __ADD(RULE_ATTR_MARK, mark), + __ADD(RULE_ATTR_MASK, mask), + __ADD(RULE_ATTR_GOTO, goto), + __ADD(RULE_ATTR_SRC, src), + __ADD(RULE_ATTR_DST, dst), + __ADD(RULE_ATTR_DSFIELD, dsfield), + __ADD(RULE_ATTR_FLOW, flow), +}; + +static char *rule_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, rule_attrs, + ARRAY_SIZE(rule_attrs)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_rule *rtnl_rule_alloc(void) +{ + return (struct rtnl_rule *) nl_object_alloc(&rule_obj_ops); +} + +void rtnl_rule_put(struct rtnl_rule *rule) +{ + nl_object_put((struct nl_object *) rule); +} + +/** @} */ + +/** + * @name Cache Management + * @{ + */ + +/** + * Build a rule cache including all rules currently configured in the kernel. + * @arg sock Netlink socket. + * @arg family Address family or AF_UNSPEC. + * @arg result Pointer to store resulting cache. + * + * Allocates a new rule cache, initializes it properly and updates it + * to include all rules currently configured in the kernel. + * + * @return 0 on success or a negative error code. + */ +int rtnl_rule_alloc_cache(struct nl_sock *sock, int family, + struct nl_cache **result) +{ + struct nl_cache * cache; + int err; + + if (!(cache = nl_cache_alloc(&rtnl_rule_ops))) + return -NLE_NOMEM; + + cache->c_iarg1 = family; + + if (sock && (err = nl_cache_refill(sock, cache)) < 0) { + free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** @} */ + +/** + * @name Rule Addition + * @{ + */ + +static int build_rule_msg(struct rtnl_rule *tmpl, int cmd, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + struct fib_rule_hdr frh = { + .family = tmpl->r_family, + .table = tmpl->r_table, + .action = tmpl->r_action, + .flags = tmpl->r_flags, + .tos = tmpl->r_dsfield, + }; + + if (!(tmpl->ce_mask & RULE_ATTR_FAMILY)) + return -NLE_MISSING_ATTR; + + msg = nlmsg_alloc_simple(cmd, flags); + if (!msg) + return -NLE_NOMEM; + + if (tmpl->ce_mask & RULE_ATTR_SRC) + frh.src_len = nl_addr_get_prefixlen(tmpl->r_src); + + if (tmpl->ce_mask & RULE_ATTR_DST) + frh.dst_len = nl_addr_get_prefixlen(tmpl->r_dst); + + if (nlmsg_append(msg, &frh, sizeof(frh), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + /* Additional table attribute replacing the 8bit in the header, was + * required to allow more than 256 tables. */ + NLA_PUT_U32(msg, FRA_TABLE, tmpl->r_table); + + if (tmpl->ce_mask & RULE_ATTR_SRC) + NLA_PUT_ADDR(msg, FRA_SRC, tmpl->r_src); + + if (tmpl->ce_mask & RULE_ATTR_DST) + NLA_PUT_ADDR(msg, FRA_DST, tmpl->r_dst); + + if (tmpl->ce_mask & RULE_ATTR_IIFNAME) + NLA_PUT_STRING(msg, FRA_IIFNAME, tmpl->r_iifname); + + if (tmpl->ce_mask & RULE_ATTR_OIFNAME) + NLA_PUT_STRING(msg, FRA_OIFNAME, tmpl->r_oifname); + + if (tmpl->ce_mask & RULE_ATTR_PRIO) + NLA_PUT_U32(msg, FRA_PRIORITY, tmpl->r_prio); + + if (tmpl->ce_mask & RULE_ATTR_MARK) + NLA_PUT_U32(msg, FRA_FWMARK, tmpl->r_mark); + + if (tmpl->ce_mask & RULE_ATTR_MASK) + NLA_PUT_U32(msg, FRA_FWMASK, tmpl->r_mask); + + if (tmpl->ce_mask & RULE_ATTR_GOTO) + NLA_PUT_U32(msg, FRA_GOTO, tmpl->r_goto); + + if (tmpl->ce_mask & RULE_ATTR_FLOW) + NLA_PUT_U32(msg, FRA_FLOW, tmpl->r_flow); + + if (tmpl->ce_mask & RULE_ATTR_L3MDEV) + NLA_PUT_U8(msg, FRA_L3MDEV, tmpl->r_l3mdev); + + if (tmpl->ce_mask & RULE_ATTR_IP_PROTO) + NLA_PUT_U8(msg, FRA_IP_PROTO, tmpl->r_ip_proto); + + if (tmpl->ce_mask & RULE_ATTR_SPORT) + NLA_PUT(msg, FRA_SPORT_RANGE, sizeof(tmpl->r_sport), + &tmpl->r_sport); + + if (tmpl->ce_mask & RULE_ATTR_DPORT) + NLA_PUT(msg, FRA_DPORT_RANGE, sizeof(tmpl->r_dport), + &tmpl->r_dport); + + if (tmpl->ce_mask & RULE_ATTR_PROTOCOL) + NLA_PUT_U8(msg, FRA_PROTOCOL, tmpl->r_protocol); + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +/** + * Build netlink request message to add a new rule + * @arg tmpl template with data of new rule + * @arg flags additional netlink message flags + * @arg result Result pointer + * + * Builds a new netlink message requesting a addition of a new + * rule. The netlink message header isn't fully equipped with + * all relevant fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. \a tmpl must contain the attributes of the new + * address set via \c rtnl_rule_set_* functions. + * + * @return 0 on success or a negative error code. + */ +int rtnl_rule_build_add_request(struct rtnl_rule *tmpl, int flags, + struct nl_msg **result) +{ + return build_rule_msg(tmpl, RTM_NEWRULE, NLM_F_CREATE | flags, + result); +} + +/** + * Add a new rule + * @arg sk Netlink socket. + * @arg tmpl template with requested changes + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_rule_build_add_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been fullfilled. + * + * @return 0 on success or a negative error if an error occured. + */ +int rtnl_rule_add(struct nl_sock *sk, struct rtnl_rule *tmpl, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_rule_build_add_request(tmpl, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** @} */ + +/** + * @name Rule Deletion + * @{ + */ + +/** + * Build a netlink request message to delete a rule + * @arg rule rule to delete + * @arg flags additional netlink message flags + * @arg result Result pointer + * + * Builds a new netlink message requesting a deletion of a rule. + * The netlink message header isn't fully equipped with all relevant + * fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. \a rule must point to an existing + * address. + * + * @return 0 on success or a negative error code. + */ +int rtnl_rule_build_delete_request(struct rtnl_rule *rule, int flags, + struct nl_msg **result) +{ + return build_rule_msg(rule, RTM_DELRULE, flags, result); +} + +/** + * Delete a rule + * @arg sk Netlink socket. + * @arg rule rule to delete + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_rule_build_delete_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been fullfilled. + * + * @return 0 on success or a negative error if an error occured. + */ +int rtnl_rule_delete(struct nl_sock *sk, struct rtnl_rule *rule, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_rule_build_delete_request(rule, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** @} */ + +/** + * @name Attribute Modification + * @{ + */ + +void rtnl_rule_set_family(struct rtnl_rule *rule, int family) +{ + rule->r_family = family; + rule->ce_mask |= RULE_ATTR_FAMILY; +} + +int rtnl_rule_get_family(struct rtnl_rule *rule) +{ + if (rule->ce_mask & RULE_ATTR_FAMILY) + return rule->r_family; + else + return AF_UNSPEC; +} + +void rtnl_rule_set_prio(struct rtnl_rule *rule, uint32_t prio) +{ + rule->r_prio = prio; + rule->ce_mask |= RULE_ATTR_PRIO; +} + +uint32_t rtnl_rule_get_prio(struct rtnl_rule *rule) +{ + return rule->r_prio; +} + +void rtnl_rule_set_mark(struct rtnl_rule *rule, uint32_t mark) +{ + rule->r_mark = mark; + rule->ce_mask |= RULE_ATTR_MARK; +} + +uint32_t rtnl_rule_get_mark(struct rtnl_rule *rule) +{ + return rule->r_mark; +} + +void rtnl_rule_set_mask(struct rtnl_rule *rule, uint32_t mask) +{ + rule->r_mask = mask; + rule->ce_mask |= RULE_ATTR_MASK; +} + +uint32_t rtnl_rule_get_mask(struct rtnl_rule *rule) +{ + return rule->r_mask; +} + +void rtnl_rule_set_table(struct rtnl_rule *rule, uint32_t table) +{ + rule->r_table = table; + rule->ce_mask |= RULE_ATTR_TABLE; +} + +uint32_t rtnl_rule_get_table(struct rtnl_rule *rule) +{ + return rule->r_table; +} + +void rtnl_rule_set_dsfield(struct rtnl_rule *rule, uint8_t dsfield) +{ + rule->r_dsfield = dsfield; + rule->ce_mask |= RULE_ATTR_DSFIELD; +} + +uint8_t rtnl_rule_get_dsfield(struct rtnl_rule *rule) +{ + return rule->r_dsfield; +} + +static inline int __assign_addr(struct rtnl_rule *rule, struct nl_addr **pos, + struct nl_addr *new, int flag) +{ + if (rule->ce_mask & RULE_ATTR_FAMILY) { + if (new->a_family != rule->r_family) + return -NLE_AF_MISMATCH; + } else + rule->r_family = new->a_family; + + if (*pos) + nl_addr_put(*pos); + + nl_addr_get(new); + *pos = new; + + rule->ce_mask |= (flag | RULE_ATTR_FAMILY); + + return 0; +} + +int rtnl_rule_set_src(struct rtnl_rule *rule, struct nl_addr *src) +{ + return __assign_addr(rule, &rule->r_src, src, RULE_ATTR_SRC); +} + +struct nl_addr *rtnl_rule_get_src(struct rtnl_rule *rule) +{ + return rule->r_src; +} + +int rtnl_rule_set_dst(struct rtnl_rule *rule, struct nl_addr *dst) +{ + return __assign_addr(rule, &rule->r_dst, dst, RULE_ATTR_DST); +} + +struct nl_addr *rtnl_rule_get_dst(struct rtnl_rule *rule) +{ + return rule->r_dst; +} + +int rtnl_rule_set_iif(struct rtnl_rule *rule, const char *dev) +{ + if (strlen(dev) > IFNAMSIZ-1) + return -NLE_RANGE; + + strcpy(rule->r_iifname, dev); + rule->ce_mask |= RULE_ATTR_IIFNAME; + return 0; +} + +char *rtnl_rule_get_iif(struct rtnl_rule *rule) +{ + if (rule->ce_mask & RULE_ATTR_IIFNAME) + return rule->r_iifname; + else + return NULL; +} + +int rtnl_rule_set_oif(struct rtnl_rule *rule, const char *dev) +{ + if (strlen(dev) > IFNAMSIZ-1) + return -NLE_RANGE; + + strcpy(rule->r_oifname, dev); + rule->ce_mask |= RULE_ATTR_OIFNAME; + return 0; +} + +char *rtnl_rule_get_oif(struct rtnl_rule *rule) +{ + if (rule->ce_mask & RULE_ATTR_OIFNAME) + return rule->r_oifname; + else + return NULL; +} + +void rtnl_rule_set_action(struct rtnl_rule *rule, uint8_t action) +{ + rule->r_action = action; + rule->ce_mask |= RULE_ATTR_ACTION; +} + +uint8_t rtnl_rule_get_action(struct rtnl_rule *rule) +{ + return rule->r_action; +} + +/** + * Set l3mdev value of the rule (FRA_L3MDEV) + * @arg rule rule + * @arg value value to set + * + * Set the l3mdev value to value. Currently supported values + * are only 1 (set it) and -1 (unset it). All other values + * are reserved. + */ +void rtnl_rule_set_l3mdev(struct rtnl_rule *rule, int value) +{ + if (value >= 0) { + rule->r_l3mdev = (uint8_t) value; + rule->ce_mask |= RULE_ATTR_L3MDEV; + } else { + rule->r_l3mdev = 0; + rule->ce_mask &= ~((uint32_t) RULE_ATTR_L3MDEV); + } +} + +/** + * Get l3mdev value of the rule (FRA_L3MDEV) + * @arg rule rule + * + * @return a negative error code, including -NLE_MISSING_ATTR + * if the property is unset. Otherwise returns a non-negative + * value. As FRA_L3MDEV is a boolean, the only expected + * value at the moment is 1. + */ +int rtnl_rule_get_l3mdev(struct rtnl_rule *rule) +{ + if (!rule) + return -NLE_INVAL; + if (!(rule->ce_mask & RULE_ATTR_L3MDEV)) + return -NLE_MISSING_ATTR; + return rule->r_l3mdev; +} + +int rtnl_rule_set_protocol(struct rtnl_rule *rule, uint8_t protocol) +{ + if (protocol) { + rule->r_protocol = protocol; + rule->ce_mask |= RULE_ATTR_PROTOCOL; + } else { + rule->r_protocol = 0; + rule->ce_mask &= ~((uint32_t) RULE_ATTR_PROTOCOL); + } + return 0; +} + +int rtnl_rule_get_protocol(struct rtnl_rule *rule, uint8_t *protocol) +{ + if (!(rule->ce_mask & RULE_ATTR_PROTOCOL)) + return -NLE_INVAL; + + *protocol = rule->r_protocol; + return 0; +} + +int rtnl_rule_set_ipproto(struct rtnl_rule *rule, uint8_t ip_proto) +{ + if (ip_proto) { + rule->r_ip_proto = ip_proto; + rule->ce_mask |= RULE_ATTR_IP_PROTO; + } else { + rule->r_ip_proto = 0; + rule->ce_mask &= ~((uint32_t) RULE_ATTR_IP_PROTO); + } + return 0; +} + +int rtnl_rule_get_ipproto(struct rtnl_rule *rule, uint8_t *ip_proto) +{ + if (!(rule->ce_mask & RULE_ATTR_IP_PROTO)) + return -NLE_INVAL; + + *ip_proto = rule->r_ip_proto; + return 0; +} + +static int __rtnl_rule_set_port(struct fib_rule_port_range *prange, + uint16_t start, uint16_t end, + uint64_t attr, uint64_t *mask) +{ + if ((start && end < start) || (end && !start)) + return -NLE_INVAL; + + if (start) { + prange->start = start; + prange->end = end; + *mask |= attr; + } else { + prange->start = 0; + prange->end = 0; + *mask &= ~attr; + + } + return 0; +} + +int rtnl_rule_set_sport(struct rtnl_rule *rule, uint16_t sport) +{ + return __rtnl_rule_set_port(&rule->r_sport, sport, sport, + RULE_ATTR_SPORT, &rule->ce_mask); +} + +int rtnl_rule_set_sport_range(struct rtnl_rule *rule, uint16_t start, + uint16_t end) +{ + return __rtnl_rule_set_port(&rule->r_sport, start, end, + RULE_ATTR_SPORT, &rule->ce_mask); +} + +int rtnl_rule_get_sport(struct rtnl_rule *rule, uint16_t *start, uint16_t *end) +{ + if (!(rule->ce_mask & RULE_ATTR_SPORT)) + return -NLE_INVAL; + + *start = rule->r_sport.start; + *end = rule->r_sport.end; + return 0; +} + +int rtnl_rule_set_dport(struct rtnl_rule *rule, uint16_t dport) +{ + return __rtnl_rule_set_port(&rule->r_dport, dport, dport, + RULE_ATTR_DPORT, &rule->ce_mask); +} + +int rtnl_rule_set_dport_range(struct rtnl_rule *rule, uint16_t start, + uint16_t end) +{ + return __rtnl_rule_set_port(&rule->r_dport, start, end, + RULE_ATTR_DPORT, &rule->ce_mask); +} + +int rtnl_rule_get_dport(struct rtnl_rule *rule, uint16_t *start, uint16_t *end) +{ + if (!(rule->ce_mask & RULE_ATTR_DPORT)) + return -NLE_INVAL; + + *start = rule->r_dport.start; + *end = rule->r_dport.end; + return 0; +} + +void rtnl_rule_set_realms(struct rtnl_rule *rule, uint32_t realms) +{ + rule->r_flow = realms; + rule->ce_mask |= RULE_ATTR_FLOW; +} + +uint32_t rtnl_rule_get_realms(struct rtnl_rule *rule) +{ + return rule->r_flow; +} + +void rtnl_rule_set_goto(struct rtnl_rule *rule, uint32_t ref) +{ + rule->r_goto = ref; + rule->ce_mask |= RULE_ATTR_GOTO; +} + +uint32_t rtnl_rule_get_goto(struct rtnl_rule *rule) +{ + return rule->r_goto; +} + +/** @} */ + +static struct nl_object_ops rule_obj_ops = { + .oo_name = "route/rule", + .oo_size = sizeof(struct rtnl_rule), + .oo_free_data = rule_free_data, + .oo_clone = rule_clone, + .oo_dump = { + [NL_DUMP_LINE] = rule_dump_line, + [NL_DUMP_DETAILS] = rule_dump_details, + [NL_DUMP_STATS] = rule_dump_stats, + }, + .oo_compare = rule_compare, + .oo_attrs2str = rule_attrs2str, + .oo_id_attrs = ~0, +}; + +static struct nl_af_group rule_groups[] = { + { AF_INET, RTNLGRP_IPV4_RULE }, + { AF_INET6, RTNLGRP_IPV6_RULE }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops rtnl_rule_ops = { + .co_name = "route/rule", + .co_hdrsize = sizeof(struct fib_rule_hdr), + .co_msgtypes = { + { RTM_NEWRULE, NL_ACT_NEW, "new" }, + { RTM_DELRULE, NL_ACT_DEL, "del" }, + { RTM_GETRULE, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_request_update = rule_request_update, + .co_msg_parser = rule_msg_parser, + .co_obj_ops = &rule_obj_ops, + .co_groups = rule_groups, +}; + +static void _nl_init rule_init(void) +{ + nl_cache_mngt_register(&rtnl_rule_ops); +} + +static void _nl_exit rule_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_rule_ops); +} + +/** @} */ diff --git a/libnl/lib/route/tc-api.h b/libnl/lib/route/tc-api.h new file mode 100644 index 0000000..43c4a28 --- /dev/null +++ b/libnl/lib/route/tc-api.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2011-2013 Thomas Graf + */ + +#ifndef NETLINK_TC_API_H_ +#define NETLINK_TC_API_H_ + +#include +#include +#include + +#include "nl-route.h" +#include "nl-priv-dynamic-route/nl-priv-dynamic-route.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" + +/*****************************************************************************/ + +#define TCA_ATTR_HANDLE 0x0001 +#define TCA_ATTR_PARENT 0x0002 +#define TCA_ATTR_IFINDEX 0x0004 +#define TCA_ATTR_KIND 0x0008 +#define TCA_ATTR_FAMILY 0x0010 +#define TCA_ATTR_INFO 0x0020 +#define TCA_ATTR_OPTS 0x0040 +#define TCA_ATTR_STATS 0x0080 +#define TCA_ATTR_XSTATS 0x0100 +#define TCA_ATTR_LINK 0x0200 +#define TCA_ATTR_MTU 0x0400 +#define TCA_ATTR_MPU 0x0800 +#define TCA_ATTR_OVERHEAD 0x1000 +#define TCA_ATTR_LINKTYPE 0x2000 +#define TCA_ATTR_CHAIN 0x4000 +#define TCA_ATTR_MAX TCA_ATTR_CHAIN + +extern int tca_parse(struct nlattr **, int, struct rtnl_tc *, + const struct nla_policy *); + +#define RTNL_TC_RTABLE_SIZE 256 + +static inline void *tca_xstats(struct rtnl_tc *tca) +{ + return tca->tc_xstats->d_data; +} + +extern struct nl_af_group tc_groups[]; + +/*****************************************************************************/ + +struct rtnl_tc_type_ops +{ + enum rtnl_tc_type tt_type; + + char *tt_dump_prefix; + + /** + * Dump callbacks + */ + void (*tt_dump[NL_DUMP_MAX+1])(struct rtnl_tc *, + struct nl_dump_params *); +}; + +void *rtnl_tc_data_peek(struct rtnl_tc *tc); + +/*****************************************************************************/ + +/* WARNING: the following symbols are wrongly exported in libnl-route-3 + * library. They are private API, but leaked. */ +extern int rtnl_tc_msg_parse(struct nlmsghdr *, + struct rtnl_tc *); +extern int rtnl_tc_msg_build(struct rtnl_tc *, int, + int, struct nl_msg **); + +extern void rtnl_tc_free_data(struct nl_object *); +extern int rtnl_tc_clone(struct nl_object *, + struct nl_object *); +extern void rtnl_tc_dump_line(struct nl_object *, + struct nl_dump_params *); +extern void rtnl_tc_dump_details(struct nl_object *, + struct nl_dump_params *); +extern void rtnl_tc_dump_stats(struct nl_object *, + struct nl_dump_params *); +extern uint64_t rtnl_tc_compare(struct nl_object *, + struct nl_object *, + uint64_t, int); + +extern void * rtnl_tc_data(struct rtnl_tc *); +extern void * rtnl_tc_data_check(struct rtnl_tc *, + struct rtnl_tc_ops *, int *); + +extern int rtnl_tc_register(struct rtnl_tc_ops *); +extern void rtnl_tc_unregister(struct rtnl_tc_ops *); + +extern void rtnl_tc_type_register(struct rtnl_tc_type_ops *); +extern void rtnl_tc_type_unregister(struct rtnl_tc_type_ops *); + + +extern int rtnl_tc_build_rate_table(struct rtnl_tc *tc, struct rtnl_ratespec *, + uint32_t *); + +/*****************************************************************************/ + +#endif diff --git a/libnl/lib/route/tc.c b/libnl/lib/route/tc.c new file mode 100644 index 0000000..bbb2f8c --- /dev/null +++ b/libnl/lib/route/tc.c @@ -0,0 +1,1164 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup tc Traffic Control + * @{ + */ + +#include "nl-default.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "tc-api.h" + +/** @cond SKIP */ + +static struct nl_list_head tc_ops_list[__RTNL_TC_TYPE_MAX]; +static struct rtnl_tc_type_ops *tc_type_ops[__RTNL_TC_TYPE_MAX]; + +static struct nla_policy tc_policy[TCA_MAX+1] = { + [TCA_KIND] = { .type = NLA_STRING, + .maxlen = TCKINDSIZ }, + [TCA_CHAIN] = { .type = NLA_U32 }, + [TCA_STATS] = { .minlen = sizeof(struct tc_stats) }, + [TCA_STATS2] = { .type = NLA_NESTED }, +}; + +int tca_parse(struct nlattr **tb, int maxattr, struct rtnl_tc *g, + const struct nla_policy *policy) +{ + + if (g->ce_mask & TCA_ATTR_OPTS) + return nla_parse(tb, maxattr, + (struct nlattr *) g->tc_opts->d_data, + g->tc_opts->d_size, policy); + else { + /* Ugly but tb[] must be in a defined state even if no + * attributes can be found. */ + memset(tb, 0, sizeof(struct nlattr *) * (maxattr + 1)); + return 0; + } +} + +static struct nla_policy tc_stats2_policy[TCA_STATS_MAX+1] = { + [TCA_STATS_BASIC] = { .minlen = sizeof(struct gnet_stats_basic) }, + [TCA_STATS_RATE_EST] = { .minlen = sizeof(struct gnet_stats_rate_est) }, + [TCA_STATS_QUEUE] = { .minlen = sizeof(struct gnet_stats_queue) }, +}; + +int rtnl_tc_msg_parse(struct nlmsghdr *n, struct rtnl_tc *tc) +{ + struct nl_cache *link_cache; + struct rtnl_tc_ops *ops; + struct nlattr *tb[TCA_MAX + 1]; + char kind[TCKINDSIZ]; + struct tcmsg *tm; + int err; + + tc->ce_msgtype = n->nlmsg_type; + + err = nlmsg_parse(n, sizeof(*tm), tb, TCA_MAX, tc_policy); + if (err < 0) + return err; + + if (tb[TCA_KIND] == NULL) + return -NLE_MISSING_ATTR; + + nla_strlcpy(kind, tb[TCA_KIND], sizeof(kind)); + rtnl_tc_set_kind(tc, kind); + + if (tb[TCA_CHAIN]) + rtnl_tc_set_chain(tc, nla_get_u32(tb[TCA_CHAIN])); + + tm = nlmsg_data(n); + tc->tc_family = tm->tcm_family; + tc->tc_ifindex = tm->tcm_ifindex; + tc->tc_handle = tm->tcm_handle; + tc->tc_parent = tm->tcm_parent; + tc->tc_info = tm->tcm_info; + + tc->ce_mask |= (TCA_ATTR_FAMILY | TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE| + TCA_ATTR_PARENT | TCA_ATTR_INFO); + + if (tb[TCA_OPTIONS]) { + tc->tc_opts = nl_data_alloc_attr(tb[TCA_OPTIONS]); + if (!tc->tc_opts) + return -NLE_NOMEM; + tc->ce_mask |= TCA_ATTR_OPTS; + } + + if (tb[TCA_STATS2]) { + struct nlattr *tbs[TCA_STATS_MAX + 1]; + + err = nla_parse_nested(tbs, TCA_STATS_MAX, tb[TCA_STATS2], + tc_stats2_policy); + if (err < 0) + return err; + + if (tbs[TCA_STATS_BASIC]) { + struct gnet_stats_basic bs; + + memcpy(&bs, nla_data(tbs[TCA_STATS_BASIC]), sizeof(bs)); + tc->tc_stats[RTNL_TC_BYTES] = bs.bytes; + tc->tc_stats[RTNL_TC_PACKETS] = bs.packets; + } + + if (tbs[TCA_STATS_RATE_EST]) { + struct gnet_stats_rate_est *re; + + re = nla_data(tbs[TCA_STATS_RATE_EST]); + tc->tc_stats[RTNL_TC_RATE_BPS] = re->bps; + tc->tc_stats[RTNL_TC_RATE_PPS] = re->pps; + } + + if (tbs[TCA_STATS_QUEUE]) { + struct gnet_stats_queue *q; + + q = nla_data(tbs[TCA_STATS_QUEUE]); + tc->tc_stats[RTNL_TC_QLEN] = q->qlen; + tc->tc_stats[RTNL_TC_BACKLOG] = q->backlog; + tc->tc_stats[RTNL_TC_DROPS] = q->drops; + tc->tc_stats[RTNL_TC_REQUEUES] = q->requeues; + tc->tc_stats[RTNL_TC_OVERLIMITS] = q->overlimits; + } + + tc->ce_mask |= TCA_ATTR_STATS; + + if (tbs[TCA_STATS_APP]) { + tc->tc_xstats = nl_data_alloc_attr(tbs[TCA_STATS_APP]); + if (tc->tc_xstats == NULL) + return -NLE_NOMEM; + tc->ce_mask |= TCA_ATTR_XSTATS; + } else + goto compat_xstats; + } else { + if (tb[TCA_STATS]) { + struct tc_stats st; + + memcpy(&st, nla_data(tb[TCA_STATS]), sizeof(st)); + tc->tc_stats[RTNL_TC_BYTES] = st.bytes; + tc->tc_stats[RTNL_TC_PACKETS] = st.packets; + tc->tc_stats[RTNL_TC_RATE_BPS] = st.bps; + tc->tc_stats[RTNL_TC_RATE_PPS] = st.pps; + tc->tc_stats[RTNL_TC_QLEN] = st.qlen; + tc->tc_stats[RTNL_TC_BACKLOG] = st.backlog; + tc->tc_stats[RTNL_TC_DROPS] = st.drops; + tc->tc_stats[RTNL_TC_OVERLIMITS]= st.overlimits; + + tc->ce_mask |= TCA_ATTR_STATS; + } + +compat_xstats: + if (tb[TCA_XSTATS]) { + tc->tc_xstats = nl_data_alloc_attr(tb[TCA_XSTATS]); + if (tc->tc_xstats == NULL) + return -NLE_NOMEM; + tc->ce_mask |= TCA_ATTR_XSTATS; + } + } + + ops = rtnl_tc_get_ops(tc); + if (ops && ops->to_msg_parser) { + void *data = rtnl_tc_data(tc); + + if (!data) + return -NLE_NOMEM; + + err = ops->to_msg_parser(tc, data); + if (err < 0) + return err; + } + + if ((link_cache = __nl_cache_mngt_require("route/link"))) { + struct rtnl_link *link; + + if ((link = rtnl_link_get(link_cache, tc->tc_ifindex))) { + rtnl_tc_set_link(tc, link); + + /* rtnl_tc_set_link incs refcnt */ + rtnl_link_put(link); + } + } + + return 0; +} + +int rtnl_tc_msg_build(struct rtnl_tc *tc, int type, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + struct rtnl_tc_ops *ops; + struct tcmsg tchdr = { + .tcm_family = AF_UNSPEC, + .tcm_ifindex = tc->tc_ifindex, + .tcm_handle = tc->tc_handle, + .tcm_parent = tc->tc_parent, + }; + int err; + + msg = nlmsg_alloc_simple(type, flags); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) { + err = -NLE_MSGSIZE; + goto out_err; + } + + if (tc->ce_mask & TCA_ATTR_KIND) + NLA_PUT_STRING(msg, TCA_KIND, tc->tc_kind); + + if (tc->ce_mask & TCA_ATTR_CHAIN) + NLA_PUT_U32(msg, TCA_CHAIN, tc->tc_chain); + + ops = rtnl_tc_get_ops(tc); + if (ops && (ops->to_msg_fill || ops->to_msg_fill_raw)) { + struct nlattr *opts; + void *data = rtnl_tc_data(tc); + + if (ops->to_msg_fill) { + if (!(opts = nla_nest_start(msg, TCA_OPTIONS))) { + err = -NLE_NOMEM; + goto out_err; + } + + if ((err = ops->to_msg_fill(tc, data, msg)) < 0) + goto out_err; + + if (strcmp("cgroup", tc->tc_kind)) + nla_nest_end(msg, opts); + else + nla_nest_end_keep_empty(msg, opts); + } else if ((err = ops->to_msg_fill_raw(tc, data, msg)) < 0) + goto out_err; + } + + *result = msg; + return 0; + +nla_put_failure: + err = -NLE_NOMEM; +out_err: + nlmsg_free(msg); + return err; +} + + +/** @endcond */ + +/** + * @name Attributes + * @{ + */ + +/** + * Set interface index of traffic control object + * @arg tc traffic control object + * @arg ifindex interface index. + * + * Sets the interface index of a traffic control object. The interface + * index defines the network device which this tc object is attached to. + * This function will overwrite any network device assigned with previous + * calls to rtnl_tc_set_ifindex() or rtnl_tc_set_link(). + */ +void rtnl_tc_set_ifindex(struct rtnl_tc *tc, int ifindex) +{ + /* Obsolete possible old link reference */ + rtnl_link_put(tc->tc_link); + tc->tc_link = NULL; + tc->ce_mask &= ~TCA_ATTR_LINK; + + tc->tc_ifindex = ifindex; + tc->ce_mask |= TCA_ATTR_IFINDEX; +} + +/** + * Return interface index of traffic control object + * @arg tc traffic control object + */ +int rtnl_tc_get_ifindex(struct rtnl_tc *tc) +{ + return tc->tc_ifindex; +} + +/** + * Set link of traffic control object + * @arg tc traffic control object + * @arg link link object + * + * Sets the link of a traffic control object. This function serves + * the same purpose as rtnl_tc_set_ifindex() but due to the continued + * allowed access to the link object it gives it the possibility to + * retrieve sane default values for the the MTU and the linktype. + * Always prefer this function over rtnl_tc_set_ifindex() if you can + * spare to have an additional link object around. + */ +void rtnl_tc_set_link(struct rtnl_tc *tc, struct rtnl_link *link) +{ + rtnl_link_put(tc->tc_link); + + if (!link) + return; + if (!link->l_index) + BUG(); + + nl_object_get(OBJ_CAST(link)); + tc->tc_link = link; + tc->tc_ifindex = link->l_index; + tc->ce_mask |= TCA_ATTR_LINK | TCA_ATTR_IFINDEX; +} + +/** + * Get link of traffic control object + * @arg tc traffic control object + * + * Returns the link of a traffic control object. The link is only + * returned if it has been set before via rtnl_tc_set_link() or + * if a link cache was available while parsing the tc object. This + * function may still return NULL even if an ifindex is assigned to + * the tc object. It will _not_ look up the link by itself. + * + * @note The returned link will have its reference counter incremented. + * It is in the responsibility of the caller to return the + * reference. + * + * @return link object or NULL if not set. + */ +struct rtnl_link *rtnl_tc_get_link(struct rtnl_tc *tc) +{ + if (tc->tc_link) { + nl_object_get(OBJ_CAST(tc->tc_link)); + return tc->tc_link; + } + + return NULL; +} + +/** + * Set the Maximum Transmission Unit (MTU) of traffic control object + * @arg tc traffic control object + * @arg mtu largest packet size expected + * + * Sets the MTU of a traffic control object. Not all traffic control + * objects will make use of this but it helps while calculating rate + * tables. This value is typically derived directly from the link + * the tc object is attached to if the link has been assigned via + * rtnl_tc_set_link(). It is usually not necessary to set the MTU + * manually, this function is provided to allow overwriting the derived + * value. + */ +void rtnl_tc_set_mtu(struct rtnl_tc *tc, uint32_t mtu) +{ + tc->tc_mtu = mtu; + tc->ce_mask |= TCA_ATTR_MTU; +} + +/** + * Return the MTU of traffic control object + * @arg tc traffic control object + * + * Returns the MTU of a traffic control object which has been set via: + * -# User specified value set via rtnl_tc_set_mtu() + * -# Dervied from link set via rtnl_tc_set_link() + * -# Fall back to default: ethernet = 1500 + */ +uint32_t rtnl_tc_get_mtu(struct rtnl_tc *tc) +{ + if (tc->ce_mask & TCA_ATTR_MTU) + return tc->tc_mtu; + else if (tc->ce_mask & TCA_ATTR_LINK) + return tc->tc_link->l_mtu; + else + return 1500; /* default to ethernet */ +} + +/** + * Set the Minimum Packet Unit (MPU) of a traffic control object + * @arg tc traffic control object + * @arg mpu minimum packet size expected + * + * Sets the MPU of a traffic contorl object. It specifies the minimum + * packet size to ever hit this traffic control object. Not all traffic + * control objects will make use of this but it helps while calculating + * rate tables. + */ +void rtnl_tc_set_mpu(struct rtnl_tc *tc, uint32_t mpu) +{ + tc->tc_mpu = mpu; + tc->ce_mask |= TCA_ATTR_MPU; +} + +/** + * Return the Minimum Packet Unit (MPU) of a traffic control object + * @arg tc traffic control object + * + * @return The MPU previously set via rtnl_tc_set_mpu() or 0. + */ +uint32_t rtnl_tc_get_mpu(struct rtnl_tc *tc) +{ + return tc->tc_mpu; +} + +/** + * Set per packet overhead of a traffic control object + * @arg tc traffic control object + * @arg overhead overhead per packet in bytes + * + * Sets the per packet overhead in bytes occuring on the link not seen + * by the kernel. This value can be used to correct size calculations + * if the packet size on the wire does not match the packet sizes seen + * in the network stack. Not all traffic control objects will make use + * this but it helps while calculating accurate packet sizes in the + * kernel. + */ +void rtnl_tc_set_overhead(struct rtnl_tc *tc, uint32_t overhead) +{ + tc->tc_overhead = overhead; + tc->ce_mask |= TCA_ATTR_OVERHEAD; +} + +/** + * Return per packet overhead of a traffic control object + * @arg tc traffic control object + * + * @return The overhead previously set by rtnl_tc_set_overhead() or 0. + */ +uint32_t rtnl_tc_get_overhead(struct rtnl_tc *tc) +{ + return tc->tc_overhead; +} + +/** + * Set the linktype of a traffic control object + * @arg tc traffic control object + * @arg type type of link (e.g. ARPHRD_ATM, ARPHRD_ETHER) + * + * Overwrites the type of link this traffic control object is attached to. + * This value is typically derived from the link this tc object is attached + * if the link has been assigned via rtnl_tc_set_link(). It is usually not + * necessary to set the linktype manually. This function is provided to + * allow overwriting the linktype. + */ +void rtnl_tc_set_linktype(struct rtnl_tc *tc, uint32_t type) +{ + tc->tc_linktype = type; + tc->ce_mask |= TCA_ATTR_LINKTYPE; +} + +/** + * Return the linktype of a traffic control object + * @arg tc traffic control object + * + * Returns the linktype of the link the traffic control object is attached to: + * -# User specified value via rtnl_tc_set_linktype() + * -# Value derived from link set via rtnl_tc_set_link() + * -# Default fall-back: ARPHRD_ETHER + */ +uint32_t rtnl_tc_get_linktype(struct rtnl_tc *tc) +{ + if (tc->ce_mask & TCA_ATTR_LINKTYPE) + return tc->tc_linktype; + else if (tc->ce_mask & TCA_ATTR_LINK) + return tc->tc_link->l_arptype; + else + return ARPHRD_ETHER; /* default to ethernet */ +} + +/** + * Set identifier of traffic control object + * @arg tc traffic control object + * @arg id unique identifier + */ +void rtnl_tc_set_handle(struct rtnl_tc *tc, uint32_t id) +{ + tc->tc_handle = id; + tc->ce_mask |= TCA_ATTR_HANDLE; +} + +/** + * Return identifier of a traffic control object + * @arg tc traffic control object + */ +uint32_t rtnl_tc_get_handle(struct rtnl_tc *tc) +{ + return tc->tc_handle; +} + +/** + * Set the parent identifier of a traffic control object + * @arg tc traffic control object + * @arg parent identifier of parent traffif control object + * + */ +void rtnl_tc_set_parent(struct rtnl_tc *tc, uint32_t parent) +{ + tc->tc_parent = parent; + tc->ce_mask |= TCA_ATTR_PARENT; +} + +/** + * Return parent identifier of a traffic control object + * @arg tc traffic control object + */ +uint32_t rtnl_tc_get_parent(struct rtnl_tc *tc) +{ + return tc->tc_parent; +} + +/** + * Define the type of traffic control object + * @arg tc traffic control object + * @arg kind name of the tc object type + * + * @return 0 on success or a negative error code + */ +int rtnl_tc_set_kind(struct rtnl_tc *tc, const char *kind) +{ + if (tc->ce_mask & TCA_ATTR_KIND) + return -NLE_EXIST; + + if ( !kind + || strlen (kind) >= sizeof (tc->tc_kind)) + return -NLE_INVAL; + + _nl_strncpy_assert(tc->tc_kind, kind, sizeof(tc->tc_kind)); + + tc->ce_mask |= TCA_ATTR_KIND; + + /* Force allocation of data */ + rtnl_tc_data(tc); + + return 0; +} + +/** + * Return kind of traffic control object + * @arg tc traffic control object + * + * @return Kind of traffic control object or NULL if not set. + */ +char *rtnl_tc_get_kind(struct rtnl_tc *tc) +{ + if (tc->ce_mask & TCA_ATTR_KIND) + return tc->tc_kind; + else + return NULL; +} + +/** + * Return value of a statistical counter of a traffic control object + * @arg tc traffic control object + * @arg id identifier of statistical counter + * + * @return Value of requested statistic counter or 0. + */ +uint64_t rtnl_tc_get_stat(struct rtnl_tc *tc, enum rtnl_tc_stat id) +{ + if ((unsigned int) id > RTNL_TC_STATS_MAX) + return 0; + + return tc->tc_stats[id]; +} + +/** + * Set the chain index of a traffic control object + * @arg tc traffic control object + * @arg chain chain index of traffic control object + * + */ +void rtnl_tc_set_chain(struct rtnl_tc *tc, uint32_t chain) +{ + tc->tc_chain = chain; + tc->ce_mask |= TCA_ATTR_CHAIN; +} + +/** + * Return chain index of traffic control object + * @arg tc traffic control object + * @arg out_value output argument. + * + * @return 0 of the output value was successfully returned, or a negative + * error code on failure. + */ +int rtnl_tc_get_chain(struct rtnl_tc *tc, uint32_t *out_value) +{ + if (!(tc->ce_mask & TCA_ATTR_CHAIN)) + return -NLE_MISSING_ATTR; + *out_value = tc->tc_chain; + return 0; +} + +/** @} */ + +/** + * @name Utilities + * @{ + */ + +static const struct trans_tbl tc_stats[] = { + __ADD(RTNL_TC_PACKETS, packets), + __ADD(RTNL_TC_BYTES, bytes), + __ADD(RTNL_TC_RATE_BPS, rate_bps), + __ADD(RTNL_TC_RATE_PPS, rate_pps), + __ADD(RTNL_TC_QLEN, qlen), + __ADD(RTNL_TC_BACKLOG, backlog), + __ADD(RTNL_TC_DROPS, drops), + __ADD(RTNL_TC_REQUEUES, requeues), + __ADD(RTNL_TC_OVERLIMITS, overlimits), +}; + +char *rtnl_tc_stat2str(enum rtnl_tc_stat st, char *buf, size_t len) +{ + return __type2str(st, buf, len, tc_stats, ARRAY_SIZE(tc_stats)); +} + +int rtnl_tc_str2stat(const char *name) +{ + return __str2type(name, tc_stats, ARRAY_SIZE(tc_stats)); +} + +/** + * Calculate time required to transmit buffer at a specific rate + * @arg bufsize Size of buffer to be transmited in bytes. + * @arg rate Transmit rate in bytes per second. + * + * Calculates the number of micro seconds required to transmit a + * specific buffer at a specific transmit rate. + * + * @f[ + * txtime=\frac{bufsize}{rate}10^6 + * @f] + * + * @return Required transmit time in micro seconds. + */ +int rtnl_tc_calc_txtime(int bufsize, int rate) +{ + return ((double) bufsize / (double) rate) * 1000000.0; +} + +/** + * Calculate buffer size able to transmit in a specific time and rate. + * @arg txtime Available transmit time in micro seconds. + * @arg rate Transmit rate in bytes per second. + * + * Calculates the size of the buffer that can be transmitted in a + * specific time period at a specific transmit rate. + * + * @f[ + * bufsize=\frac{{txtime} \times {rate}}{10^6} + * @f] + * + * @return Size of buffer in bytes. + */ +int rtnl_tc_calc_bufsize(int txtime, int rate) +{ + return ((double) txtime * (double) rate) / 1000000.0; +} + +/** + * Calculate the binary logarithm for a specific cell size + * @arg cell_size Size of cell, must be a power of two. + * @return Binary logarithm of cell size or a negative error code. + */ +int rtnl_tc_calc_cell_log(int cell_size) +{ + int i; + + for (i = 0; i < 32; i++) + if ((((uint32_t)1u) << i) == ((uint32_t)cell_size)) + return i; + + return -NLE_INVAL; +} + + +/** @} */ + +/** + * @name Rate Tables + * @{ + */ + +/* + * COPYRIGHT NOTE: + * align_to_atm() and adjust_size() derived/coped from iproute2 source. + */ + +/* + * The align to ATM cells is used for determining the (ATM) SAR + * alignment overhead at the ATM layer. (SAR = Segmentation And + * Reassembly). This is for example needed when scheduling packet on + * an ADSL connection. Note that the extra ATM-AAL overhead is _not_ + * included in this calculation. This overhead is added in the kernel + * before doing the rate table lookup, as this gives better precision + * (as the table will always be aligned for 48 bytes). + * --Hawk, d.7/11-2004. + */ +static unsigned int align_to_atm(unsigned int size) +{ + int linksize, cells; + cells = size / ATM_CELL_PAYLOAD; + if ((size % ATM_CELL_PAYLOAD) > 0) + cells++; + + linksize = cells * ATM_CELL_SIZE; /* Use full cell size to add ATM tax */ + return linksize; +} + +static unsigned int adjust_size(unsigned int size, unsigned int mpu, + uint32_t linktype) +{ + if (size < mpu) + size = mpu; + + switch (linktype) { + case ARPHRD_ATM: + return align_to_atm(size); + + case ARPHRD_ETHER: + default: + return size; + } +} + +/** + * Compute a transmission time lookup table + * @arg tc traffic control object + * @arg spec Rate specification + * @arg dst Destination buffer of RTNL_TC_RTABLE_SIZE uint32_t[]. + * + * Computes a table of RTNL_TC_RTABLE_SIZE entries specyfing the + * transmission times for various packet sizes, e.g. the transmission + * time for a packet of size \c pktsize could be looked up: + * @code + * txtime = table[pktsize >> log2(mtu)]; + * @endcode + */ +int rtnl_tc_build_rate_table(struct rtnl_tc *tc, struct rtnl_ratespec *spec, + uint32_t *dst) +{ + uint32_t mtu = rtnl_tc_get_mtu(tc); + uint32_t linktype = rtnl_tc_get_linktype(tc); + uint8_t cell_log = spec->rs_cell_log; + unsigned int size, i; + + spec->rs_mpu = rtnl_tc_get_mpu(tc); + spec->rs_overhead = rtnl_tc_get_overhead(tc); + + if (mtu == 0) + mtu = 2047; + + if (cell_log == UINT8_MAX) { + /* + * cell_log not specified, calculate it. It has to specify the + * minimum number of rshifts required to break the MTU to below + * RTNL_TC_RTABLE_SIZE. + */ + cell_log = 0; + while ((mtu >> cell_log) >= RTNL_TC_RTABLE_SIZE) + cell_log++; + } + + for (i = 0; i < RTNL_TC_RTABLE_SIZE; i++) { + size = adjust_size((i + 1) << cell_log, spec->rs_mpu, linktype); + dst[i] = nl_us2ticks(rtnl_tc_calc_txtime64(size, spec->rs_rate64)); + } + + spec->rs_cell_align = -1; + spec->rs_cell_log = cell_log; + + return 0; +} + +/** @} */ + +/** + * @name TC implementation of cache functions + */ + +void rtnl_tc_free_data(struct nl_object *obj) +{ + struct rtnl_tc *tc = TC_CAST(obj); + struct rtnl_tc_ops *ops; + + rtnl_link_put(tc->tc_link); + nl_data_free(tc->tc_opts); + nl_data_free(tc->tc_xstats); + + if (tc->tc_subdata) { + ops = rtnl_tc_get_ops(tc); + if (ops && ops->to_free_data) + ops->to_free_data(tc, nl_data_get(tc->tc_subdata)); + + nl_data_free(tc->tc_subdata); + } +} + +int rtnl_tc_clone(struct nl_object *dstobj, struct nl_object *srcobj) +{ + struct rtnl_tc *dst = TC_CAST(dstobj); + struct rtnl_tc *src = TC_CAST(srcobj); + struct rtnl_tc_ops *ops; + + dst->tc_opts = NULL; + dst->tc_xstats = NULL; + dst->tc_subdata = NULL; + dst->tc_link = NULL; + dst->tc_ops = NULL; + + if (src->tc_link) { + nl_object_get(OBJ_CAST(src->tc_link)); + dst->tc_link = src->tc_link; + } + + dst->ce_mask &= ~(TCA_ATTR_OPTS | + TCA_ATTR_XSTATS); + + if (src->tc_opts) { + dst->tc_opts = nl_data_clone(src->tc_opts); + if (!dst->tc_opts) + return -NLE_NOMEM; + dst->ce_mask |= TCA_ATTR_OPTS; + } + + if (src->tc_xstats) { + dst->tc_xstats = nl_data_clone(src->tc_xstats); + if (!dst->tc_xstats) + return -NLE_NOMEM; + dst->ce_mask |= TCA_ATTR_XSTATS; + } + + if (src->tc_subdata) { + if (!(dst->tc_subdata = nl_data_clone(src->tc_subdata))) { + return -NLE_NOMEM; + } + + /* Warning: if the data contains pointer, then at this point, dst->tc_subdata + * will alias those pointers. + * + * ops->to_clone() MUST fix that. + * + * If the type is actually "struct rtnl_act", then to_clone() must also + * fix dangling "a_next" pointer. */ + + ops = rtnl_tc_get_ops(src); + if (ops && ops->to_clone) { + return ops->to_clone(rtnl_tc_data(dst), rtnl_tc_data(src)); + } + } + + return 0; +} + +static int tc_dump(struct rtnl_tc *tc, enum nl_dump_type type, + struct nl_dump_params *p) +{ + struct rtnl_tc_type_ops *type_ops; + struct rtnl_tc_ops *ops; + void *data = rtnl_tc_data(tc); + + type_ops = tc_type_ops[tc->tc_type]; + if (type_ops && type_ops->tt_dump[type]) + type_ops->tt_dump[type](tc, p); + + ops = rtnl_tc_get_ops(tc); + if (ops && ops->to_dump[type]) { + ops->to_dump[type](tc, data, p); + return 1; + } + + return 0; +} + +void rtnl_tc_dump_line(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_tc_type_ops *type_ops; + struct rtnl_tc *tc = TC_CAST(obj); + struct nl_cache *link_cache; + char buf[32]; + + nl_new_line(p); + + type_ops = tc_type_ops[tc->tc_type]; + if (type_ops && type_ops->tt_dump_prefix) + nl_dump(p, "%s ", type_ops->tt_dump_prefix); + + nl_dump(p, "%s ", tc->tc_kind); + + if ((link_cache = nl_cache_mngt_require_safe("route/link"))) { + nl_dump(p, "dev %s ", + rtnl_link_i2name(link_cache, tc->tc_ifindex, + buf, sizeof(buf))); + } else + nl_dump(p, "dev %u ", tc->tc_ifindex); + + nl_dump(p, "id %s ", + rtnl_tc_handle2str(tc->tc_handle, buf, sizeof(buf))); + + nl_dump(p, "parent %s", + rtnl_tc_handle2str(tc->tc_parent, buf, sizeof(buf))); + + tc_dump(tc, NL_DUMP_LINE, p); + nl_dump(p, "\n"); + + if (link_cache) + nl_cache_put(link_cache); +} + +void rtnl_tc_dump_details(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_tc *tc = TC_CAST(obj); + + rtnl_tc_dump_line(OBJ_CAST(tc), p); + + nl_dump_line(p, " "); + + if (tc->ce_mask & TCA_ATTR_MTU) + nl_dump(p, " mtu %u", tc->tc_mtu); + + if (tc->ce_mask & TCA_ATTR_MPU) + nl_dump(p, " mpu %u", tc->tc_mpu); + + if (tc->ce_mask & TCA_ATTR_OVERHEAD) + nl_dump(p, " overhead %u", tc->tc_overhead); + + if (!tc_dump(tc, NL_DUMP_DETAILS, p)) + nl_dump(p, "no options"); + nl_dump(p, "\n"); +} + +void rtnl_tc_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_tc *tc = TC_CAST(obj); + char *unit; + float res; + + rtnl_tc_dump_details(OBJ_CAST(tc), p); + + nl_dump_line(p, + " stats: %-14s %-10s %-10s %-10s %-10s %-10s\n", + "bytes", "packets", "drops", "overlimits", "qlen", "backlog"); + + res = nl_cancel_down_bytes(tc->tc_stats[RTNL_TC_BYTES], &unit); + + nl_dump_line( + p, + " %10.2f %3s %10llu %-10llu %-10llu %-10llu %-10llu\n", + res, unit, (long long unsigned)tc->tc_stats[RTNL_TC_PACKETS], + (long long unsigned)tc->tc_stats[RTNL_TC_DROPS], + (long long unsigned)tc->tc_stats[RTNL_TC_OVERLIMITS], + (long long unsigned)tc->tc_stats[RTNL_TC_QLEN], + (long long unsigned)tc->tc_stats[RTNL_TC_BACKLOG]); + + res = nl_cancel_down_bytes(tc->tc_stats[RTNL_TC_RATE_BPS], &unit); + + nl_dump_line(p, " %10.2f %3s/s %10llu/s\n", res, unit, + (long long unsigned)tc->tc_stats[RTNL_TC_RATE_PPS]); +} + +uint64_t rtnl_tc_compare(struct nl_object *aobj, struct nl_object *bobj, + uint64_t attrs, int flags) +{ + struct rtnl_tc *a = TC_CAST(aobj); + struct rtnl_tc *b = TC_CAST(bobj); + uint64_t diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(TCA_ATTR_HANDLE, a->tc_handle != b->tc_handle); + diff |= _DIFF(TCA_ATTR_PARENT, a->tc_parent != b->tc_parent); + diff |= _DIFF(TCA_ATTR_IFINDEX, a->tc_ifindex != b->tc_ifindex); + diff |= _DIFF(TCA_ATTR_KIND, strcmp(a->tc_kind, b->tc_kind)); +#undef _DIFF + + return diff; +} + +/** @} */ + +/** + * @name Modules API + */ + +struct rtnl_tc_ops *rtnl_tc_lookup_ops(enum rtnl_tc_type type, const char *kind) +{ + struct rtnl_tc_ops *ops; + + nl_list_for_each_entry(ops, &tc_ops_list[type], to_list) + if (!strcmp(kind, ops->to_kind)) + return ops; + + return NULL; +} + +struct rtnl_tc_ops *rtnl_tc_get_ops(struct rtnl_tc *tc) +{ + if (!tc->tc_ops) + tc->tc_ops = rtnl_tc_lookup_ops(tc->tc_type, tc->tc_kind); + + return tc->tc_ops; +} + +/** + * Register a traffic control module + * @arg ops traffic control module operations + */ +int rtnl_tc_register(struct rtnl_tc_ops *ops) +{ + static int init = 0; + + /* + * Initialiation hack, make sure list is initialized when + * the first tc module registers. Putting this in a + * separate _nl_init would required correct ordering of init + * functions + */ + if (!init) { + int i; + + for (i = 0; i < __RTNL_TC_TYPE_MAX; i++) + nl_init_list_head(&tc_ops_list[i]); + + init = 1; + } + + if (!ops->to_kind || ops->to_type > RTNL_TC_TYPE_MAX) + BUG(); + + if (rtnl_tc_lookup_ops(ops->to_type, ops->to_kind)) + return -NLE_EXIST; + + nl_list_add_tail(&ops->to_list, &tc_ops_list[ops->to_type]); + + return 0; +} + +/** + * Unregister a traffic control module + * @arg ops traffic control module operations + */ +void rtnl_tc_unregister(struct rtnl_tc_ops *ops) +{ + nl_list_del(&ops->to_list); +} + +/** + * Returns the private data of the traffic control object. + * Contrary to rtnl_tc_data(), this returns NULL if the data is + * not yet allocated + * @arg tc traffic control object + * + * @return pointer to the private data or NULL if not allocated. + */ +void *rtnl_tc_data_peek(struct rtnl_tc *tc) +{ + return tc->tc_subdata ? nl_data_get(tc->tc_subdata) : NULL; +} + +/** + * Return pointer to private data of traffic control object + * @arg tc traffic control object + * + * Allocates the private traffic control object data section + * as necessary and returns it. + * + * @return Pointer to private tc data or NULL if allocation failed. + */ +void *rtnl_tc_data(struct rtnl_tc *tc) +{ + if (!tc->tc_subdata) { + size_t size; + + if (!tc->tc_ops) { + if (!rtnl_tc_get_ops(tc)) + return NULL; + } + + if (!(size = tc->tc_ops->to_size)) + BUG(); + + if (!(tc->tc_subdata = nl_data_alloc(NULL, size))) + return NULL; + } + + return nl_data_get(tc->tc_subdata); +} + +/** + * Check traffic control object type and return private data section + * @arg tc traffic control object + * @arg ops expected traffic control object operations + * @arg err the place where saves the error code if fails + * + * Checks whether the traffic control object matches the type + * specified with the traffic control object operations. If the + * type matches, the private tc object data is returned. If type + * mismatches, APPBUG() will print a application bug warning. + * + * @see rtnl_tc_data() + * + * @return Pointer to private tc data or NULL if type mismatches. + */ +void *rtnl_tc_data_check(struct rtnl_tc *tc, struct rtnl_tc_ops *ops, int *err) +{ + void *ret; + + if (tc->tc_ops != ops) { + char buf[64]; + + snprintf(buf, sizeof(buf), + "tc object %p used in %s context but is of type %s", + tc, ops->to_kind, tc->tc_ops->to_kind); + APPBUG(buf); + + if (err) + *err = -NLE_OPNOTSUPP; + return NULL; + } + + ret = rtnl_tc_data(tc); + if (ret == NULL) { + if (err) + *err = -NLE_NOMEM; + } + + return ret; +} + +struct nl_af_group tc_groups[] = { + { AF_UNSPEC, RTNLGRP_TC }, + { END_OF_GROUP_LIST }, +}; + + +void rtnl_tc_type_register(struct rtnl_tc_type_ops *ops) +{ + if (ops->tt_type > RTNL_TC_TYPE_MAX) + BUG(); + + tc_type_ops[ops->tt_type] = ops; +} + +void rtnl_tc_type_unregister(struct rtnl_tc_type_ops *ops) +{ + if (ops->tt_type > RTNL_TC_TYPE_MAX) + BUG(); + + tc_type_ops[ops->tt_type] = NULL; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/socket.c b/libnl/lib/socket.c new file mode 100644 index 0000000..4e64cbb --- /dev/null +++ b/libnl/lib/socket.c @@ -0,0 +1,969 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup core_types + * @defgroup socket Socket + * + * Representation of a netlink socket + * + * Related sections in the development guide: + * - @core_doc{core_sockets, Netlink Sockets} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#include "nl-default.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "nl-core.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-aux-core/nl-core.h" + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + +static int default_cb = NL_CB_DEFAULT; + +static void _nl_init init_default_cb(void) +{ + char *nlcb; + + if ((nlcb = getenv("NLCB"))) { + if (!strcasecmp(nlcb, "default")) + default_cb = NL_CB_DEFAULT; + else if (!strcasecmp(nlcb, "verbose")) + default_cb = NL_CB_VERBOSE; + else if (!strcasecmp(nlcb, "debug")) + default_cb = NL_CB_DEBUG; + else { + fprintf(stderr, "Unknown value for NLCB, valid values: " + "{default | verbose | debug}\n"); + } + } +} + +static uint32_t _badrandom_from_time(void) +{ + uint32_t result; + uint64_t v64; + time_t t; + + t = time(NULL); + v64 = (uint64_t)t; + result = (uint32_t)v64; + + /* XOR with the upper bits. Otherwise, coverity warns about only + * considering 32 bit from time_t. Use the inverse, so that for the + * most part the bits don't change. */ + result ^= (~(v64 >> 32)); + + return result; +} + +static uint32_t used_ports_map[32]; +static NL_RW_LOCK(port_map_lock); + +static uint32_t generate_local_port(void) +{ + int i, j, m; + uint32_t n; + static uint16_t idx_state = 0; + uint32_t pid = getpid() & 0x3FFFFF; + + nl_write_lock(&port_map_lock); + + if (idx_state == 0) { + uint32_t t = _badrandom_from_time(); + + /* from time to time (on average each 2^15 calls), the idx_state will + * be zero again. No problem, just "seed" anew with time(). */ + idx_state = t ^ (t >> 16) ^ 0x3047; + } else + idx_state = idx_state + 20011; /* add prime number */ + + i = idx_state >> 5; + n = idx_state; + for (j = 0; j < 32; j++) { + /* walk the index somewhat randomized, with always leaving the block + * #0 as last. The reason is that libnl-1 will start at block #0, + * so just leave the first 32 ports preferably for libnl-1 owned sockets + * (this is relevant only if the applications ends up using both versions + * of the library and doesn't hurt otherwise). */ + if (j == 31) + i = 0; + else + i = (((i-1) + 7) % 31) + 1; + + if (used_ports_map[i] == 0xFFFFFFFF) + continue; + + for (m = 0; m < 32; m++) { + n = (n + 13u) % 32u; + if (1UL & (used_ports_map[i] >> n)) + continue; + + used_ports_map[i] |= (1UL << n); + n += (i * 32); + + /* PID_MAX_LIMIT is currently at 2^22, leaving 10 bit + * to, i.e. 1024 unique ports per application. */ + + nl_write_unlock(&port_map_lock); + + /* ensure we don't return zero. */ + pid = pid + (n << 22); + return pid ? pid : 1024; + } + } + + nl_write_unlock(&port_map_lock); + return 0; +} + +static void release_local_port(uint32_t port) +{ + int nr; + uint32_t mask; + + BUG_ON(port == 0); + + nr = port >> 22; + mask = 1UL << (nr % 32); + nr /= 32; + + nl_write_lock(&port_map_lock); + BUG_ON((used_ports_map[nr] & mask) != mask); + used_ports_map[nr] &= ~mask; + nl_write_unlock(&port_map_lock); +} + +/** \cond skip */ +void _nl_socket_used_ports_release_all(const uint32_t *used_ports) +{ + int i; + + for (i = 0; i < 32; i++) { + if (used_ports[i] != 0) { + nl_write_lock(&port_map_lock); + for (; i < 32; i++) { + BUG_ON((used_ports_map[i] & used_ports[i]) != used_ports[i]); + used_ports_map[i] &= ~(used_ports[i]); + } + nl_write_unlock(&port_map_lock); + return; + } + } +} + +void _nl_socket_used_ports_set(uint32_t *used_ports, uint32_t port) +{ + int nr; + int32_t mask; + + nr = port >> 22; + mask = 1UL << (nr % 32); + nr /= 32; + + /* + BUG_ON(port == 0 || (getpid() & 0x3FFFFF) != (port & 0x3FFFFF)); + BUG_ON(used_ports[nr] & mask); + */ + + used_ports[nr] |= mask; +} +/** \endcond */ + +/** + * @name Allocation + * @{ + */ + +static struct nl_sock *__alloc_socket(struct nl_cb *cb) +{ + struct nl_sock *sk; + + sk = calloc(1, sizeof(*sk)); + if (!sk) + return NULL; + + sk->s_fd = -1; + sk->s_cb = nl_cb_get(cb); + sk->s_local.nl_family = AF_NETLINK; + sk->s_peer.nl_family = AF_NETLINK; + sk->s_seq_next = _badrandom_from_time(); + sk->s_seq_expect = sk->s_seq_next; + + /* the port is 0 (unspecified), meaning NL_OWN_PORT */ + sk->s_flags = NL_OWN_PORT; + + return sk; +} + +/** + * Allocate new netlink socket + * + * @return Newly allocated netlink socket or NULL. + */ +struct nl_sock *nl_socket_alloc(void) +{ + struct nl_cb *cb; + struct nl_sock *sk; + + cb = nl_cb_alloc(default_cb); + if (!cb) + return NULL; + + /* will increment cb reference count on success */ + sk = __alloc_socket(cb); + + nl_cb_put(cb); + + return sk; +} + +/** + * Allocate new socket with custom callbacks + * @arg cb Callback handler + * + * The reference to the callback handler is taken into account + * automatically, it is released again upon calling nl_socket_free(). + * + *@return Newly allocted socket handle or NULL. + */ +struct nl_sock *nl_socket_alloc_cb(struct nl_cb *cb) +{ + if (cb == NULL) + BUG(); + + return __alloc_socket(cb); +} + +/** + * Free a netlink socket. + * @arg sk Netlink socket. + */ +void nl_socket_free(struct nl_sock *sk) +{ + if (!sk) + return; + + if (sk->s_fd >= 0) + close(sk->s_fd); + + if (!(sk->s_flags & NL_OWN_PORT)) + release_local_port(sk->s_local.nl_pid); + + nl_cb_put(sk->s_cb); + free(sk); +} + +/** @} */ + +/** + * @name Sequence Numbers + * @{ + */ + +static int noop_seq_check(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + + +/** + * Disable sequence number checking. + * @arg sk Netlink socket. + * + * Disables checking of sequence numbers on the netlink socket This is + * required to allow messages to be processed which were not requested by + * a preceding request message, e.g. netlink events. + * + * @note This function modifies the NL_CB_SEQ_CHECK configuration in + * the callback handle associated with the socket. + */ +void nl_socket_disable_seq_check(struct nl_sock *sk) +{ + nl_cb_set(sk->s_cb, NL_CB_SEQ_CHECK, + NL_CB_CUSTOM, noop_seq_check, NULL); +} + +/** + * Use next sequence number + * @arg sk Netlink socket. + * + * Uses the next available sequence number and increases the counter + * by one for subsequent calls. + * + * @return Unique serial sequence number + */ +unsigned int nl_socket_use_seq(struct nl_sock *sk) +{ + if (sk->s_seq_next == UINT_MAX) { + sk->s_seq_next = 0; + return UINT_MAX; + } + return sk->s_seq_next++; +} + +/** + * Disable automatic request for ACK + * @arg sk Netlink socket. + * + * The default behaviour of a socket is to request an ACK for + * each message sent to allow for the caller to synchronize to + * the completion of the netlink operation. This function + * disables this behaviour and will result in requests being + * sent which will not have the NLM_F_ACK flag set automatically. + * However, it is still possible for the caller to set the + * NLM_F_ACK flag explicitely. + */ +void nl_socket_disable_auto_ack(struct nl_sock *sk) +{ + sk->s_flags |= NL_NO_AUTO_ACK; +} + +/** + * Enable automatic request for ACK (default) + * @arg sk Netlink socket. + * @see nl_socket_disable_auto_ack + */ +void nl_socket_enable_auto_ack(struct nl_sock *sk) +{ + sk->s_flags &= ~NL_NO_AUTO_ACK; +} + +/** @} */ + +/** \cond skip */ +int _nl_socket_is_local_port_unspecified(struct nl_sock *sk) +{ + return (sk->s_local.nl_pid == 0); +} + +uint32_t _nl_socket_set_local_port_no_release(struct nl_sock *sk, int generate_other) +{ + uint32_t port; + + /* reset the port to generate_local_port(), but do not release + * the previously generated port. */ + + if (generate_other) + port = generate_local_port(); + else + port = 0; + sk->s_local.nl_pid = port; + if (port == 0) { + /* failed to find an unsed port. Restore the socket to have an + * unspecified port. */ + sk->s_flags |= NL_OWN_PORT; + } else + sk->s_flags &= ~NL_OWN_PORT; + return port; +} +/** \endcond */ + +/** + * @name Source Idenficiation + * @{ + */ + +uint32_t nl_socket_get_local_port(const struct nl_sock *sk) +{ + if (sk->s_local.nl_pid == 0) { + struct nl_sock *sk_mutable = (struct nl_sock *) sk; + + /* modify the const argument sk. This is justified, because + * nobody ever saw the local_port from externally. So, we + * initilize it on first use. + * + * Note that this also means that you cannot call this function + * from multiple threads without synchronization. But nl_sock + * is not automatically threadsafe anyway, so the user is not + * allowed to do that. + */ + sk_mutable->s_local.nl_pid = generate_local_port(); + if (sk_mutable->s_local.nl_pid == 0) { + /* could not generate a local port. Assign UINT32_MAX to preserve + * backward compatibility. A user who cares can clear that anyway + * with nl_socket_set_local_port(). */ + sk_mutable->s_local.nl_pid = UINT32_MAX; + sk_mutable->s_flags |= NL_OWN_PORT; + } else + sk_mutable->s_flags &= ~NL_OWN_PORT; + } + return sk->s_local.nl_pid; +} + +/** + * Set local port of socket + * @arg sk Netlink socket. + * @arg port Local port identifier + * + * Assigns a local port identifier to the socket. + * + * If port is 0, the port is reset to 'unspecified' as it is after newly + * calling nl_socket_alloc(). + * Unspecified means, that the port will be generated automatically later + * on first use (either on nl_socket_get_local_port() or nl_connect()). + */ +void nl_socket_set_local_port(struct nl_sock *sk, uint32_t port) +{ + if (!(sk->s_flags & NL_OWN_PORT)) + release_local_port(sk->s_local.nl_pid); + sk->s_flags |= NL_OWN_PORT; + sk->s_local.nl_pid = port; +} + +/** @} */ + +/** + * @name Group Subscriptions + * @{ + */ + +/** + * Join groups + * @arg sk Netlink socket + * @arg group Group identifier + * + * Joins the specified groups using the modern socket option which + * is available since kernel version 2.6.14. It allows joining an + * almost arbitary number of groups without limitation. The list + * of groups has to be terminated by 0 (%NFNLGRP_NONE). + * + * Make sure to use the correct group definitions as the older + * bitmask definitions for nl_join_groups() are likely to still + * be present for backward compatibility reasons. + * + * @return 0 on success or a negative error code. + */ +int nl_socket_add_memberships(struct nl_sock *sk, int group, ...) +{ + int err; + va_list ap; + + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + va_start(ap, group); + + while (group != 0) { + if (group < 0) { + va_end(ap); + return -NLE_INVAL; + } + + err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, + &group, sizeof(group)); + if (err < 0) { + va_end(ap); + NL_DBG(4, "nl_socket_add_memberships(%p): setsockopt() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + group = va_arg(ap, int); + } + + va_end(ap); + + return 0; +} + +int nl_socket_add_membership(struct nl_sock *sk, int group) +{ + return nl_socket_add_memberships(sk, group, 0); +} + +/** + * Leave groups + * @arg sk Netlink socket + * @arg group Group identifier + * + * Leaves the specified groups using the modern socket option + * which is available since kernel version 2.6.14. The list of groups + * has to terminated by 0 (%NFNLGRP_NONE). + * + * @see nl_socket_add_membership + * @return 0 on success or a negative error code. + */ +int nl_socket_drop_memberships(struct nl_sock *sk, int group, ...) +{ + int err; + va_list ap; + + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + va_start(ap, group); + + while (group != 0) { + if (group < 0) { + va_end(ap); + return -NLE_INVAL; + } + + err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, + &group, sizeof(group)); + if (err < 0) { + va_end(ap); + NL_DBG(4, "nl_socket_drop_memberships(%p): setsockopt() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + group = va_arg(ap, int); + } + + va_end(ap); + + return 0; +} + +int nl_socket_drop_membership(struct nl_sock *sk, int group) +{ + return nl_socket_drop_memberships(sk, group, 0); +} + + +/** + * Join multicast groups (deprecated) + * @arg sk Netlink socket. + * @arg groups Bitmask of groups to join. + * + * This function defines the old way of joining multicast group which + * has to be done prior to calling nl_connect(). It works on any kernel + * version but is very limited as only 32 groups can be joined. + */ +void nl_join_groups(struct nl_sock *sk, int groups) +{ + sk->s_local.nl_groups |= groups; +} + + +/** @} */ + +/** + * @name Peer Identfication + * @{ + */ + +uint32_t nl_socket_get_peer_port(const struct nl_sock *sk) +{ + return sk->s_peer.nl_pid; +} + +void nl_socket_set_peer_port(struct nl_sock *sk, uint32_t port) +{ + sk->s_peer.nl_pid = port; +} + +uint32_t nl_socket_get_peer_groups(const struct nl_sock *sk) +{ + return sk->s_peer.nl_groups; +} + +void nl_socket_set_peer_groups(struct nl_sock *sk, uint32_t groups) +{ + sk->s_peer.nl_groups = groups; +} + + + +/** @} */ + +/** + * @name File Descriptor + * @{ + */ + +/** + * Return the file descriptor of the backing socket + * @arg sk Netlink socket + * + * Only valid after calling nl_connect() to create and bind the respective + * socket. + * + * @return File descriptor or -1 if not available. + */ +int nl_socket_get_fd(const struct nl_sock *sk) +{ + return sk->s_fd; +} + +/** + * Set the socket file descriptor externally which initializes the + * socket similar to nl_connect(). + * + * @arg sk Netlink socket (required) + * @arg protocol The socket protocol (optional). Linux 2.6.32 supports + * the socket option SO_PROTOCOL. In this case, you can set + * protocol to a negative value and let it autodetect. + * If you set it to a non-negative value, the detected protocol + * must match the one provided. + * To support older kernels, you must specify the protocol. + * @arg fd Socket file descriptor to use (required) + * + * Set the socket file descriptor. @fd must be valid and bind'ed. + * + * This is an alternative to nl_connect(). nl_connect() creates, binds and + * sets the socket. With this function you can set the socket to an externally + * created file descriptor. + * + * @see nl_connect() + * + * @return 0 on success or a negative error code. On error, @fd is not closed but + * possibly unusable. + * + * @retval -NLE_BAD_SOCK Netlink socket is already connected + * @retval -NLE_INVAL Socket is of unexpected type + */ +int nl_socket_set_fd(struct nl_sock *sk, int protocol, int fd) +{ + int err = 0; + socklen_t addrlen; + struct sockaddr_nl local = { 0 }; + int so_type = -1, so_protocol = -1; + + if (sk->s_fd != -1) + return -NLE_BAD_SOCK; + if (fd < 0) + return -NLE_INVAL; + + addrlen = sizeof(local); + err = getsockname(fd, (struct sockaddr *) &local, + &addrlen); + if (err < 0) { + NL_DBG(4, "nl_socket_set_fd(%p,%d): getsockname() failed with %d (%s)\n", + sk, fd, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + if (addrlen != sizeof(local)) + return -NLE_INVAL; + if (local.nl_family != AF_NETLINK) { + NL_DBG(4, "nl_socket_set_fd(%p,%d): getsockname() returned family %d instead of %d (AF_NETLINK)\n", + sk, fd, local.nl_family, AF_NETLINK); + return -NLE_INVAL; + } + + addrlen = sizeof(so_type); + err = getsockopt(fd, SOL_SOCKET, SO_TYPE, &so_type, &addrlen); + if (err < 0) { + NL_DBG(4, "nl_socket_set_fd(%p,%d): getsockopt() for SO_TYPE failed with %d (%s)\n", + sk, fd, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + if (addrlen != sizeof(so_type)) + return -NLE_INVAL; + if (so_type != SOCK_RAW) { + NL_DBG(4, "nl_socket_set_fd(%p,%d): getsockopt() returned SO_TYPE %d instead of %d (SOCK_RAW)\n", + sk, fd, so_type, SOCK_RAW); + return -NLE_INVAL; + } + +#if SO_PROTOCOL + addrlen = sizeof(so_protocol); + err = getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &so_protocol, &addrlen); + if (err < 0) { + if (errno == ENOPROTOOPT) + goto no_so_protocol; + NL_DBG(4, "nl_socket_set_fd(%p,%d): getsockopt() for SO_PROTOCOL failed with %d (%s)\n", + sk, fd, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + if (addrlen != sizeof(so_protocol)) + return -NLE_INVAL; + if (protocol >= 0 && protocol != so_protocol) { + NL_DBG(4, "nl_socket_set_fd(%p,%d): getsockopt() for SO_PROTOCOL returned %d instead of %d\n", + sk, fd, so_protocol, protocol); + return -NLE_INVAL; + } + + if (0) +#endif + { +no_so_protocol: + if (protocol < 0) { + NL_DBG(4, "nl_socket_set_fd(%p,%d): unknown protocol and unable to detect it via SO_PROTOCOL socket option\n", + sk, fd); + return -NLE_INVAL; + } + so_protocol = protocol; + } + + nl_socket_set_local_port (sk, local.nl_pid); + sk->s_local = local; + sk->s_fd = fd; + sk->s_proto = so_protocol; + + return 0; +} + +/** + * Set file descriptor of socket to non-blocking state + * @arg sk Netlink socket. + * + * @return 0 on success or a negative error code. + */ +int nl_socket_set_nonblocking(const struct nl_sock *sk) +{ + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0) { + NL_DBG(4, "nl_socket_set_nonblocking(%p): fcntl() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + return 0; +} + +/** + * Enable use of MSG_PEEK when reading from socket + * @arg sk Netlink socket. + * + * See also NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT capability + */ +void nl_socket_enable_msg_peek(struct nl_sock *sk) +{ + sk->s_flags |= (NL_MSG_PEEK | NL_MSG_PEEK_EXPLICIT); +} + +/** + * Disable use of MSG_PEEK when reading from socket + * @arg sk Netlink socket. + * + * See also NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT capability + */ +void nl_socket_disable_msg_peek(struct nl_sock *sk) +{ + sk->s_flags |= NL_MSG_PEEK_EXPLICIT; + sk->s_flags &= ~NL_MSG_PEEK; +} + +/** @} */ + +/** + * @name Callback Handler + * @{ + */ + +struct nl_cb *nl_socket_get_cb(const struct nl_sock *sk) +{ + return nl_cb_get(sk->s_cb); +} + +void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb) +{ + if (cb == NULL) + BUG(); + + nl_cb_put(sk->s_cb); + sk->s_cb = nl_cb_get(cb); +} + +/** + * Modify the callback handler associated with the socket + * @arg sk Netlink socket. + * @arg type which type callback to set + * @arg kind kind of callback + * @arg func callback function + * @arg arg argument to be passed to callback function + * + * @see nl_cb_set + */ +int nl_socket_modify_cb(struct nl_sock *sk, enum nl_cb_type type, + enum nl_cb_kind kind, nl_recvmsg_msg_cb_t func, + void *arg) +{ + return nl_cb_set(sk->s_cb, type, kind, func, arg); +} + +/** + * Modify the error callback handler associated with the socket + * @arg sk Netlink socket. + * @arg kind kind of callback + * @arg func callback function + * @arg arg argument to be passed to callback function + * + * @see nl_cb_err + */ +int nl_socket_modify_err_cb(struct nl_sock *sk, enum nl_cb_kind kind, + nl_recvmsg_err_cb_t func, void *arg) +{ + return nl_cb_err(sk->s_cb, kind, func, arg); +} + +/** @} */ + +/** + * @name Utilities + * @{ + */ + +/** + * Set socket buffer size of netlink socket. + * @arg sk Netlink socket. + * @arg rxbuf New receive socket buffer size in bytes. + * @arg txbuf New transmit socket buffer size in bytes. + * + * Sets the socket buffer size of a netlink socket to the specified + * values \c rxbuf and \c txbuf. Providing a value of \c 0 assumes a + * good default value. + * + * @note It is not required to call this function prior to nl_connect(). + * @return 0 on success or a negative error code. + */ +int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf) +{ + int err; + + if (rxbuf <= 0) + rxbuf = 32768; + + if (txbuf <= 0) + txbuf = 32768; + + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + err = setsockopt(sk->s_fd, SOL_SOCKET, SO_SNDBUF, + &txbuf, sizeof(txbuf)); + if (err < 0) { + NL_DBG(4, "nl_socket_set_buffer_size(%p): setsockopt() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + err = setsockopt(sk->s_fd, SOL_SOCKET, SO_RCVBUF, + &rxbuf, sizeof(rxbuf)); + if (err < 0) { + NL_DBG(4, "nl_socket_set_buffer_size(%p): setsockopt() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + return 0; +} + +/** + * Set default message buffer size of netlink socket. + * @arg sk Netlink socket. + * @arg bufsize Default message buffer size in bytes. + * + * Sets the default message buffer size to the specified length in bytes. + * The default message buffer size limits the maximum message size the + * socket will be able to receive. It is generally recommneded to specify + * a buffer size no less than the size of a memory page. + * + * Setting the @bufsize to zero means to use a default of 4 times getpagesize(). + * + * When MSG_PEEK is enabled, the buffer size is used for the initial choice + * of the buffer while peeking. It still makes sense to choose an optimal value + * to avoid realloc(). + * + * When MSG_PEEK is disabled, the buffer size is important because a too small + * size will lead to failure of receiving the message via nl_recvmsgs(). + * + * By default, MSG_PEEK is enabled unless the user calls either nl_socket_disable_msg_peek()/ + * nl_socket_enable_msg_peek() or sets the message buffer size to a positive value. + * See capability NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT for that. + * + * @return 0 on success or a negative error code. + */ +int nl_socket_set_msg_buf_size(struct nl_sock *sk, size_t bufsize) +{ + sk->s_bufsize = bufsize; + + return 0; +} + +/** + * Get default message buffer size of netlink socket. + * @arg sk Netlink socket. + * + * @return Size of default message buffer. + */ +size_t nl_socket_get_msg_buf_size(struct nl_sock *sk) +{ + return sk->s_bufsize; +} + +/** + * Enable/disable credential passing on netlink socket. + * @arg sk Netlink socket. + * @arg state New state (0 - disabled, 1 - enabled) + * + * @return 0 on success or a negative error code + */ +int nl_socket_set_passcred(struct nl_sock *sk, int state) +{ + int err; + + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + err = setsockopt(sk->s_fd, SOL_SOCKET, SO_PASSCRED, + &state, sizeof(state)); + if (err < 0) { + NL_DBG(4, "nl_socket_set_passcred(%p): setsockopt() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + if (state) + sk->s_flags |= NL_SOCK_PASSCRED; + else + sk->s_flags &= ~NL_SOCK_PASSCRED; + + return 0; +} + +/** + * Enable/disable receival of additional packet information + * @arg sk Netlink socket. + * @arg state New state (0 - disabled, 1 - enabled) + * + * @return 0 on success or a negative error code + */ +int nl_socket_recv_pktinfo(struct nl_sock *sk, int state) +{ + int err; + + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_PKTINFO, + &state, sizeof(state)); + if (err < 0) { + NL_DBG(4, "nl_socket_recv_pktinfo(%p): setsockopt() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + return 0; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/utils.c b/libnl/lib/utils.c new file mode 100644 index 0000000..1cee863 --- /dev/null +++ b/libnl/lib/utils.c @@ -0,0 +1,1319 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup core + * @defgroup utils Utilities + * + * Collection of helper functions + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#include "nl-default.h" + +#include + +#include +#include + +#include +#include + +#include "nl-core.h" +#include "nl-priv-dynamic-core/object-api.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-aux-core/nl-core.h" + +/** + * Global variable indicating the desired level of debugging output. + * + * Level | Messages Printed + * ----- | --------------------------------------------------------- + * 0 | Debugging output disabled + * 1 | Warnings, important events and notifications + * 2 | More or less important debugging messages + * 3 | Repetitive events causing a flood of debugging messages + * 4 | Even less important messages + * + * If available, the variable will be initialized to the value of the + * environment variable `NLDBG`. The default value is 0 (disabled). + * + * For more information, see section @core_doc{_debugging, Debugging}. + */ +int nl_debug = 0; + +/** @cond SKIP */ +struct nl_dump_params nl_debug_dp = { + .dp_type = NL_DUMP_DETAILS, +}; + +static void _nl_init nl_debug_init(void) +{ + char *nldbg, *end; + + if (NL_DEBUG && (nldbg = getenv("NLDBG"))) { + long level = strtol(nldbg, &end, 0); + if (nldbg != end) + nl_debug = level; + } + + nl_debug_dp.dp_fd = stderr; +} + +int __nl_read_num_str_file(const char *path, int (*cb)(long, const char *)) +{ + FILE *fd; + char buf[128]; + + fd = fopen(path, "re"); + if (fd == NULL) + return -nl_syserr2nlerr(errno); + + while (fgets(buf, sizeof(buf), fd)) { + int goodlen, err; + long num; + char *end; + + if (*buf == '#' || *buf == '\n' || *buf == '\r') + continue; + + num = strtol(buf, &end, 0); + if (end == buf) { + fclose(fd); + return -NLE_INVAL; + } + + if (num == LONG_MIN || num == LONG_MAX) { + fclose(fd); + return -NLE_RANGE; + } + + while (*end == ' ' || *end == '\t') + end++; + + goodlen = strcspn(end, "#\r\n\t "); + if (goodlen == 0) { + fclose(fd); + return -NLE_INVAL; + } + + end[goodlen] = '\0'; + + err = cb(num, end); + if (err < 0) { + fclose(fd); + return err; + } + } + + fclose(fd); + + return 0; +} + +struct trans_list { + int i; + char *a; + struct nl_list_head list; +}; + +int nl_getprotobyname(const char *name) +{ + const struct protoent *result; + +#if HAVE_DECL_GETPROTOBYNAME_R + struct protoent result_buf; + char buf[2048]; + int r; + + r = getprotobyname_r(name, &result_buf, buf, sizeof(buf), + (struct protoent **)&result); + if (r != 0 || result != &result_buf) + result = NULL; +#else + result = getprotobyname(name); +#endif + + if (!result) + return -1; + + if (result->p_proto < 0 || result->p_proto > UINT8_MAX) + return -1; + return (uint8_t)result->p_proto; +} + +bool nl_getprotobynumber(int proto, char *out_name, size_t name_len) +{ + const struct protoent *result; + +#if HAVE_DECL_GETPROTOBYNUMBER_R + struct protoent result_buf; + char buf[2048]; + int r; + + r = getprotobynumber_r(proto, &result_buf, buf, sizeof(buf), + (struct protoent **)&result); + if (r != 0 || result != &result_buf) + result = NULL; +#else + result = getprotobynumber(proto); +#endif + + if (!result) + return false; + + if (strlen(result->p_name) >= name_len) + return false; + strcpy(out_name, result->p_name); + return true; +} + +const char *nl_strerror_l(int err) +{ + const char *buf; +#ifdef HAVE_STRERROR_L + int errno_save = errno; + locale_t loc = newlocale(LC_MESSAGES_MASK, "", (locale_t)0); + + if (loc == (locale_t)0) { + if (errno == ENOENT) + loc = newlocale(LC_MESSAGES_MASK, + "POSIX", (locale_t)0); + } + if (loc != (locale_t)0) { + buf = strerror_l(err, loc); + freelocale(loc); + } else { + buf = "newlocale() failed"; + } + + errno = errno_save; +#else + buf = strerror(err); +#endif + return buf; +} +/** @endcond */ + +/** + * @name Pretty Printing of Numbers + * @{ + */ + +/** + * Cancel down a byte counter + * @arg l byte counter + * @arg unit destination unit pointer + * + * Cancels down a byte counter until it reaches a reasonable + * unit. The chosen unit is assigned to \a unit. + * This function assume 1024 bytes in one kilobyte + * + * @return The cancelled down byte counter in the new unit. + */ +double nl_cancel_down_bytes(unsigned long long l, char **unit) +{ + if (l >= 1099511627776LL) { + *unit = "TiB"; + return ((double) l) / 1099511627776LL; + } else if (l >= 1073741824) { + *unit = "GiB"; + return ((double) l) / 1073741824; + } else if (l >= 1048576) { + *unit = "MiB"; + return ((double) l) / 1048576; + } else if (l >= 1024) { + *unit = "KiB"; + return ((double) l) / 1024; + } else { + *unit = "B"; + return (double) l; + } +} + +/** + * Cancel down a bit counter + * @arg l bit counter + * @arg unit destination unit pointer + * + * Cancels down bit counter until it reaches a reasonable + * unit. The chosen unit is assigned to \a unit. + * This function assume 1000 bits in one kilobit + * + * @return The cancelled down bit counter in the new unit. + */ +double nl_cancel_down_bits(unsigned long long l, char **unit) +{ + if (l >= 1000000000000ULL) { + *unit = "Tbit"; + return ((double) l) / 1000000000000ULL; + } + + if (l >= 1000000000) { + *unit = "Gbit"; + return ((double) l) / 1000000000; + } + + if (l >= 1000000) { + *unit = "Mbit"; + return ((double) l) / 1000000; + } + + if (l >= 1000) { + *unit = "Kbit"; + return ((double) l) / 1000; + } + + *unit = "bit"; + return (double) l; +} + +int nl_rate2str(unsigned long long rate, int type, char *buf, size_t len) +{ + char *unit; + double frac; + + switch (type) { + case NL_BYTE_RATE: + frac = nl_cancel_down_bytes(rate, &unit); + break; + + case NL_BIT_RATE: + frac = nl_cancel_down_bits(rate, &unit); + break; + + default: + BUG(); + } + + return snprintf(buf, len, "%.2f%s/s", frac, unit); +} + +/** + * Cancel down a micro second value + * @arg l micro seconds + * @arg unit destination unit pointer + * + * Cancels down a microsecond counter until it reaches a + * reasonable unit. The chosen unit is assigned to \a unit. + * + * @return The cancelled down microsecond in the new unit + */ +double nl_cancel_down_us(uint32_t l, char **unit) +{ + if (l >= 1000000) { + *unit = "s"; + return ((double) l) / 1000000; + } else if (l >= 1000) { + *unit = "ms"; + return ((double) l) / 1000; + } else { + *unit = "us"; + return (double) l; + } +} + +/** @} */ + +/** + * @name Generic Unit Translations + * @{ + */ + +/** + * Convert a character string to a size + * @arg str size encoded as character string + * + * Converts the specified size as character to the corresponding + * number of bytes. + * + * Supported formats are: + * - b,kb/k,m/mb,gb/g for bytes + * - bit,kbit/mbit/gbit + * + * This function assume 1000 bits in one kilobit and + * 1024 bytes in one kilobyte + * + * @return The number of bytes or -1 if the string is unparseable + */ +long nl_size2int(const char *str) +{ + char *p; + long l = strtol(str, &p, 0); + if (p == str) + return -NLE_INVAL; + + if (*p) { + if (!strcasecmp(p, "kb") || !strcasecmp(p, "k")) + l *= 1024; + else if (!strcasecmp(p, "gb") || !strcasecmp(p, "g")) + l *= 1024*1024*1024; + else if (!strcasecmp(p, "gbit")) + l *= 1000000000L/8; + else if (!strcasecmp(p, "mb") || !strcasecmp(p, "m")) + l *= 1024*1024; + else if (!strcasecmp(p, "mbit")) + l *= 1000000/8; + else if (!strcasecmp(p, "kbit")) + l *= 1000/8; + else if (!strcasecmp(p, "bit")) + l /= 8; + else if (strcasecmp(p, "b") != 0) + return -NLE_INVAL; + } + + return l; +} + +static const struct { + double limit; + const char *unit; +} size_units[] = { + { 1024. * 1024. * 1024. * 1024. * 1024., "EiB" }, + { 1024. * 1024. * 1024. * 1024., "TiB" }, + { 1024. * 1024. * 1024., "GiB" }, + { 1024. * 1024., "MiB" }, + { 1024., "KiB" }, + { 0., "B" }, +}; + +/** + * Convert a size toa character string + * @arg size Size in number of bytes + * @arg buf Buffer to write character string to + * @arg len Size of buf + * + * This function converts a value in bytes to a human readable representation + * of it. The function uses IEC prefixes: + * + * @code + * 1024 bytes => 1 KiB + * 1048576 bytes => 1 MiB + * @endcode + * + * The highest prefix is used which ensures a result of >= 1.0, the result + * is provided as floating point number with a maximum precision of 2 digits: + * @code + * 965176 bytes => 942.55 KiB + * @endcode + * + * @return pointer to buf + */ +char *nl_size2str(const size_t size, char *buf, const size_t len) +{ + size_t i; + + if (size == 0) { + snprintf(buf, len, "0B"); + return buf; + } + + for (i = 0; i < ARRAY_SIZE(size_units); i++) { + if (size >= size_units[i].limit) { + snprintf(buf, len, "%.2g%s", + (double) size / size_units[i].limit, + size_units[i].unit); + return buf; + } + } + + BUG(); +} + +/** + * Convert a character string to a probability + * @arg str probability encoded as character string + * + * Converts the specified probability as character to the + * corresponding probability number. + * + * Supported formats are: + * - 0.0-1.0 + * - 0%-100% + * + * @return The probability relative to NL_PROB_MIN and NL_PROB_MAX + */ +long nl_prob2int(const char *str) +{ + char *p; + double d = strtod(str, &p); + + if (p == str) + return -NLE_INVAL; + + if (d > 1.0) + d /= 100.0f; + + if (d > 1.0f || d < 0.0f) + return -NLE_RANGE; + + if (*p && strcmp(p, "%") != 0) + return -NLE_INVAL; + + return (long) (((d * NL_PROB_MAX) + 0.5)); +} + +/** @} */ + +/** + * @name Time Translations + * @{ + */ + +#ifndef USER_HZ +#define USER_HZ 100 +#endif + +static uint32_t user_hz = USER_HZ; +static uint32_t psched_hz = USER_HZ; + +static double ticks_per_usec = 1.0f; + +/* Retrieves the configured HZ and ticks/us value in the kernel. + * The value is cached. Supported ways of getting it: + * + * 1) environment variable + * 2) /proc/net/psched and sysconf + * + * Supports the environment variables: + * PROC_NET_PSCHED - may point to psched file in /proc + * PROC_ROOT - may point to /proc fs */ +static void get_psched_settings(void) +{ + char name[FILENAME_MAX]; + FILE *fd; + int got_hz = 0; + static volatile int initialized = 0; + const char *ev; + NL_LOCK(mutex); + + if (initialized == 1) + return; + + nl_lock(&mutex); + + if (initialized == 1) + return; + + if ((ev = getenv("HZ"))) { + long hz = strtol(ev, NULL, 0); + + if (LONG_MIN != hz && LONG_MAX != hz) { + user_hz = hz; + got_hz = 1; + } + } + + if (!got_hz) + user_hz = sysconf(_SC_CLK_TCK); + + psched_hz = user_hz; + + if ((ev = getenv("TICKS_PER_USEC"))) { + double t = strtod(ev, NULL); + ticks_per_usec = t; + } + else { + if ((ev = getenv("PROC_NET_PSCHED"))) + snprintf(name, sizeof(name), "%s", ev); + else if ((ev = getenv("PROC_ROOT"))) + snprintf(name, sizeof(name), "%s/net/psched", ev); + else + _nl_strncpy_assert(name, "/proc/net/psched", sizeof(name)); + + if ((fd = fopen(name, "re"))) { + unsigned int ns_per_usec, ns_per_tick, nom, denom; + + if (fscanf(fd, "%08x %08x %08x %08x", + &ns_per_usec, &ns_per_tick, &nom, &denom) != 4) { + NL_DBG(1, "Fatal error: can not read psched settings from \"%s\". " \ + "Try to set TICKS_PER_USEC, PROC_NET_PSCHED or PROC_ROOT " \ + "environment variables\n", name); + exit(1); + } + + ticks_per_usec = (double) ns_per_usec / + (double) ns_per_tick; + + if (nom == 1000000) + psched_hz = denom; + + fclose(fd); + } + } + initialized = 1; + + nl_unlock(&mutex); +} + + +/** + * Return the value of HZ + */ +int nl_get_user_hz(void) +{ + get_psched_settings(); + return user_hz; +} + +/** + * Return the value of packet scheduler HZ + */ +int nl_get_psched_hz(void) +{ + get_psched_settings(); + return psched_hz; +} + +/** + * Convert micro seconds to ticks + * @arg us micro seconds + * @return number of ticks + */ +uint32_t nl_us2ticks(uint32_t us) +{ + get_psched_settings(); + return us * ticks_per_usec; +} + + +/** + * Convert ticks to micro seconds + * @arg ticks number of ticks + * @return microseconds + */ +uint32_t nl_ticks2us(uint32_t ticks) +{ + get_psched_settings(); + return ticks / ticks_per_usec; +} + +int nl_str2msec(const char *str, uint64_t *result) +{ + uint64_t total = 0, l; + int plen; + char *p; + + do { + l = strtoul(str, &p, 0); + if (p == str) + return -NLE_INVAL; + else if (*p) { + plen = strcspn(p, " \t"); + + if (!plen) + total += l; + else if (!strncasecmp(p, "sec", plen)) + total += (l * 1000); + else if (!strncasecmp(p, "min", plen)) + total += (l * 1000*60); + else if (!strncasecmp(p, "hour", plen)) + total += (l * 1000*60*60); + else if (!strncasecmp(p, "day", plen)) + total += (l * 1000*60*60*24); + else + return -NLE_INVAL; + + str = p + plen; + } else + total += l; + } while (*str && *p); + + *result = total; + + return 0; +} + +/** + * Convert milliseconds to a character string + * @arg msec number of milliseconds + * @arg buf destination buffer + * @arg len buffer length + * + * Converts milliseconds to a character string split up in days, hours, + * minutes, seconds, and milliseconds and stores it in the specified + * destination buffer. + * + * @return The destination buffer. + */ +char * nl_msec2str(uint64_t msec, char *buf, size_t len) +{ + uint64_t split[5]; + size_t i; + static const char *units[5] = {"d", "h", "m", "s", "msec"}; + char * const buf_orig = buf; + + if (msec == 0) { + snprintf(buf, len, "0msec"); + return buf_orig; + } + +#define _SPLIT(idx, unit) if ((split[idx] = msec / unit)) msec %= unit + _SPLIT(0, 86400000); /* days */ + _SPLIT(1, 3600000); /* hours */ + _SPLIT(2, 60000); /* minutes */ + _SPLIT(3, 1000); /* seconds */ +#undef _SPLIT + split[4] = msec; + + for (i = 0; i < ARRAY_SIZE(split) && len; i++) { + int l; + if (split[i] == 0) + continue; + l = snprintf(buf, len, "%s%" PRIu64 "%s", + (buf==buf_orig) ? "" : " ", split[i], units[i]); + buf += l; + len -= l; + } + + return buf_orig; +} + +/** @} */ + +/** + * @name Netlink Family Translations + * @{ + */ + +static const struct trans_tbl nlfamilies[] = { + __ADD(NETLINK_ROUTE,route), + __ADD(NETLINK_USERSOCK,usersock), + __ADD(NETLINK_FIREWALL,firewall), + __ADD(NETLINK_INET_DIAG,inetdiag), + __ADD(NETLINK_NFLOG,nflog), + __ADD(NETLINK_XFRM,xfrm), + __ADD(NETLINK_SELINUX,selinux), + __ADD(NETLINK_ISCSI,iscsi), + __ADD(NETLINK_AUDIT,audit), + __ADD(NETLINK_FIB_LOOKUP,fib_lookup), + __ADD(NETLINK_CONNECTOR,connector), + __ADD(NETLINK_NETFILTER,netfilter), + __ADD(NETLINK_IP6_FW,ip6_fw), + __ADD(NETLINK_DNRTMSG,dnrtmsg), + __ADD(NETLINK_KOBJECT_UEVENT,kobject_uevent), + __ADD(NETLINK_GENERIC,generic), + __ADD(NETLINK_SCSITRANSPORT,scsitransport), + __ADD(NETLINK_ECRYPTFS,ecryptfs), + __ADD(NETLINK_RDMA,rdma), + __ADD(NETLINK_CRYPTO,crypto), +}; + +char * nl_nlfamily2str(int family, char *buf, size_t size) +{ + return __type2str(family, buf, size, nlfamilies, + ARRAY_SIZE(nlfamilies)); +} + +int nl_str2nlfamily(const char *name) +{ + return __str2type(name, nlfamilies, ARRAY_SIZE(nlfamilies)); +} + +/** + * @} + */ + +/** + * @name Link Layer Protocol Translations + * @{ + */ + +static const struct trans_tbl llprotos[] = { + {0, "generic"}, + __ADD(ARPHRD_NETROM,netrom), + __ADD(ARPHRD_ETHER,ether), + __ADD(ARPHRD_EETHER,eether), + __ADD(ARPHRD_AX25,ax25), + __ADD(ARPHRD_PRONET,pronet), + __ADD(ARPHRD_CHAOS,chaos), + __ADD(ARPHRD_IEEE802,ieee802), + __ADD(ARPHRD_ARCNET,arcnet), + __ADD(ARPHRD_APPLETLK,atalk), + __ADD(ARPHRD_DLCI,dlci), + __ADD(ARPHRD_ATM,atm), + __ADD(ARPHRD_METRICOM,metricom), + __ADD(ARPHRD_IEEE1394,ieee1394), + __ADD(ARPHRD_EUI64,eui64), + __ADD(ARPHRD_INFINIBAND,infiniband), + __ADD(ARPHRD_SLIP,slip), + __ADD(ARPHRD_CSLIP,cslip), + __ADD(ARPHRD_SLIP6,slip6), + __ADD(ARPHRD_CSLIP6,cslip6), + __ADD(ARPHRD_RSRVD,rsrvd), + __ADD(ARPHRD_ADAPT,adapt), + __ADD(ARPHRD_ROSE,rose), + __ADD(ARPHRD_X25,x25), + __ADD(ARPHRD_HWX25,hwx25), + __ADD(ARPHRD_CAN,can), + __ADD(ARPHRD_PPP,ppp), + __ADD(ARPHRD_CISCO,cisco), + __ADD(ARPHRD_HDLC,hdlc), + __ADD(ARPHRD_LAPB,lapb), + __ADD(ARPHRD_DDCMP,ddcmp), + __ADD(ARPHRD_RAWHDLC,rawhdlc), + __ADD(ARPHRD_TUNNEL,ipip), + __ADD(ARPHRD_TUNNEL6,tunnel6), + __ADD(ARPHRD_FRAD,frad), + __ADD(ARPHRD_SKIP,skip), + __ADD(ARPHRD_LOOPBACK,loopback), + __ADD(ARPHRD_LOCALTLK,localtlk), + __ADD(ARPHRD_FDDI,fddi), + __ADD(ARPHRD_BIF,bif), + __ADD(ARPHRD_SIT,sit), + __ADD(ARPHRD_IPDDP,ip/ddp), + __ADD(ARPHRD_IPGRE,gre), + __ADD(ARPHRD_PIMREG,pimreg), + __ADD(ARPHRD_HIPPI,hippi), + __ADD(ARPHRD_ASH,ash), + __ADD(ARPHRD_ECONET,econet), + __ADD(ARPHRD_IRDA,irda), + __ADD(ARPHRD_FCPP,fcpp), + __ADD(ARPHRD_FCAL,fcal), + __ADD(ARPHRD_FCPL,fcpl), + __ADD(ARPHRD_FCFABRIC,fcfb_0), + __ADD(ARPHRD_FCFABRIC+1,fcfb_1), + __ADD(ARPHRD_FCFABRIC+2,fcfb_2), + __ADD(ARPHRD_FCFABRIC+3,fcfb_3), + __ADD(ARPHRD_FCFABRIC+4,fcfb_4), + __ADD(ARPHRD_FCFABRIC+5,fcfb_5), + __ADD(ARPHRD_FCFABRIC+6,fcfb_6), + __ADD(ARPHRD_FCFABRIC+7,fcfb_7), + __ADD(ARPHRD_FCFABRIC+8,fcfb_8), + __ADD(ARPHRD_FCFABRIC+9,fcfb_9), + __ADD(ARPHRD_FCFABRIC+10,fcfb_10), + __ADD(ARPHRD_FCFABRIC+11,fcfb_11), + __ADD(ARPHRD_FCFABRIC+12,fcfb_12), + __ADD(ARPHRD_IEEE802_TR,tr), + __ADD(ARPHRD_IEEE80211,ieee802.11), + __ADD(ARPHRD_IEEE80211_PRISM,ieee802.11_prism), + __ADD(ARPHRD_IEEE80211_RADIOTAP,ieee802.11_radiotap), + __ADD(ARPHRD_IEEE802154,ieee802.15.4), + __ADD(ARPHRD_IEEE802154_MONITOR,ieee802.15.4_monitor), + __ADD(ARPHRD_PHONET,phonet), + __ADD(ARPHRD_PHONET_PIPE,phonet_pipe), + __ADD(ARPHRD_CAIF,caif), + __ADD(ARPHRD_IP6GRE,ip6gre), + __ADD(ARPHRD_NETLINK,netlink), + __ADD(ARPHRD_6LOWPAN,6lowpan), + __ADD(ARPHRD_VOID,void), + __ADD(ARPHRD_NONE,nohdr), +}; + +char * nl_llproto2str(int llproto, char *buf, size_t len) +{ + return __type2str(llproto, buf, len, llprotos, ARRAY_SIZE(llprotos)); +} + +int nl_str2llproto(const char *name) +{ + return __str2type(name, llprotos, ARRAY_SIZE(llprotos)); +} + +/** @} */ + + +/** + * @name Ethernet Protocol Translations + * @{ + */ + +static const struct trans_tbl ether_protos[] = { + __ADD(ETH_P_LOOP,loop), + __ADD(ETH_P_PUP,pup), + __ADD(ETH_P_PUPAT,pupat), + __ADD(ETH_P_IP,ip), + __ADD(ETH_P_X25,x25), + __ADD(ETH_P_ARP,arp), + __ADD(ETH_P_BPQ,bpq), + __ADD(ETH_P_IEEEPUP,ieeepup), + __ADD(ETH_P_IEEEPUPAT,ieeepupat), + __ADD(ETH_P_DEC,dec), + __ADD(ETH_P_DNA_DL,dna_dl), + __ADD(ETH_P_DNA_RC,dna_rc), + __ADD(ETH_P_DNA_RT,dna_rt), + __ADD(ETH_P_LAT,lat), + __ADD(ETH_P_DIAG,diag), + __ADD(ETH_P_CUST,cust), + __ADD(ETH_P_SCA,sca), + __ADD(ETH_P_TEB,teb), + __ADD(ETH_P_RARP,rarp), + __ADD(ETH_P_ATALK,atalk), + __ADD(ETH_P_AARP,aarp), +#ifdef ETH_P_8021Q + __ADD(ETH_P_8021Q,802.1q), +#endif + __ADD(ETH_P_IPX,ipx), + __ADD(ETH_P_IPV6,ipv6), + __ADD(ETH_P_PAUSE,pause), + __ADD(ETH_P_SLOW,slow), +#ifdef ETH_P_WCCP + __ADD(ETH_P_WCCP,wccp), +#endif + __ADD(ETH_P_PPP_DISC,ppp_disc), + __ADD(ETH_P_PPP_SES,ppp_ses), + __ADD(ETH_P_MPLS_UC,mpls_uc), + __ADD(ETH_P_MPLS_MC,mpls_mc), + __ADD(ETH_P_ATMMPOA,atmmpoa), + __ADD(ETH_P_LINK_CTL,link_ctl), + __ADD(ETH_P_ATMFATE,atmfate), + __ADD(ETH_P_PAE,pae), + __ADD(ETH_P_AOE,aoe), + __ADD(ETH_P_TIPC,tipc), + __ADD(ETH_P_1588,ieee1588), + __ADD(ETH_P_FCOE,fcoe), + __ADD(ETH_P_FIP,fip), + __ADD(ETH_P_EDSA,edsa), + __ADD(ETH_P_EDP2,edp2), + __ADD(ETH_P_802_3,802.3), + __ADD(ETH_P_AX25,ax25), + __ADD(ETH_P_ALL,all), + __ADD(ETH_P_802_2,802.2), + __ADD(ETH_P_SNAP,snap), + __ADD(ETH_P_DDCMP,ddcmp), + __ADD(ETH_P_WAN_PPP,wan_ppp), + __ADD(ETH_P_PPP_MP,ppp_mp), + __ADD(ETH_P_LOCALTALK,localtalk), + __ADD(ETH_P_CAN,can), + __ADD(ETH_P_PPPTALK,ppptalk), + __ADD(ETH_P_TR_802_2,tr_802.2), + __ADD(ETH_P_MOBITEX,mobitex), + __ADD(ETH_P_CONTROL,control), + __ADD(ETH_P_IRDA,irda), + __ADD(ETH_P_ECONET,econet), + __ADD(ETH_P_HDLC,hdlc), + __ADD(ETH_P_ARCNET,arcnet), + __ADD(ETH_P_DSA,dsa), + __ADD(ETH_P_TRAILER,trailer), + __ADD(ETH_P_PHONET,phonet), + __ADD(ETH_P_IEEE802154,ieee802154), + __ADD(ETH_P_CAIF,caif), +}; + +char *nl_ether_proto2str(int eproto, char *buf, size_t len) +{ + return __type2str(eproto, buf, len, ether_protos, + ARRAY_SIZE(ether_protos)); +} + +int nl_str2ether_proto(const char *name) +{ + return __str2type(name, ether_protos, ARRAY_SIZE(ether_protos)); +} + +/** @} */ + +/** + * @name IP Protocol Translations + * @{ + */ + +char *nl_ip_proto2str(int proto, char *buf, size_t len) +{ + if (nl_getprotobynumber(proto, buf, len)) + return buf; + + snprintf(buf, len, "0x%x", proto); + return buf; +} + +int nl_str2ip_proto(const char *name) +{ + unsigned long l; + char *end; + int p; + + if (!name) + return -NLE_INVAL; + + p = nl_getprotobyname(name); + if (p >= 0) + return p; + + l = strtoul(name, &end, 0); + if (name == end || *end != '\0' || l > (unsigned long)INT_MAX) + return -NLE_OBJ_NOTFOUND; + + return (int) l; +} + +/** @} */ + +/** + * @name Dumping Helpers + * @{ + */ + +/** + * Handle a new line while dumping + * @arg params Dumping parameters + * + * This function must be called before dumping any onto a + * new line. It will ensure proper prefixing as specified + * by the dumping parameters. + * + * @note This function will NOT dump any newlines itself + */ +void nl_new_line(struct nl_dump_params *params) +{ + params->dp_line++; + + if (params->dp_prefix) { + int i; + for (i = 0; i < params->dp_prefix; i++) { + if (params->dp_fd) + fprintf(params->dp_fd, " "); + else if (params->dp_buf) + strncat(params->dp_buf, " ", + params->dp_buflen - + strlen(params->dp_buf) - 1); + } + } + + if (params->dp_nl_cb) + params->dp_nl_cb(params, params->dp_line); +} + +static void dump_one(struct nl_dump_params *parms, const char *fmt, + va_list args) +{ + if (parms->dp_fd) + vfprintf(parms->dp_fd, fmt, args); + else if (parms->dp_buf || parms->dp_cb) { + char *buf = NULL; + if (vasprintf(&buf, fmt, args) >= 0) { + if (parms->dp_cb) + parms->dp_cb(parms, buf); + else + strncat(parms->dp_buf, buf, + parms->dp_buflen - + strlen(parms->dp_buf) - 1); + free(buf); + } + } +} + + +/** + * Dump a formatted character string + * @arg params Dumping parameters + * @arg fmt printf style formatting string + * @arg ... Arguments to formatting string + * + * Dumps a printf style formatting string to the output device + * as specified by the dumping parameters. + */ +void nl_dump(struct nl_dump_params *params, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + dump_one(params, fmt, args); + va_end(args); +} + +void nl_dump_line(struct nl_dump_params *parms, const char *fmt, ...) +{ + va_list args; + + nl_new_line(parms); + + va_start(args, fmt); + dump_one(parms, fmt, args); + va_end(args); +} + + +/** @} */ + +/** @cond SKIP */ + +int __trans_list_add(int i, const char *a, struct nl_list_head *head) +{ + struct trans_list *tl; + + tl = calloc(1, sizeof(*tl)); + if (!tl) + return -NLE_NOMEM; + + tl->i = i; + tl->a = strdup(a); + + nl_list_add_tail(&tl->list, head); + + return 0; +} + +void __trans_list_clear(struct nl_list_head *head) +{ + struct trans_list *tl, *next; + + nl_list_for_each_entry_safe(tl, next, head, list) { + free(tl->a); + free(tl); + } + + nl_init_list_head(head); +} + +char *__type2str(int type, char *buf, size_t len, + const struct trans_tbl *tbl, size_t tbl_len) +{ + size_t i; + + for (i = 0; i < tbl_len; i++) { + if (tbl[i].i == ((uint64_t)type)) { + snprintf(buf, len, "%s", tbl[i].a); + return buf; + } + } + + snprintf(buf, len, "0x%x", (unsigned)type); + return buf; +} + +char *__list_type2str(int type, char *buf, size_t len, + struct nl_list_head *head) +{ + struct trans_list *tl; + + nl_list_for_each_entry(tl, head, list) { + if (tl->i == type) { + snprintf(buf, len, "%s", tl->a); + return buf; + } + } + + snprintf(buf, len, "0x%x", type); + return buf; +} + +char *__flags2str(int flags, char *buf, size_t len, + const struct trans_tbl *tbl, size_t tbl_len) +{ + size_t i; + int tmp = flags; + + memset(buf, 0, len); + + for (i = 0; i < tbl_len; i++) { + if (tbl[i].i & tmp) { + tmp &= ~tbl[i].i; + strncat(buf, tbl[i].a, len - strlen(buf) - 1); + if ((tmp & flags)) + strncat(buf, ",", len - strlen(buf) - 1); + } + } + + return buf; +} + +int __str2type(const char *buf, const struct trans_tbl *tbl, size_t tbl_len) +{ + unsigned long l; + char *end; + size_t i; + + if (*buf == '\0') + return -NLE_INVAL; + + for (i = 0; i < tbl_len; i++) + if (!strcasecmp(tbl[i].a, buf)) + return tbl[i].i; + + l = strtoul(buf, &end, 0); + if (l == ULONG_MAX || *end != '\0') + return -NLE_OBJ_NOTFOUND; + + return (int) l; +} + +int __list_str2type(const char *buf, struct nl_list_head *head) +{ + struct trans_list *tl; + unsigned long l; + char *end; + + if (*buf == '\0') + return -NLE_INVAL; + + nl_list_for_each_entry(tl, head, list) { + if (!strcasecmp(tl->a, buf)) + return tl->i; + } + + l = strtoul(buf, &end, 0); + if (l == ULONG_MAX || *end != '\0') + return -NLE_OBJ_NOTFOUND; + + return (int) l; +} + +int __str2flags(const char *buf, const struct trans_tbl *tbl, size_t tbl_len) +{ + int flags = 0; + size_t i; + size_t len; /* ptrdiff_t ? */ + char *p = (char *) buf, *t; + + for (;;) { + if (*p == ' ') + p++; + + t = strchr(p, ','); + len = t ? ((size_t)(t - p)) : strlen(p); + for (i = 0; i < tbl_len; i++) + if (len == strlen(tbl[i].a) && + !strncasecmp(tbl[i].a, p, len)) + flags |= tbl[i].i; + + if (!t) + return flags; + + p = ++t; + } + + return 0; +} + +void dump_from_ops(struct nl_object *obj, struct nl_dump_params *params) +{ + int type = params->dp_type; + + if (type < 0 || type > NL_DUMP_MAX) + BUG(); + + params->dp_line = 0; + + if (params->dp_dump_msgtype) { +#if 0 + /* XXX */ + char buf[64]; + + dp_dump_line(params, 0, "%s ", + nl_cache_mngt_type2name(obj->ce_ops, + obj->ce_ops->co_protocol, + obj->ce_msgtype, + buf, sizeof(buf))); +#endif + params->dp_pre_dump = 1; + } + + if (obj->ce_ops->oo_dump[type]) + obj->ce_ops->oo_dump[type](obj, params); +} + +/** + * Check for library capabilities + * + * @arg capability capability identifier + * + * Check whether the loaded libnl library supports a certain capability. + * This is useful so that applications can workaround known issues of + * libnl that are fixed in newer library versions, without + * having a hard dependency on the new version. It is also useful, for + * capabilities that cannot easily be detected using autoconf tests. + * The capabilities are integer constants with name NL_CAPABILITY_*. + * + * As this function is intended to detect capabilities at runtime, + * you might not want to depend during compile time on the NL_CAPABILITY_* + * names. Instead you can use their numeric values which are guaranteed not to + * change meaning. + * + * @return non zero if libnl supports a certain capability, 0 otherwise. + **/ +int nl_has_capability (int capability) +{ + static const uint8_t caps[ ( NL_CAPABILITY_MAX + 7 ) / 8 ] = { +#define _NL_ASSERT(expr) ( 0 * sizeof(struct { unsigned int x: ( (!!(expr)) ? 1 : -1 ); }) ) +#define _NL_SETV(i, r, v) \ + ( _NL_ASSERT( (v) == 0 || (i) * 8 + (r) == (v) - 1 ) + \ + ( (v) == 0 ? 0 : (1 << (r)) ) ) +#define _NL_SET(i, v0, v1, v2, v3, v4, v5, v6, v7) \ + [(i)] = ( \ + _NL_SETV((i), 0, (v0)) | _NL_SETV((i), 4, (v4)) | \ + _NL_SETV((i), 1, (v1)) | _NL_SETV((i), 5, (v5)) | \ + _NL_SETV((i), 2, (v2)) | _NL_SETV((i), 6, (v6)) | \ + _NL_SETV((i), 3, (v3)) | _NL_SETV((i), 7, (v7)) ) + _NL_SET(0, + NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE, + NL_CAPABILITY_ROUTE_LINK_VETH_GET_PEER_OWN_REFERENCE, + NL_CAPABILITY_ROUTE_LINK_CLS_ADD_ACT_OWN_REFERENCE, + NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE, + NL_CAPABILITY_ROUTE_LINK_GET_KERNEL_FAIL_OPNOTSUPP, + NL_CAPABILITY_ROUTE_ADDR_COMPARE_CACHEINFO, + NL_CAPABILITY_VERSION_3_2_26, + NL_CAPABILITY_NL_RECV_FAIL_TRUNC_NO_PEEK), + _NL_SET(1, + NL_CAPABILITY_LINK_BUILD_CHANGE_REQUEST_SET_CHANGE, + NL_CAPABILITY_RTNL_NEIGH_GET_FILTER_AF_UNSPEC_FIX, + NL_CAPABILITY_VERSION_3_2_27, + NL_CAPABILITY_RTNL_LINK_VLAN_PROTOCOL_SERIALZE, + NL_CAPABILITY_RTNL_LINK_PARSE_GRE_REMOTE, + NL_CAPABILITY_RTNL_LINK_VLAN_INGRESS_MAP_CLEAR, + NL_CAPABILITY_RTNL_LINK_VXLAN_IO_COMPARE, + NL_CAPABILITY_NL_OBJECT_DIFF64), + _NL_SET (2, + NL_CAPABILITY_XFRM_SA_KEY_SIZE, + NL_CAPABILITY_RTNL_ADDR_PEER_FIX, + NL_CAPABILITY_VERSION_3_2_28, + NL_CAPABILITY_RTNL_ADDR_PEER_ID_FIX, + NL_CAPABILITY_NL_ADDR_FILL_SOCKADDR, + NL_CAPABILITY_XFRM_SEC_CTX_LEN, + NL_CAPABILITY_LINK_BUILD_ADD_REQUEST_SET_CHANGE, + NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT), + _NL_SET (3, + NL_CAPABILITY_VERSION_3_2_29, + NL_CAPABILITY_XFRM_SP_SEC_CTX_LEN, + NL_CAPABILITY_VERSION_3_3_0, + NL_CAPABILITY_VERSION_3_4_0, + NL_CAPABILITY_ROUTE_FIX_VLAN_SET_EGRESS_MAP, + NL_CAPABILITY_VERSION_3_5_0, + NL_CAPABILITY_NL_OBJECT_IDENTICAL_PARTIAL, + NL_CAPABILITY_VERSION_3_6_0), + _NL_SET (4, + NL_CAPABILITY_VERSION_3_7_0, + NL_CAPABILITY_VERSION_3_8_0, + NL_CAPABILITY_VERSION_3_9_0, + NL_CAPABILITY_VERSION_3_10_0, + NL_CAPABILITY_VERSION_3_11_0, + 0, /* NL_CAPABILITY_VERSION_3_12_0 */ + 0, /* NL_CAPABILITY_VERSION_3_13_0 */ + 0), /* NL_CAPABILITY_VERSION_3_14_0 */ + /* IMPORTANT: these capability numbers are intended to be universal and stable + * for libnl3. Don't allocate new numbers on your own that differ from upstream + * libnl3. + * + * Instead register a capability number upstream too. We will take patches + * for that. We especially take patches to register a capability number that is + * only implemented in your fork of libnl3. + * + * If you really don't want that, use capabilities in the range 0x7000 to 0x7FFF. + * (NL_CAPABILITY_IS_USER_RESERVED). Upstream libnl3 will not register conflicting + * capabilities in that range. + * + * Obviously, only backport capability numbers to libnl versions that actually + * implement that capability as well. */ +#undef _NL_SET +#undef _NL_SETV +#undef _NL_ASSERT + }; + + if (capability <= 0 || capability > NL_CAPABILITY_MAX) + return 0; + capability--; + return (caps[capability / 8] & (1 << (capability % 8))) != 0; +} + +/** @endcond */ + +/** @} */ diff --git a/libnl/lib/version.c b/libnl/lib/version.c new file mode 100644 index 0000000..da4bd25 --- /dev/null +++ b/libnl/lib/version.c @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup core + * @defgroup utils Utilities + * + * Run-time version information + * + * @{ + */ + + +/** + * @name Run-time version information + * @{ + */ + +#include "nl-default.h" + +#include + +const int nl_ver_num = LIBNL_VER_NUM; +const int nl_ver_maj = LIBNL_VER_MAJ; +const int nl_ver_min = LIBNL_VER_MIN; +const int nl_ver_mic = LIBNL_VER_MIC; + +/** @} */ + +/** @} */ diff --git a/libnl/lib/xfrm/ae.c b/libnl/lib/xfrm/ae.c new file mode 100644 index 0000000..1898038 --- /dev/null +++ b/libnl/lib/xfrm/ae.c @@ -0,0 +1,1028 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * Neither the name of Texas Instruments Incorporated 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 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. + * + */ + +/** + * @ingroup xfrmnl + * @defgroup ae Attribute Element + * @brief + * + * The AE interface allows a user to retrieve and update various + * Security Association (SA) attributes such as lifetime, replay state etc. + * + * @par AE Flags + * @code + * XFRM_AE_UNSPEC + * XFRM_AE_RTHR=1 + * XFRM_AE_RVAL=2 + * XFRM_AE_LVAL=4 + * XFRM_AE_ETHR=8 + * XFRM_AE_CR=16 + * XFRM_AE_CE=32 + * XFRM_AE_CU=64 + * @endcode + * + * @par AE Identification + * An AE is uniquely identified by the attributes listed below, whenever + * you refer to an existing AE all of the attributes must be set. There is + * no cache support for AE since you can retrieve the AE for any given combination + * of attributes mentioned below, but not all at once since they just characterize + * an SA. + * - destination address (xfrmnl_ae_set_daddr()) + * - SPI (xfrmnl_ae_set_spi) + * - protocol (xfrmnl_ae_set_proto) + * - mark (xfrmnl_ae_set_mark) + * + * @par Changeable Attributes + * \anchor ae_changeable + * - current lifetime (xfrmnl_ae_set_curlifetime()) + * - replay properties (xfrmnl_ae_set_replay_maxage(), xfrmnl_ae_set_replay_maxdiff()) + * - replay state (xfrmnl_ae_set_replay_state(), xfrmnl_ae_set_replay_state_esn)) + * + * @par Required Caches for Dumping + * None + * + * @par TODO + * None + * + * @par 1) Retrieving AE information for a given SA tuple + * @code + * // Create a netlink socket and connect it to XFRM subsystem in + * the kernel to be able to send/receive info from userspace. + * struct nl_sock* sk = nl_socket_alloc (); + * nl_connect (sk, NETLINK_XFRM); + * + * // AEs can then be looked up by the SA tuple, destination address, + * SPI, protocol, mark: + * struct xfrmnl_ae *ae; + * xfrmnl_ae_get_kernel(sk, dst_addr, spi, proto,mark_mask, mark_value, &ae); + * + * // After successful usage, the object must be freed + * xfrmnl_ae_put(ae); + * @endcode + * + * @par 2) Updating AE + * @code + * // Allocate an empty AE handle to be filled out with the attributes + * // of the new AE. + * struct xfrmnl_ae *ae = xfrmnl_ae_alloc(); + * + * // Fill out the attributes of the new AE + * xfrmnl_ae_set_daddr(ae, dst_addr); + * xfrmnl_ae_set_spi(ae, 0xDEADBEEF); + * xfrmnl_ae_set_proto(ae, 50); + * xfrmnl_ae_set_mark(ae, 0x0); + * xfrmnl_ae_set_saddr(ae, src_addr); + * xfrmnl_ae_set_curlifetime(ae, 540, 10, 0xAABB1122, 0x0); + * + * // Build the netlink message and send it to the kernel, the operation will + * // block until the operation has been completed. Alternatively, a netlink message + * // can be built using xfrmnl_ae_build_get_request () API and be sent using + * // nl_send_auto(). Further the result from the kernel can be parsed using + * // xfrmnl_ae_parse() API. + * xfrmnl_ae_set(sk, ae, NLM_F_REPLACE); + * + * // Free the memory + * xfrmnl_ae_put(ae); + * @endcode + * + * @{ + */ + +#include "nl-default.h" + +#include +#include + +#include +#include +#include +#include + +#include "nl-xfrm.h" +#include "nl-priv-dynamic-core/object-api.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" +#include "nl-aux-core/nl-core.h" +#include "nl-aux-xfrm/nl-xfrm.h" + +/** @cond SKIP */ + +struct xfrmnl_sa_id { + struct nl_addr* daddr; + uint32_t spi; + uint16_t family; + uint8_t proto; +}; + +struct xfrmnl_ae { + NLHDR_COMMON + + struct xfrmnl_sa_id sa_id; + struct nl_addr* saddr; + uint32_t flags; + uint32_t reqid; + struct xfrmnl_mark mark; + struct xfrmnl_lifetime_cur lifetime_cur; + uint32_t replay_maxage; + uint32_t replay_maxdiff; + struct xfrmnl_replay_state replay_state; + struct xfrmnl_replay_state_esn* replay_state_esn; +}; + +#define XFRM_AE_ATTR_DADDR 0x01 +#define XFRM_AE_ATTR_SPI 0x02 +#define XFRM_AE_ATTR_PROTO 0x04 +#define XFRM_AE_ATTR_SADDR 0x08 +#define XFRM_AE_ATTR_FLAGS 0x10 +#define XFRM_AE_ATTR_REQID 0x20 +#define XFRM_AE_ATTR_MARK 0x40 +#define XFRM_AE_ATTR_LIFETIME 0x80 +#define XFRM_AE_ATTR_REPLAY_MAXAGE 0x100 +#define XFRM_AE_ATTR_REPLAY_MAXDIFF 0x200 +#define XFRM_AE_ATTR_REPLAY_STATE 0x400 +#define XFRM_AE_ATTR_FAMILY 0x800 + +static struct nl_object_ops xfrm_ae_obj_ops; +/** @endcond */ + + +static void xfrm_ae_free_data(struct nl_object *c) +{ + struct xfrmnl_ae* ae = nl_object_priv (c); + + if (ae == NULL) + return; + + nl_addr_put (ae->sa_id.daddr); + nl_addr_put (ae->saddr); + + if (ae->replay_state_esn) + free (ae->replay_state_esn); +} + +static int xfrm_ae_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct xfrmnl_ae* dst = nl_object_priv(_dst); + struct xfrmnl_ae* src = nl_object_priv(_src); + + dst->sa_id.daddr = NULL; + dst->saddr = NULL; + dst->replay_state_esn = NULL; + + if (src->sa_id.daddr) { + if ((dst->sa_id.daddr = nl_addr_clone (src->sa_id.daddr)) == NULL) + return -NLE_NOMEM; + } + + if (src->saddr) { + if ((dst->saddr = nl_addr_clone (src->saddr)) == NULL) + return -NLE_NOMEM; + } + + if (src->replay_state_esn) { + uint32_t len = sizeof (struct xfrmnl_replay_state_esn) + (sizeof (uint32_t) * src->replay_state_esn->bmp_len); + + if ((dst->replay_state_esn = malloc (len)) == NULL) + return -NLE_NOMEM; + memcpy (dst->replay_state_esn, src->replay_state_esn, len); + } + + return 0; +} + +static uint64_t xfrm_ae_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct xfrmnl_ae* a = (struct xfrmnl_ae *) _a; + struct xfrmnl_ae* b = (struct xfrmnl_ae *) _b; + uint64_t diff = 0; + int found = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(XFRM_AE_ATTR_DADDR, + nl_addr_cmp(a->sa_id.daddr, b->sa_id.daddr)); + diff |= _DIFF(XFRM_AE_ATTR_SPI, a->sa_id.spi != b->sa_id.spi); + diff |= _DIFF(XFRM_AE_ATTR_PROTO, a->sa_id.proto != b->sa_id.proto); + diff |= _DIFF(XFRM_AE_ATTR_SADDR, nl_addr_cmp(a->saddr, b->saddr)); + diff |= _DIFF(XFRM_AE_ATTR_FLAGS, a->flags != b->flags); + diff |= _DIFF(XFRM_AE_ATTR_REQID, a->reqid != b->reqid); + diff |= _DIFF(XFRM_AE_ATTR_MARK, + (a->mark.v & a->mark.m) != (b->mark.v & b->mark.m)); + diff |= _DIFF(XFRM_AE_ATTR_REPLAY_MAXAGE, + a->replay_maxage != b->replay_maxage); + diff |= _DIFF(XFRM_AE_ATTR_REPLAY_MAXDIFF, + a->replay_maxdiff != b->replay_maxdiff); + + /* Compare replay states */ + found = AVAILABLE_MISMATCH (a, b, XFRM_AE_ATTR_REPLAY_STATE); + if (found == 0) // attribute exists in both objects + { + if (((a->replay_state_esn != NULL) && (b->replay_state_esn == NULL)) || + ((a->replay_state_esn == NULL) && (b->replay_state_esn != NULL))) + found |= 1; + + if (found == 0) // same replay type. compare actual values + { + if (a->replay_state_esn) + { + if (a->replay_state_esn->bmp_len != b->replay_state_esn->bmp_len) + diff |= 1; + else + { + uint32_t len = sizeof (struct xfrmnl_replay_state_esn) + (sizeof (uint32_t) * a->replay_state_esn->bmp_len); + diff |= memcmp (a->replay_state_esn, b->replay_state_esn, len); + } + } + else + { + if ((a->replay_state.oseq != b->replay_state.oseq) || + (a->replay_state.seq != b->replay_state.seq) || + (a->replay_state.bitmap != b->replay_state.bitmap)) + diff |= 1; + } + } + } +#undef _DIFF + + return diff; +} + +/** + * @name XFRM AE Attribute Translations + * @{ + */ +static const struct trans_tbl ae_attrs[] = +{ + __ADD(XFRM_AE_ATTR_DADDR, daddr), + __ADD(XFRM_AE_ATTR_SPI, spi), + __ADD(XFRM_AE_ATTR_PROTO, protocol), + __ADD(XFRM_AE_ATTR_SADDR, saddr), + __ADD(XFRM_AE_ATTR_FLAGS, flags), + __ADD(XFRM_AE_ATTR_REQID, reqid), + __ADD(XFRM_AE_ATTR_MARK, mark), + __ADD(XFRM_AE_ATTR_LIFETIME, cur_lifetime), + __ADD(XFRM_AE_ATTR_REPLAY_MAXAGE, replay_maxage), + __ADD(XFRM_AE_ATTR_REPLAY_MAXDIFF, replay_maxdiff), + __ADD(XFRM_AE_ATTR_REPLAY_STATE, replay_state), +}; + +static char* xfrm_ae_attrs2str (int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, ae_attrs, ARRAY_SIZE(ae_attrs)); +} +/** @} */ + +/** + * @name XFRM AE Flags Translations + * @{ + */ + +static const struct trans_tbl ae_flags[] = { + __ADD(XFRM_AE_UNSPEC, unspecified), + __ADD(XFRM_AE_RTHR, replay threshold), + __ADD(XFRM_AE_RVAL, replay value), + __ADD(XFRM_AE_LVAL, lifetime value), + __ADD(XFRM_AE_ETHR, expiry time threshold), + __ADD(XFRM_AE_CR, replay update event), + __ADD(XFRM_AE_CE, timer expiry event), + __ADD(XFRM_AE_CU, policy update event), +}; + +char* xfrmnl_ae_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str (flags, buf, len, ae_flags, ARRAY_SIZE(ae_flags)); +} + +int xfrmnl_ae_str2flag(const char *name) +{ + return __str2flags(name, ae_flags, ARRAY_SIZE(ae_flags)); +} +/** @} */ + +static void xfrm_ae_dump_line(struct nl_object *a, struct nl_dump_params *p) +{ + char dst[INET6_ADDRSTRLEN+5], src[INET6_ADDRSTRLEN+5]; + struct xfrmnl_ae* ae = (struct xfrmnl_ae *) a; + char flags[128], buf[128]; + time_t add_time, use_time; + struct tm *add_time_tm, *use_time_tm; + struct tm tm_buf; + + nl_dump_line(p, "src %s dst %s \n", nl_addr2str(ae->saddr, src, sizeof(src)), + nl_addr2str(ae->sa_id.daddr, dst, sizeof(dst))); + + nl_dump_line(p, "\tproto %s spi 0x%x reqid %u ", + nl_ip_proto2str (ae->sa_id.proto, buf, sizeof (buf)), + ae->sa_id.spi, ae->reqid); + + xfrmnl_ae_flags2str(ae->flags, flags, sizeof (flags)); + nl_dump_line(p, "flags %s(0x%x) mark mask/value 0x%x/0x%x \n", flags, + ae->flags, ae->mark.m, ae->mark.v); + + nl_dump_line(p, "\tlifetime current: \n"); + nl_dump_line(p, "\t\tbytes %llu packets %llu \n", + (long long unsigned)ae->lifetime_cur.bytes, + (long long unsigned)ae->lifetime_cur.packets); + if (ae->lifetime_cur.add_time != 0) + { + add_time = ae->lifetime_cur.add_time; + add_time_tm = gmtime_r (&add_time, &tm_buf); + strftime (flags, 128, "%Y-%m-%d %H-%M-%S", add_time_tm); + } + else + { + sprintf (flags, "%s", "-"); + } + + if (ae->lifetime_cur.use_time != 0) + { + use_time = ae->lifetime_cur.use_time; + use_time_tm = gmtime_r (&use_time, &tm_buf); + strftime (buf, 128, "%Y-%m-%d %H-%M-%S", use_time_tm); + } + else + { + sprintf (buf, "%s", "-"); + } + nl_dump_line(p, "\t\tadd_time: %s, use_time: %s\n", flags, buf); + + nl_dump_line(p, "\treplay info: \n"); + nl_dump_line(p, "\t\tmax age %u max diff %u \n", ae->replay_maxage, ae->replay_maxdiff); + + nl_dump_line(p, "\treplay state info: \n"); + if (ae->replay_state_esn) + { + nl_dump_line(p, "\t\toseq %u seq %u oseq_hi %u seq_hi %u replay window: %u \n", + ae->replay_state_esn->oseq, ae->replay_state_esn->seq, + ae->replay_state_esn->oseq_hi, ae->replay_state_esn->seq_hi, + ae->replay_state_esn->replay_window); + } + else + { + nl_dump_line(p, "\t\toseq %u seq %u bitmap: %u \n", ae->replay_state.oseq, + ae->replay_state.seq, ae->replay_state.bitmap); + } + + nl_dump(p, "\n"); +} + +static void xfrm_ae_dump_details(struct nl_object *a, struct nl_dump_params *p) +{ + xfrm_ae_dump_line(a, p); +} + +static void xfrm_ae_dump_stats(struct nl_object *a, struct nl_dump_params *p) +{ + xfrm_ae_dump_details(a, p); +} + + +static int build_xfrm_ae_message(struct xfrmnl_ae *tmpl, int cmd, int flags, + struct nl_msg **result) +{ + struct nl_msg* msg; + struct xfrm_aevent_id ae_id; + + if (!(tmpl->ce_mask & XFRM_AE_ATTR_DADDR) || + !(tmpl->ce_mask & XFRM_AE_ATTR_SPI) || + !(tmpl->ce_mask & XFRM_AE_ATTR_PROTO)) + return -NLE_MISSING_ATTR; + + memset(&ae_id, 0, sizeof(ae_id)); + + memcpy (&ae_id.sa_id.daddr, nl_addr_get_binary_addr (tmpl->sa_id.daddr), sizeof (uint8_t) * nl_addr_get_len (tmpl->sa_id.daddr)); + ae_id.sa_id.spi = htonl(tmpl->sa_id.spi); + ae_id.sa_id.family = tmpl->sa_id.family; + ae_id.sa_id.proto = tmpl->sa_id.proto; + + if (tmpl->ce_mask & XFRM_AE_ATTR_SADDR) + memcpy (&ae_id.saddr, nl_addr_get_binary_addr (tmpl->saddr), sizeof (uint8_t) * nl_addr_get_len (tmpl->saddr)); + + if (tmpl->ce_mask & XFRM_AE_ATTR_FLAGS) + ae_id.flags = tmpl->flags; + + if (tmpl->ce_mask & XFRM_AE_ATTR_REQID) + ae_id.reqid = tmpl->reqid; + + msg = nlmsg_alloc_simple(cmd, flags); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &ae_id, sizeof(ae_id), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (tmpl->ce_mask & XFRM_AE_ATTR_MARK) + NLA_PUT (msg, XFRMA_MARK, sizeof (struct xfrmnl_mark), &tmpl->mark); + + if (tmpl->ce_mask & XFRM_AE_ATTR_LIFETIME) + NLA_PUT (msg, XFRMA_LTIME_VAL, sizeof (struct xfrmnl_lifetime_cur), &tmpl->lifetime_cur); + + if (tmpl->ce_mask & XFRM_AE_ATTR_REPLAY_MAXAGE) + NLA_PUT_U32 (msg, XFRMA_ETIMER_THRESH, tmpl->replay_maxage); + + if (tmpl->ce_mask & XFRM_AE_ATTR_REPLAY_MAXDIFF) + NLA_PUT_U32 (msg, XFRMA_REPLAY_THRESH, tmpl->replay_maxdiff); + + if (tmpl->ce_mask & XFRM_AE_ATTR_REPLAY_STATE) { + if (tmpl->replay_state_esn) { + uint32_t len = sizeof (struct xfrm_replay_state_esn) + (sizeof (uint32_t) * tmpl->replay_state_esn->bmp_len); + NLA_PUT (msg, XFRMA_REPLAY_ESN_VAL, len, tmpl->replay_state_esn); + } + else { + NLA_PUT (msg, XFRMA_REPLAY_VAL, sizeof (struct xfrmnl_replay_state), &tmpl->replay_state); + } + } + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +/** + * @name XFRM AE Update + * @{ + */ + +int xfrmnl_ae_set(struct nl_sock* sk, struct xfrmnl_ae* ae, int flags) +{ + int err; + struct nl_msg *msg; + + if ((err = build_xfrm_ae_message(ae, XFRM_MSG_NEWAE, flags|NLM_F_REPLACE, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return nl_wait_for_ack(sk); +} + +/** @} */ + +/** + * @name XFRM AE Object Allocation/Freeage + * @{ + */ + +struct xfrmnl_ae* xfrmnl_ae_alloc(void) +{ + return (struct xfrmnl_ae*) nl_object_alloc(&xfrm_ae_obj_ops); +} + +void xfrmnl_ae_put(struct xfrmnl_ae* ae) +{ + nl_object_put((struct nl_object *) ae); +} + +/** @} */ + +static struct nla_policy xfrm_ae_policy[XFRMA_MAX+1] = { + [XFRMA_LTIME_VAL] = { .minlen = sizeof(struct xfrm_lifetime_cur) }, + [XFRMA_REPLAY_VAL] = { .minlen = sizeof(struct xfrm_replay_state) }, + [XFRMA_REPLAY_THRESH] = { .type = NLA_U32 }, + [XFRMA_ETIMER_THRESH] = { .type = NLA_U32 }, + [XFRMA_SRCADDR] = { .minlen = sizeof(xfrm_address_t) }, + [XFRMA_MARK] = { .minlen = sizeof(struct xfrm_mark) }, + [XFRMA_REPLAY_ESN_VAL] = { .minlen = sizeof(struct xfrm_replay_state_esn) }, +}; + +int xfrmnl_ae_parse(struct nlmsghdr *n, struct xfrmnl_ae **result) +{ + _nl_auto_xfrmnl_ae struct xfrmnl_ae *ae = NULL; + struct nlattr *tb[XFRMA_MAX + 1]; + struct xfrm_aevent_id* ae_id; + int err; + + ae = xfrmnl_ae_alloc(); + if (!ae) + return -NLE_NOMEM; + + ae->ce_msgtype = n->nlmsg_type; + ae_id = nlmsg_data(n); + + err = nlmsg_parse(n, sizeof(struct xfrm_aevent_id), tb, XFRMA_MAX, xfrm_ae_policy); + if (err < 0) + return err; + + if (!(ae->sa_id.daddr = + _nl_addr_build(ae_id->sa_id.family, &ae_id->sa_id.daddr))) + return -NLE_NOMEM; + ae->sa_id.family= ae_id->sa_id.family; + ae->sa_id.spi = ntohl(ae_id->sa_id.spi); + ae->sa_id.proto = ae_id->sa_id.proto; + if (!(ae->saddr = _nl_addr_build(ae_id->sa_id.family, &ae_id->saddr))) + return -NLE_NOMEM; + ae->reqid = ae_id->reqid; + ae->flags = ae_id->flags; + ae->ce_mask |= (XFRM_AE_ATTR_DADDR | XFRM_AE_ATTR_FAMILY | XFRM_AE_ATTR_SPI | + XFRM_AE_ATTR_PROTO | XFRM_AE_ATTR_SADDR | XFRM_AE_ATTR_REQID | + XFRM_AE_ATTR_FLAGS); + + if (tb[XFRMA_MARK]) { + struct xfrm_mark* m = nla_data(tb[XFRMA_MARK]); + ae->mark.m = m->m; + ae->mark.v = m->v; + ae->ce_mask |= XFRM_AE_ATTR_MARK; + } + + if (tb[XFRMA_LTIME_VAL]) { + struct xfrm_lifetime_cur* cur = nla_data(tb[XFRMA_LTIME_VAL]); + + ae->lifetime_cur.bytes = cur->bytes; + ae->lifetime_cur.packets = cur->packets; + ae->lifetime_cur.add_time = cur->add_time; + ae->lifetime_cur.use_time = cur->use_time; + ae->ce_mask |= XFRM_AE_ATTR_LIFETIME; + } + + if (tb[XFRM_AE_ETHR]) { + ae->replay_maxage = *(uint32_t*)nla_data(tb[XFRM_AE_ETHR]); + ae->ce_mask |= XFRM_AE_ATTR_REPLAY_MAXAGE; + } + + if (tb[XFRM_AE_RTHR]) { + ae->replay_maxdiff = *(uint32_t*)nla_data(tb[XFRM_AE_RTHR]); + ae->ce_mask |= XFRM_AE_ATTR_REPLAY_MAXDIFF; + } + + if (tb[XFRMA_REPLAY_ESN_VAL]) { + struct xfrm_replay_state_esn* esn = nla_data (tb[XFRMA_REPLAY_ESN_VAL]); + uint32_t len = sizeof (struct xfrmnl_replay_state_esn) + (sizeof (uint32_t) * esn->bmp_len); + + if ((ae->replay_state_esn = calloc (1, len)) == NULL) + return -NLE_NOMEM; + ae->replay_state_esn->oseq = esn->oseq; + ae->replay_state_esn->seq = esn->seq; + ae->replay_state_esn->oseq_hi = esn->oseq_hi; + ae->replay_state_esn->seq_hi = esn->seq_hi; + ae->replay_state_esn->replay_window = esn->replay_window; + ae->replay_state_esn->bmp_len = esn->bmp_len; + memcpy (ae->replay_state_esn->bmp, esn->bmp, sizeof (uint32_t) * esn->bmp_len); + ae->ce_mask |= XFRM_AE_ATTR_REPLAY_STATE; + } + else + { + struct xfrm_replay_state* replay_state = nla_data (tb[XFRMA_REPLAY_VAL]); + ae->replay_state.oseq = replay_state->oseq; + ae->replay_state.seq = replay_state->seq; + ae->replay_state.bitmap = replay_state->bitmap; + ae->ce_mask |= XFRM_AE_ATTR_REPLAY_STATE; + + ae->replay_state_esn = NULL; + } + + *result = _nl_steal_pointer(&ae); + return 0; +} + +static int xfrm_ae_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *n, struct nl_parser_param *pp) +{ + struct xfrmnl_ae* ae; + int err; + + if ((err = xfrmnl_ae_parse(n, &ae)) < 0) + return err; + + err = pp->pp_cb((struct nl_object *) ae, pp); + + xfrmnl_ae_put(ae); + return err; +} + +/** + * @name XFRM AE Get + * @{ + */ + +int xfrmnl_ae_build_get_request(struct nl_addr* daddr, unsigned int spi, unsigned int protocol, + unsigned int mark_mask, unsigned int mark_value, struct nl_msg **result) +{ + struct nl_msg *msg; + struct xfrm_aevent_id ae_id; + struct xfrmnl_mark mark; + + if (!daddr || !spi) + { + fprintf(stderr, "APPLICATION BUG: %s:%d:%s: A valid destination address, spi must be specified\n", + __FILE__, __LINE__, __func__); + assert(0); + return -NLE_MISSING_ATTR; + } + + memset(&ae_id, 0, sizeof(ae_id)); + memcpy (&ae_id.sa_id.daddr, nl_addr_get_binary_addr (daddr), sizeof (uint8_t) * nl_addr_get_len (daddr)); + ae_id.sa_id.spi = htonl(spi); + ae_id.sa_id.family = nl_addr_get_family (daddr); + ae_id.sa_id.proto = protocol; + + if (!(msg = nlmsg_alloc_simple(XFRM_MSG_GETAE, 0))) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &ae_id, sizeof(ae_id), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + mark.m = mark_mask; + mark.v = mark_value; + NLA_PUT (msg, XFRMA_MARK, sizeof (struct xfrmnl_mark), &mark); + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +int xfrmnl_ae_get_kernel(struct nl_sock* sock, struct nl_addr* daddr, unsigned int spi, unsigned int protocol, + unsigned int mark_mask, unsigned int mark_value, struct xfrmnl_ae** result) +{ + struct nl_msg *msg = NULL; + struct nl_object *obj; + int err; + + if ((err = xfrmnl_ae_build_get_request(daddr, spi, protocol, mark_mask, mark_value, &msg)) < 0) + return err; + + err = nl_send_auto(sock, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + if ((err = nl_pickup(sock, &xfrm_ae_msg_parser, &obj)) < 0) + return err; + + /* We have used xfrm_ae_msg_parser(), object is definitely a xfrm ae */ + *result = (struct xfrmnl_ae *) obj; + + /* If an object has been returned, we also need to wait for the ACK */ + if (err == 0 && obj) + nl_wait_for_ack(sock); + + return 0; +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +static inline int __assign_addr(struct xfrmnl_ae* ae, struct nl_addr **pos, + struct nl_addr *new, int flag, int nocheck) +{ + if (!nocheck) { + if (ae->ce_mask & XFRM_AE_ATTR_FAMILY) { + if (nl_addr_get_family (new) != ae->sa_id.family) + return -NLE_AF_MISMATCH; + } else { + ae->sa_id.family = nl_addr_get_family (new); + ae->ce_mask |= XFRM_AE_ATTR_FAMILY; + } + } + + if (*pos) + nl_addr_put(*pos); + + nl_addr_get(new); + *pos = new; + + ae->ce_mask |= flag; + + return 0; +} + + +struct nl_addr* xfrmnl_ae_get_daddr (struct xfrmnl_ae* ae) +{ + if (ae->ce_mask & XFRM_AE_ATTR_DADDR) + return ae->sa_id.daddr; + else + return NULL; +} + +int xfrmnl_ae_set_daddr (struct xfrmnl_ae* ae, struct nl_addr* addr) +{ + return __assign_addr(ae, &ae->sa_id.daddr, addr, XFRM_AE_ATTR_DADDR, 0); +} + +int xfrmnl_ae_get_spi (struct xfrmnl_ae* ae) +{ + if (ae->ce_mask & XFRM_AE_ATTR_SPI) + return ae->sa_id.spi; + else + return -1; +} + +int xfrmnl_ae_set_spi (struct xfrmnl_ae* ae, unsigned int spi) +{ + ae->sa_id.spi = spi; + ae->ce_mask |= XFRM_AE_ATTR_SPI; + + return 0; +} + +int xfrmnl_ae_get_family (struct xfrmnl_ae* ae) +{ + if (ae->ce_mask & XFRM_AE_ATTR_FAMILY) + return ae->sa_id.family; + else + return -1; +} + +int xfrmnl_ae_set_family (struct xfrmnl_ae* ae, unsigned int family) +{ + ae->sa_id.family = family; + ae->ce_mask |= XFRM_AE_ATTR_FAMILY; + + return 0; +} + +int xfrmnl_ae_get_proto (struct xfrmnl_ae* ae) +{ + if (ae->ce_mask & XFRM_AE_ATTR_PROTO) + return ae->sa_id.proto; + else + return -1; +} + +int xfrmnl_ae_set_proto (struct xfrmnl_ae* ae, unsigned int protocol) +{ + ae->sa_id.proto = protocol; + ae->ce_mask |= XFRM_AE_ATTR_PROTO; + + return 0; +} + +struct nl_addr* xfrmnl_ae_get_saddr (struct xfrmnl_ae* ae) +{ + if (ae->ce_mask & XFRM_AE_ATTR_SADDR) + return ae->saddr; + else + return NULL; +} + +int xfrmnl_ae_set_saddr (struct xfrmnl_ae* ae, struct nl_addr* addr) +{ + return __assign_addr(ae, &ae->saddr, addr, XFRM_AE_ATTR_SADDR, 1); +} + +int xfrmnl_ae_get_flags (struct xfrmnl_ae* ae) +{ + if (ae->ce_mask & XFRM_AE_ATTR_FLAGS) + return ae->flags; + else + return -1; +} + +int xfrmnl_ae_set_flags (struct xfrmnl_ae* ae, unsigned int flags) +{ + ae->flags = flags; + ae->ce_mask |= XFRM_AE_ATTR_FLAGS; + + return 0; +} + +int xfrmnl_ae_get_reqid (struct xfrmnl_ae* ae) +{ + if (ae->ce_mask & XFRM_AE_ATTR_REQID) + return ae->reqid; + else + return -1; +} + +int xfrmnl_ae_set_reqid (struct xfrmnl_ae* ae, unsigned int reqid) +{ + ae->reqid = reqid; + ae->ce_mask |= XFRM_AE_ATTR_REQID; + + return 0; +} + +int xfrmnl_ae_get_mark (struct xfrmnl_ae* ae, unsigned int* mark_mask, unsigned int* mark_value) +{ + if (mark_mask == NULL || mark_value == NULL) + return -1; + + if (ae->ce_mask & XFRM_AE_ATTR_MARK) + { + *mark_mask = ae->mark.m; + *mark_value = ae->mark.v; + + return 0; + } + else + return -1; +} + +int xfrmnl_ae_set_mark (struct xfrmnl_ae* ae, unsigned int value, unsigned int mask) +{ + ae->mark.v = value; + ae->mark.m = mask; + ae->ce_mask |= XFRM_AE_ATTR_MARK; + + return 0; +} + +int xfrmnl_ae_get_curlifetime (struct xfrmnl_ae* ae, unsigned long long int* curr_bytes, + unsigned long long int* curr_packets, unsigned long long int* curr_add_time, + unsigned long long int* curr_use_time) +{ + if (curr_bytes == NULL || curr_packets == NULL || curr_add_time == NULL || curr_use_time == NULL) + return -1; + + if (ae->ce_mask & XFRM_AE_ATTR_LIFETIME) + { + *curr_bytes = ae->lifetime_cur.bytes; + *curr_packets = ae->lifetime_cur.packets; + *curr_add_time = ae->lifetime_cur.add_time; + *curr_use_time = ae->lifetime_cur.use_time; + + return 0; + } + else + return -1; +} + +int xfrmnl_ae_set_curlifetime (struct xfrmnl_ae* ae, unsigned long long int curr_bytes, + unsigned long long int curr_packets, unsigned long long int curr_add_time, + unsigned long long int curr_use_time) +{ + ae->lifetime_cur.bytes = curr_bytes; + ae->lifetime_cur.packets = curr_packets; + ae->lifetime_cur.add_time = curr_add_time; + ae->lifetime_cur.use_time = curr_use_time; + ae->ce_mask |= XFRM_AE_ATTR_LIFETIME; + + return 0; +} + +int xfrmnl_ae_get_replay_maxage (struct xfrmnl_ae* ae) +{ + if (ae->ce_mask & XFRM_AE_ATTR_REPLAY_MAXAGE) + return ae->replay_maxage; + else + return -1; +} + +int xfrmnl_ae_set_replay_maxage (struct xfrmnl_ae* ae, unsigned int replay_maxage) +{ + ae->replay_maxage = replay_maxage; + ae->ce_mask |= XFRM_AE_ATTR_REPLAY_MAXAGE; + + return 0; +} + +int xfrmnl_ae_get_replay_maxdiff (struct xfrmnl_ae* ae) +{ + if (ae->ce_mask & XFRM_AE_ATTR_REPLAY_MAXDIFF) + return ae->replay_maxdiff; + else + return -1; +} + +int xfrmnl_ae_set_replay_maxdiff (struct xfrmnl_ae* ae, unsigned int replay_maxdiff) +{ + ae->replay_maxdiff = replay_maxdiff; + ae->ce_mask |= XFRM_AE_ATTR_REPLAY_MAXDIFF; + + return 0; +} + +int xfrmnl_ae_get_replay_state (struct xfrmnl_ae* ae, unsigned int* oseq, unsigned int* seq, unsigned int* bmp) +{ + if (ae->ce_mask & XFRM_AE_ATTR_REPLAY_STATE) + { + if (ae->replay_state_esn == NULL) + { + *oseq = ae->replay_state.oseq; + *seq = ae->replay_state.seq; + *bmp = ae->replay_state.bitmap; + + return 0; + } + else + { + return -1; + } + } + else + return -1; +} + +int xfrmnl_ae_set_replay_state (struct xfrmnl_ae* ae, unsigned int oseq, unsigned int seq, unsigned int bitmap) +{ + ae->replay_state.oseq = oseq; + ae->replay_state.seq = seq; + ae->replay_state.bitmap = bitmap; + ae->ce_mask |= XFRM_AE_ATTR_REPLAY_STATE; + + return 0; +} + +int xfrmnl_ae_get_replay_state_esn(struct xfrmnl_ae* ae, unsigned int* oseq, unsigned int* seq, unsigned int* oseq_hi, + unsigned int* seq_hi, unsigned int* replay_window, unsigned int* bmp_len, unsigned int* bmp) +{ + if (ae->ce_mask & XFRM_AE_ATTR_REPLAY_STATE) + { + if (ae->replay_state_esn) + { + *oseq = ae->replay_state_esn->oseq; + *seq = ae->replay_state_esn->seq; + *oseq_hi= ae->replay_state_esn->oseq_hi; + *seq_hi = ae->replay_state_esn->seq_hi; + *replay_window = ae->replay_state_esn->replay_window; + *bmp_len = ae->replay_state_esn->bmp_len; // In number of 32 bit words + memcpy (bmp, ae->replay_state_esn->bmp, ae->replay_state_esn->bmp_len * sizeof (uint32_t)); + + return 0; + } + else + { + return -1; + } + } + else + return -1; +} + +int xfrmnl_ae_set_replay_state_esn(struct xfrmnl_ae* ae, unsigned int oseq, unsigned int seq, + unsigned int oseq_hi, unsigned int seq_hi, unsigned int replay_window, + unsigned int bmp_len, unsigned int* bmp) +{ + /* Free the old replay ESN state and allocate new one */ + if (ae->replay_state_esn) + free (ae->replay_state_esn); + + if ((ae->replay_state_esn = calloc (1, sizeof (struct xfrmnl_replay_state_esn) + sizeof (uint32_t) * bmp_len)) == NULL) + return -1; + + ae->replay_state_esn->oseq = oseq; + ae->replay_state_esn->seq = seq; + ae->replay_state_esn->oseq_hi = oseq_hi; + ae->replay_state_esn->seq_hi = seq_hi; + ae->replay_state_esn->replay_window = replay_window; + ae->replay_state_esn->bmp_len = bmp_len; // In number of 32 bit words + memcpy (ae->replay_state_esn->bmp, bmp, bmp_len * sizeof (uint32_t)); + ae->ce_mask |= XFRM_AE_ATTR_REPLAY_STATE; + + return 0; +} + +/** @} */ + +static struct nl_object_ops xfrm_ae_obj_ops = { + .oo_name = "xfrm/ae", + .oo_size = sizeof(struct xfrmnl_ae), + .oo_free_data = xfrm_ae_free_data, + .oo_clone = xfrm_ae_clone, + .oo_dump = { + [NL_DUMP_LINE] = xfrm_ae_dump_line, + [NL_DUMP_DETAILS] = xfrm_ae_dump_details, + [NL_DUMP_STATS] = xfrm_ae_dump_stats, + }, + .oo_compare = xfrm_ae_compare, + .oo_attrs2str = xfrm_ae_attrs2str, + .oo_id_attrs = (XFRM_AE_ATTR_DADDR | XFRM_AE_ATTR_SPI | XFRM_AE_ATTR_PROTO), +}; + +/** @} */ + diff --git a/libnl/lib/xfrm/lifetime.c b/libnl/lib/xfrm/lifetime.c new file mode 100644 index 0000000..17c9883 --- /dev/null +++ b/libnl/lib/xfrm/lifetime.c @@ -0,0 +1,278 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * Neither the name of Texas Instruments Incorporated 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 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. + * + */ +/** + * @ingroup xfrmnl + * @defgroup XFRM Lifetime Configuration Object + * + * Abstract data type representing XFRM SA lifetime properties + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#include "nl-default.h" + +#include + +#include "nl-xfrm.h" + +static void ltime_cfg_destroy(struct xfrmnl_ltime_cfg* ltime) +{ + if (!ltime) + return; + + if (ltime->refcnt != 1) + { + fprintf(stderr, "BUG: %s:%d\n", __FILE__, __LINE__); + assert(0); + } + + free(ltime); +} + +/** + * @name Creating Selector + * @{ + */ + +/** + * Allocate new lifetime config object. + * @return Newly allocated lifetime config object or NULL + */ +struct xfrmnl_ltime_cfg* xfrmnl_ltime_cfg_alloc() +{ + struct xfrmnl_ltime_cfg* ltime; + + ltime = calloc(1, sizeof(struct xfrmnl_ltime_cfg)); + if (!ltime) + return NULL; + + ltime->refcnt = 1; + + return ltime; +} + +/** + * Clone existing lifetime config object. + * @arg ltime Selector object. + * @return Newly allocated lifetime config object being a duplicate of the + * specified lifetime config object or NULL if a failure occured. + */ +struct xfrmnl_ltime_cfg* xfrmnl_ltime_cfg_clone(struct xfrmnl_ltime_cfg* ltime) +{ + struct xfrmnl_ltime_cfg* new; + + new = xfrmnl_ltime_cfg_alloc(); + if (new) + memcpy ((void*)new, (void*)ltime, sizeof (struct xfrmnl_ltime_cfg)); + + return new; +} + +/** @} */ + +/** + * @name Managing Usage References + * @{ + */ + +struct xfrmnl_ltime_cfg* xfrmnl_ltime_cfg_get(struct xfrmnl_ltime_cfg* ltime) +{ + ltime->refcnt++; + + return ltime; +} + +void xfrmnl_ltime_cfg_put(struct xfrmnl_ltime_cfg* ltime) +{ + if (!ltime) + return; + + if (ltime->refcnt == 1) + ltime_cfg_destroy(ltime); + else + ltime->refcnt--; +} + +/** + * Check whether an lifetime config object is shared. + * @arg addr Selector object. + * @return Non-zero if the lifetime config object is shared, otherwise 0. + */ +int xfrmnl_ltime_cfg_shared(struct xfrmnl_ltime_cfg* ltime) +{ + return ltime->refcnt > 1; +} + +/** @} */ + +/** + * @name Miscellaneous + * @{ + */ + +/** + * Compares two lifetime config objects. + * @arg a A lifetime config object. + * @arg b Another lifetime config object. + * + * @return Non zero if difference is found, 0 otherwise if both + * the objects are identical. + */ +int xfrmnl_ltime_cfg_cmp(struct xfrmnl_ltime_cfg* a, struct xfrmnl_ltime_cfg* b) +{ + /* Check for any differences */ + if ((a->soft_byte_limit != b->soft_byte_limit) || + (a->soft_packet_limit != b->soft_packet_limit) || + (a->hard_byte_limit != b->hard_byte_limit) || + (a->hard_packet_limit != b->hard_packet_limit) || + (a->soft_add_expires_seconds != b->soft_add_expires_seconds) || + (a->hard_add_expires_seconds != b->hard_add_expires_seconds) || + (a->soft_use_expires_seconds != b->soft_use_expires_seconds) || + (a->hard_use_expires_seconds != b->hard_use_expires_seconds)) + return 1; + + /* The objects are identical */ + return 0; +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ +unsigned long long xfrmnl_ltime_cfg_get_soft_bytelimit (struct xfrmnl_ltime_cfg* ltime) +{ + return ltime->soft_byte_limit; +} + +int xfrmnl_ltime_cfg_set_soft_bytelimit (struct xfrmnl_ltime_cfg* ltime, unsigned long long soft_byte_limit) +{ + ltime->soft_byte_limit = soft_byte_limit; + + return 0; +} + +unsigned long long xfrmnl_ltime_cfg_get_hard_bytelimit (struct xfrmnl_ltime_cfg* ltime) +{ + return ltime->hard_byte_limit; +} + +int xfrmnl_ltime_cfg_set_hard_bytelimit (struct xfrmnl_ltime_cfg* ltime, unsigned long long hard_byte_limit) +{ + ltime->hard_byte_limit = hard_byte_limit; + + return 0; +} + +unsigned long long xfrmnl_ltime_cfg_get_soft_packetlimit (struct xfrmnl_ltime_cfg* ltime) +{ + return ltime->soft_packet_limit; +} + +int xfrmnl_ltime_cfg_set_soft_packetlimit (struct xfrmnl_ltime_cfg* ltime, unsigned long long soft_packet_limit) +{ + ltime->soft_packet_limit = soft_packet_limit; + + return 0; +} + +unsigned long long xfrmnl_ltime_cfg_get_hard_packetlimit (struct xfrmnl_ltime_cfg* ltime) +{ + return ltime->hard_packet_limit; +} + +int xfrmnl_ltime_cfg_set_hard_packetlimit (struct xfrmnl_ltime_cfg* ltime, unsigned long long hard_packet_limit) +{ + ltime->hard_packet_limit = hard_packet_limit; + + return 0; +} + +unsigned long long xfrmnl_ltime_cfg_get_soft_addexpires (struct xfrmnl_ltime_cfg* ltime) +{ + return ltime->soft_add_expires_seconds; +} + +int xfrmnl_ltime_cfg_set_soft_addexpires (struct xfrmnl_ltime_cfg* ltime, unsigned long long soft_add_expires_seconds) +{ + ltime->soft_add_expires_seconds = soft_add_expires_seconds; + + return 0; +} + +unsigned long long xfrmnl_ltime_cfg_get_hard_addexpires (struct xfrmnl_ltime_cfg* ltime) +{ + return ltime->hard_add_expires_seconds; +} + +int xfrmnl_ltime_cfg_set_hard_addexpires (struct xfrmnl_ltime_cfg* ltime, unsigned long long hard_add_expires_seconds) +{ + ltime->hard_add_expires_seconds = hard_add_expires_seconds; + + return 0; +} + +unsigned long long xfrmnl_ltime_cfg_get_soft_useexpires (struct xfrmnl_ltime_cfg* ltime) +{ + return ltime->soft_use_expires_seconds; +} + +int xfrmnl_ltime_cfg_set_soft_useexpires (struct xfrmnl_ltime_cfg* ltime, unsigned long long soft_use_expires_seconds) +{ + ltime->soft_use_expires_seconds = soft_use_expires_seconds; + + return 0; +} + +unsigned long long xfrmnl_ltime_cfg_get_hard_useexpires (struct xfrmnl_ltime_cfg* ltime) +{ + return ltime->hard_use_expires_seconds; +} + +int xfrmnl_ltime_cfg_set_hard_useexpires (struct xfrmnl_ltime_cfg* ltime, unsigned long long hard_use_expires_seconds) +{ + ltime->hard_use_expires_seconds = hard_use_expires_seconds; + + return 0; +} + +/** @} */ diff --git a/libnl/lib/xfrm/nl-xfrm.h b/libnl/lib/xfrm/nl-xfrm.h new file mode 100644 index 0000000..0469c63 --- /dev/null +++ b/libnl/lib/xfrm/nl-xfrm.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + * Copyright (c) 2013 Sassano Systems LLC + */ + +#ifndef __LIB_XFRM_NL_XFRM_H__ +#define __LIB_XFRM_NL_XFRM_H__ + +/* Lifetime configuration, used for both policy rules (SPD) and SAs. */ +struct xfrmnl_ltime_cfg { + uint32_t refcnt; + uint64_t soft_byte_limit; + uint64_t hard_byte_limit; + uint64_t soft_packet_limit; + uint64_t hard_packet_limit; + uint64_t soft_add_expires_seconds; + uint64_t hard_add_expires_seconds; + uint64_t soft_use_expires_seconds; + uint64_t hard_use_expires_seconds; +}; + +/* Current lifetime, used for both policy rules (SPD) and SAs. */ +struct xfrmnl_lifetime_cur { + uint64_t bytes; + uint64_t packets; + uint64_t add_time; + uint64_t use_time; +}; + +struct xfrmnl_replay_state { + uint32_t oseq; + uint32_t seq; + uint32_t bitmap; +}; + +struct xfrmnl_replay_state_esn { + uint32_t bmp_len; + uint32_t oseq; + uint32_t seq; + uint32_t oseq_hi; + uint32_t seq_hi; + uint32_t replay_window; + uint32_t bmp[0]; +}; + +struct xfrmnl_mark { + uint32_t v; /* value */ + uint32_t m; /* mask */ +}; + +struct xfrmnl_id { + struct nl_addr *daddr; + uint32_t spi; + uint8_t proto; +}; + +struct xfrmnl_user_sec_ctx { + uint16_t len; + uint16_t exttype; + uint8_t ctx_alg; + uint8_t ctx_doi; + uint16_t ctx_len; + char ctx[0]; +}; + +struct xfrmnl_user_tmpl { + struct xfrmnl_id id; + uint16_t family; + struct nl_addr *saddr; + uint32_t reqid; + uint8_t mode; + uint8_t share; + uint8_t optional; + uint32_t aalgos; + uint32_t ealgos; + uint32_t calgos; + struct nl_list_head utmpl_list; +}; + +#endif /* __LIB_XFRM_NL_XFRM_H__ */ diff --git a/libnl/lib/xfrm/sa.c b/libnl/lib/xfrm/sa.c new file mode 100644 index 0000000..a9256d9 --- /dev/null +++ b/libnl/lib/xfrm/sa.c @@ -0,0 +1,2383 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * Neither the name of Texas Instruments Incorporated 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 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. + * + */ + +/** + * @ingroup xfrmnl + * @defgroup sa Security Association + * @brief + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "nl-xfrm.h" +#include "nl-priv-dynamic-core/object-api.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" +#include "nl-aux-core/nl-core.h" +#include "nl-aux-xfrm/nl-xfrm.h" + +/** @cond SKIP */ + +struct xfrmnl_stats { + uint32_t replay_window; + uint32_t replay; + uint32_t integrity_failed; +}; + +struct xfrmnl_algo_aead { + char alg_name[64]; + uint32_t alg_key_len; /* in bits */ + uint32_t alg_icv_len; /* in bits */ + char alg_key[0]; +}; + +struct xfrmnl_algo_auth { + char alg_name[64]; + uint32_t alg_key_len; /* in bits */ + uint32_t alg_trunc_len; /* in bits */ + char alg_key[0]; +}; + +struct xfrmnl_algo { + char alg_name[64]; + uint32_t alg_key_len; /* in bits */ + char alg_key[0]; +}; + +struct xfrmnl_encap_tmpl { + uint16_t encap_type; + uint16_t encap_sport; + uint16_t encap_dport; + struct nl_addr* encap_oa; +}; + +struct xfrmnl_user_offload { + int ifindex; + uint8_t flags; +}; + +struct xfrmnl_sa { + NLHDR_COMMON + + struct xfrmnl_sel* sel; + struct xfrmnl_id id; + struct nl_addr* saddr; + struct xfrmnl_ltime_cfg* lft; + struct xfrmnl_lifetime_cur curlft; + struct xfrmnl_stats stats; + uint32_t seq; + uint32_t reqid; + uint16_t family; + uint8_t mode; /* XFRM_MODE_xxx */ + uint8_t replay_window; + uint8_t flags; + struct xfrmnl_algo_aead* aead; + struct xfrmnl_algo_auth* auth; + struct xfrmnl_algo* crypt; + struct xfrmnl_algo* comp; + struct xfrmnl_encap_tmpl* encap; + uint32_t tfcpad; + struct nl_addr* coaddr; + struct xfrmnl_mark mark; + struct xfrmnl_user_sec_ctx* sec_ctx; + uint32_t replay_maxage; + uint32_t replay_maxdiff; + struct xfrmnl_replay_state replay_state; + struct xfrmnl_replay_state_esn* replay_state_esn; + uint8_t hard; + struct xfrmnl_user_offload* user_offload; +}; + +#define XFRM_SA_ATTR_SEL 0x01 +#define XFRM_SA_ATTR_DADDR 0x02 +#define XFRM_SA_ATTR_SPI 0x04 +#define XFRM_SA_ATTR_PROTO 0x08 +#define XFRM_SA_ATTR_SADDR 0x10 +#define XFRM_SA_ATTR_LTIME_CFG 0x20 +#define XFRM_SA_ATTR_LTIME_CUR 0x40 +#define XFRM_SA_ATTR_STATS 0x80 +#define XFRM_SA_ATTR_SEQ 0x100 +#define XFRM_SA_ATTR_REQID 0x200 +#define XFRM_SA_ATTR_FAMILY 0x400 +#define XFRM_SA_ATTR_MODE 0x800 +#define XFRM_SA_ATTR_REPLAY_WIN 0x1000 +#define XFRM_SA_ATTR_FLAGS 0x2000 +#define XFRM_SA_ATTR_ALG_AEAD 0x4000 +#define XFRM_SA_ATTR_ALG_AUTH 0x8000 +#define XFRM_SA_ATTR_ALG_CRYPT 0x10000 +#define XFRM_SA_ATTR_ALG_COMP 0x20000 +#define XFRM_SA_ATTR_ENCAP 0x40000 +#define XFRM_SA_ATTR_TFCPAD 0x80000 +#define XFRM_SA_ATTR_COADDR 0x100000 +#define XFRM_SA_ATTR_MARK 0x200000 +#define XFRM_SA_ATTR_SECCTX 0x400000 +#define XFRM_SA_ATTR_REPLAY_MAXAGE 0x800000 +#define XFRM_SA_ATTR_REPLAY_MAXDIFF 0x1000000 +#define XFRM_SA_ATTR_REPLAY_STATE 0x2000000 +#define XFRM_SA_ATTR_EXPIRE 0x4000000 +#define XFRM_SA_ATTR_OFFLOAD_DEV 0x8000000 + +static struct nl_cache_ops xfrmnl_sa_ops; +static struct nl_object_ops xfrm_sa_obj_ops; +/** @endcond */ + +static void xfrm_sa_alloc_data(struct nl_object *c) +{ + struct xfrmnl_sa* sa = nl_object_priv (c); + + if ((sa->sel = xfrmnl_sel_alloc ()) == NULL) + return; + + if ((sa->lft = xfrmnl_ltime_cfg_alloc ()) == NULL) + return; +} + +static void xfrm_sa_free_data(struct nl_object *c) +{ + struct xfrmnl_sa* sa = nl_object_priv (c); + + if (sa == NULL) + return; + + xfrmnl_sel_put (sa->sel); + xfrmnl_ltime_cfg_put (sa->lft); + nl_addr_put (sa->id.daddr); + nl_addr_put (sa->saddr); + + if (sa->aead) + free (sa->aead); + if (sa->auth) + free (sa->auth); + if (sa->crypt) + free (sa->crypt); + if (sa->comp) + free (sa->comp); + if (sa->encap) { + if (sa->encap->encap_oa) + nl_addr_put(sa->encap->encap_oa); + free(sa->encap); + } + if (sa->coaddr) + nl_addr_put (sa->coaddr); + if (sa->sec_ctx) + free (sa->sec_ctx); + if (sa->replay_state_esn) + free (sa->replay_state_esn); + if (sa->user_offload) + free(sa->user_offload); +} + +static int xfrm_sa_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct xfrmnl_sa* dst = nl_object_priv(_dst); + struct xfrmnl_sa* src = nl_object_priv(_src); + uint32_t len = 0; + + dst->sel = NULL; + dst->id.daddr = NULL; + dst->saddr = NULL; + dst->lft = NULL; + dst->aead = NULL; + dst->auth = NULL; + dst->crypt = NULL; + dst->comp = NULL; + dst->encap = NULL; + dst->coaddr = NULL; + dst->sec_ctx = NULL; + dst->replay_state_esn = NULL; + dst->user_offload = NULL; + + if (src->sel) + if ((dst->sel = xfrmnl_sel_clone (src->sel)) == NULL) + return -NLE_NOMEM; + + if (src->lft) + if ((dst->lft = xfrmnl_ltime_cfg_clone (src->lft)) == NULL) + return -NLE_NOMEM; + + if (src->id.daddr) + if ((dst->id.daddr = nl_addr_clone (src->id.daddr)) == NULL) + return -NLE_NOMEM; + + if (src->saddr) + if ((dst->saddr = nl_addr_clone (src->saddr)) == NULL) + return -NLE_NOMEM; + + if (src->aead) { + len = sizeof (struct xfrmnl_algo_aead) + ((src->aead->alg_key_len + 7) / 8); + if ((dst->aead = calloc (1, len)) == NULL) + return -NLE_NOMEM; + memcpy ((void *)dst->aead, (void *)src->aead, len); + } + + if (src->auth) { + len = sizeof (struct xfrmnl_algo_auth) + ((src->auth->alg_key_len + 7) / 8); + if ((dst->auth = calloc (1, len)) == NULL) + return -NLE_NOMEM; + memcpy ((void *)dst->auth, (void *)src->auth, len); + } + + if (src->crypt) { + len = sizeof (struct xfrmnl_algo) + ((src->crypt->alg_key_len + 7) / 8); + if ((dst->crypt = calloc (1, len)) == NULL) + return -NLE_NOMEM; + memcpy ((void *)dst->crypt, (void *)src->crypt, len); + } + + if (src->comp) { + len = sizeof (struct xfrmnl_algo) + ((src->comp->alg_key_len + 7) / 8); + if ((dst->comp = calloc (1, len)) == NULL) + return -NLE_NOMEM; + memcpy ((void *)dst->comp, (void *)src->comp, len); + } + + if (src->encap) { + len = sizeof (struct xfrmnl_encap_tmpl); + if ((dst->encap = calloc (1, len)) == NULL) + return -NLE_NOMEM; + memcpy ((void *)dst->encap, (void *)src->encap, len); + } + + if (src->coaddr) + if ((dst->coaddr = nl_addr_clone (src->coaddr)) == NULL) + return -NLE_NOMEM; + + if (src->sec_ctx) { + len = sizeof (*src->sec_ctx) + src->sec_ctx->ctx_len; + if ((dst->sec_ctx = calloc (1, len)) == NULL) + return -NLE_NOMEM; + memcpy ((void *)dst->sec_ctx, (void *)src->sec_ctx, len); + } + + if (src->replay_state_esn) { + len = sizeof (struct xfrmnl_replay_state_esn) + (src->replay_state_esn->bmp_len * sizeof (uint32_t)); + if ((dst->replay_state_esn = calloc (1, len)) == NULL) + return -NLE_NOMEM; + memcpy ((void *)dst->replay_state_esn, (void *)src->replay_state_esn, len); + } + + if (src->user_offload) { + dst->user_offload = _nl_memdup_ptr(src->user_offload); + if (!dst->user_offload) + return -NLE_NOMEM; + } + + return 0; +} + +static uint64_t xfrm_sa_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct xfrmnl_sa* a = (struct xfrmnl_sa *) _a; + struct xfrmnl_sa* b = (struct xfrmnl_sa *) _b; + uint64_t diff = 0; + int found = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(XFRM_SA_ATTR_SEL, xfrmnl_sel_cmp(a->sel, b->sel)); + diff |= _DIFF(XFRM_SA_ATTR_DADDR, + nl_addr_cmp(a->id.daddr, b->id.daddr)); + diff |= _DIFF(XFRM_SA_ATTR_SPI, a->id.spi != b->id.spi); + diff |= _DIFF(XFRM_SA_ATTR_PROTO, a->id.proto != b->id.proto); + diff |= _DIFF(XFRM_SA_ATTR_SADDR, nl_addr_cmp(a->saddr, b->saddr)); + diff |= _DIFF(XFRM_SA_ATTR_LTIME_CFG, + xfrmnl_ltime_cfg_cmp(a->lft, b->lft)); + diff |= _DIFF(XFRM_SA_ATTR_REQID, a->reqid != b->reqid); + diff |= _DIFF(XFRM_SA_ATTR_FAMILY, a->family != b->family); + diff |= _DIFF(XFRM_SA_ATTR_MODE, a->mode != b->mode); + diff |= _DIFF(XFRM_SA_ATTR_REPLAY_WIN, + a->replay_window != b->replay_window); + diff |= _DIFF(XFRM_SA_ATTR_FLAGS, a->flags != b->flags); + diff |= _DIFF(XFRM_SA_ATTR_ALG_AEAD, + (strcmp(a->aead->alg_name, b->aead->alg_name) || + (a->aead->alg_key_len != b->aead->alg_key_len) || + (a->aead->alg_icv_len != b->aead->alg_icv_len) || + memcmp(a->aead->alg_key, b->aead->alg_key, + ((a->aead->alg_key_len + 7) / 8)))); + diff |= _DIFF(XFRM_SA_ATTR_ALG_AUTH, + (strcmp(a->auth->alg_name, b->auth->alg_name) || + (a->auth->alg_key_len != b->auth->alg_key_len) || + (a->auth->alg_trunc_len != b->auth->alg_trunc_len) || + memcmp(a->auth->alg_key, b->auth->alg_key, + ((a->auth->alg_key_len + 7) / 8)))); + diff |= _DIFF(XFRM_SA_ATTR_ALG_CRYPT, + (strcmp(a->crypt->alg_name, b->crypt->alg_name) || + (a->crypt->alg_key_len != b->crypt->alg_key_len) || + memcmp(a->crypt->alg_key, b->crypt->alg_key, + ((a->crypt->alg_key_len + 7) / 8)))); + diff |= _DIFF(XFRM_SA_ATTR_ALG_COMP, + (strcmp(a->comp->alg_name, b->comp->alg_name) || + (a->comp->alg_key_len != b->comp->alg_key_len) || + memcmp(a->comp->alg_key, b->comp->alg_key, + ((a->comp->alg_key_len + 7) / 8)))); + diff |= _DIFF(XFRM_SA_ATTR_ENCAP, + ((a->encap->encap_type != b->encap->encap_type) || + (a->encap->encap_sport != b->encap->encap_sport) || + (a->encap->encap_dport != b->encap->encap_dport) || + nl_addr_cmp(a->encap->encap_oa, b->encap->encap_oa))); + diff |= _DIFF(XFRM_SA_ATTR_TFCPAD, a->tfcpad != b->tfcpad); + diff |= _DIFF(XFRM_SA_ATTR_COADDR, nl_addr_cmp(a->coaddr, b->coaddr)); + diff |= _DIFF(XFRM_SA_ATTR_MARK, + (a->mark.m != b->mark.m) || (a->mark.v != b->mark.v)); + diff |= _DIFF(XFRM_SA_ATTR_SECCTX, + ((a->sec_ctx->ctx_doi != b->sec_ctx->ctx_doi) || + (a->sec_ctx->ctx_alg != b->sec_ctx->ctx_alg) || + (a->sec_ctx->ctx_len != b->sec_ctx->ctx_len) || + strcmp(a->sec_ctx->ctx, b->sec_ctx->ctx))); + diff |= _DIFF(XFRM_SA_ATTR_REPLAY_MAXAGE, + a->replay_maxage != b->replay_maxage); + diff |= _DIFF(XFRM_SA_ATTR_REPLAY_MAXDIFF, + a->replay_maxdiff != b->replay_maxdiff); + diff |= _DIFF(XFRM_SA_ATTR_EXPIRE, a->hard != b->hard); + + /* Compare replay states */ + found = AVAILABLE_MISMATCH (a, b, XFRM_SA_ATTR_REPLAY_STATE); + if (found == 0) // attribute exists in both objects + { + if (((a->replay_state_esn != NULL) && (b->replay_state_esn == NULL)) || + ((a->replay_state_esn == NULL) && (b->replay_state_esn != NULL))) + found |= 1; + + if (found == 0) // same replay type. compare actual values + { + if (a->replay_state_esn) + { + if (a->replay_state_esn->bmp_len != b->replay_state_esn->bmp_len) + diff |= 1; + else + { + uint32_t len = sizeof (struct xfrmnl_replay_state_esn) + + (a->replay_state_esn->bmp_len * sizeof (uint32_t)); + diff |= memcmp (a->replay_state_esn, b->replay_state_esn, len); + } + } + else + { + if ((a->replay_state.oseq != b->replay_state.oseq) || + (a->replay_state.seq != b->replay_state.seq) || + (a->replay_state.bitmap != b->replay_state.bitmap)) + diff |= 1; + } + } + } +#undef _DIFF + + return diff; +} + +/** + * @name XFRM SA Attribute Translations + * @{ + */ +static const struct trans_tbl sa_attrs[] = { + __ADD(XFRM_SA_ATTR_SEL, selector), + __ADD(XFRM_SA_ATTR_DADDR, daddr), + __ADD(XFRM_SA_ATTR_SPI, spi), + __ADD(XFRM_SA_ATTR_PROTO, proto), + __ADD(XFRM_SA_ATTR_SADDR, saddr), + __ADD(XFRM_SA_ATTR_LTIME_CFG, lifetime_cfg), + __ADD(XFRM_SA_ATTR_LTIME_CUR, lifetime_cur), + __ADD(XFRM_SA_ATTR_STATS, stats), + __ADD(XFRM_SA_ATTR_SEQ, seqnum), + __ADD(XFRM_SA_ATTR_REQID, reqid), + __ADD(XFRM_SA_ATTR_FAMILY, family), + __ADD(XFRM_SA_ATTR_MODE, mode), + __ADD(XFRM_SA_ATTR_REPLAY_WIN, replay_window), + __ADD(XFRM_SA_ATTR_FLAGS, flags), + __ADD(XFRM_SA_ATTR_ALG_AEAD, alg_aead), + __ADD(XFRM_SA_ATTR_ALG_AUTH, alg_auth), + __ADD(XFRM_SA_ATTR_ALG_CRYPT, alg_crypto), + __ADD(XFRM_SA_ATTR_ALG_COMP, alg_comp), + __ADD(XFRM_SA_ATTR_ENCAP, encap), + __ADD(XFRM_SA_ATTR_TFCPAD, tfcpad), + __ADD(XFRM_SA_ATTR_COADDR, coaddr), + __ADD(XFRM_SA_ATTR_MARK, mark), + __ADD(XFRM_SA_ATTR_SECCTX, sec_ctx), + __ADD(XFRM_SA_ATTR_REPLAY_MAXAGE, replay_maxage), + __ADD(XFRM_SA_ATTR_REPLAY_MAXDIFF, replay_maxdiff), + __ADD(XFRM_SA_ATTR_REPLAY_STATE, replay_state), + __ADD(XFRM_SA_ATTR_EXPIRE, expire), + __ADD(XFRM_SA_ATTR_OFFLOAD_DEV, user_offload), +}; + +static char* xfrm_sa_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str (attrs, buf, len, sa_attrs, ARRAY_SIZE(sa_attrs)); +} +/** @} */ + +/** + * @name XFRM SA Flags Translations + * @{ + */ +static const struct trans_tbl sa_flags[] = { + __ADD(XFRM_STATE_NOECN, no ecn), + __ADD(XFRM_STATE_DECAP_DSCP, decap dscp), + __ADD(XFRM_STATE_NOPMTUDISC, no pmtu discovery), + __ADD(XFRM_STATE_WILDRECV, wild receive), + __ADD(XFRM_STATE_ICMP, icmp), + __ADD(XFRM_STATE_AF_UNSPEC, unspecified), + __ADD(XFRM_STATE_ALIGN4, align4), + __ADD(XFRM_STATE_ESN, esn), +}; + +char* xfrmnl_sa_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str (flags, buf, len, sa_flags, ARRAY_SIZE(sa_flags)); +} + +int xfrmnl_sa_str2flag(const char *name) +{ + return __str2flags (name, sa_flags, ARRAY_SIZE(sa_flags)); +} +/** @} */ + +/** + * @name XFRM SA Mode Translations + * @{ + */ +static const struct trans_tbl sa_modes[] = { + __ADD(XFRM_MODE_TRANSPORT, transport), + __ADD(XFRM_MODE_TUNNEL, tunnel), + __ADD(XFRM_MODE_ROUTEOPTIMIZATION, route optimization), + __ADD(XFRM_MODE_IN_TRIGGER, in trigger), + __ADD(XFRM_MODE_BEET, beet), +}; + +char* xfrmnl_sa_mode2str(int mode, char *buf, size_t len) +{ + return __type2str (mode, buf, len, sa_modes, ARRAY_SIZE(sa_modes)); +} + +int xfrmnl_sa_str2mode(const char *name) +{ + return __str2type (name, sa_modes, ARRAY_SIZE(sa_modes)); +} +/** @} */ + + +static void xfrm_sa_dump_line(struct nl_object *a, struct nl_dump_params *p) +{ + char dst[INET6_ADDRSTRLEN+5], src[INET6_ADDRSTRLEN+5]; + struct xfrmnl_sa* sa = (struct xfrmnl_sa *) a; + char flags[128], mode[128]; + time_t add_time, use_time; + struct tm *add_time_tm, *use_time_tm; + struct tm tm_buf; + + nl_dump_line(p, "src %s dst %s family: %s\n", nl_addr2str(sa->saddr, src, sizeof(src)), + nl_addr2str(sa->id.daddr, dst, sizeof(dst)), + nl_af2str (sa->family, flags, sizeof (flags))); + + nl_dump_line(p, "\tproto %s spi 0x%x reqid %u\n", + nl_ip_proto2str (sa->id.proto, flags, sizeof(flags)), + sa->id.spi, sa->reqid); + + xfrmnl_sa_flags2str(sa->flags, flags, sizeof (flags)); + xfrmnl_sa_mode2str(sa->mode, mode, sizeof (mode)); + nl_dump_line(p, "\tmode: %s flags: %s (0x%x) seq: %u replay window: %u\n", + mode, flags, sa->flags, sa->seq, sa->replay_window); + + nl_dump_line(p, "\tlifetime configuration: \n"); + if (sa->lft->soft_byte_limit == XFRM_INF) + sprintf (flags, "INF"); + else + sprintf (flags, "%" PRIu64, sa->lft->soft_byte_limit); + if (sa->lft->soft_packet_limit == XFRM_INF) + sprintf (mode, "INF"); + else + sprintf (mode, "%" PRIu64, sa->lft->soft_packet_limit); + nl_dump_line(p, "\t\tsoft limit: %s (bytes), %s (packets)\n", flags, mode); + if (sa->lft->hard_byte_limit == XFRM_INF) + sprintf (flags, "INF"); + else + sprintf (flags, "%" PRIu64, sa->lft->hard_byte_limit); + if (sa->lft->hard_packet_limit == XFRM_INF) + sprintf (mode, "INF"); + else + sprintf (mode, "%" PRIu64, sa->lft->hard_packet_limit); + nl_dump_line(p, "\t\thard limit: %s (bytes), %s (packets)\n", flags, + mode); + nl_dump_line( + p, + "\t\tsoft add_time: %llu (seconds), soft use_time: %llu (seconds) \n", + (long long unsigned)sa->lft->soft_add_expires_seconds, + (long long unsigned)sa->lft->soft_use_expires_seconds); + nl_dump_line( + p, + "\t\thard add_time: %llu (seconds), hard use_time: %llu (seconds) \n", + (long long unsigned)sa->lft->hard_add_expires_seconds, + (long long unsigned)sa->lft->hard_use_expires_seconds); + + nl_dump_line(p, "\tlifetime current: \n"); + nl_dump_line(p, "\t\t%llu bytes, %llu packets\n", + (long long unsigned)sa->curlft.bytes, + (long long unsigned)sa->curlft.packets); + if (sa->curlft.add_time != 0) + { + add_time = sa->curlft.add_time; + add_time_tm = gmtime_r (&add_time, &tm_buf); + strftime (flags, 128, "%Y-%m-%d %H-%M-%S", add_time_tm); + } + else + { + sprintf (flags, "%s", "-"); + } + + if (sa->curlft.use_time != 0) + { + use_time = sa->curlft.use_time; + use_time_tm = gmtime_r (&use_time, &tm_buf); + strftime (mode, 128, "%Y-%m-%d %H-%M-%S", use_time_tm); + } + else + { + sprintf (mode, "%s", "-"); + } + nl_dump_line(p, "\t\tadd_time: %s, use_time: %s\n", flags, mode); + + if (sa->aead) + { + nl_dump_line(p, "\tAEAD Algo: \n"); + nl_dump_line(p, "\t\tName: %s Key len(bits): %u ICV Len(bits): %u\n", + sa->aead->alg_name, sa->aead->alg_key_len, sa->aead->alg_icv_len); + } + + if (sa->auth) + { + nl_dump_line(p, "\tAuth Algo: \n"); + nl_dump_line(p, "\t\tName: %s Key len(bits): %u Trunc len(bits): %u\n", + sa->auth->alg_name, sa->auth->alg_key_len, sa->auth->alg_trunc_len); + } + + if (sa->crypt) + { + nl_dump_line(p, "\tEncryption Algo: \n"); + nl_dump_line(p, "\t\tName: %s Key len(bits): %u\n", + sa->crypt->alg_name, sa->crypt->alg_key_len); + } + + if (sa->comp) + { + nl_dump_line(p, "\tCompression Algo: \n"); + nl_dump_line(p, "\t\tName: %s Key len(bits): %u\n", + sa->comp->alg_name, sa->comp->alg_key_len); + } + + if (sa->encap) + { + nl_dump_line(p, "\tEncapsulation template: \n"); + nl_dump_line(p, "\t\tType: %d Src port: %d Dst port: %d Encap addr: %s\n", + sa->encap->encap_type, sa->encap->encap_sport, sa->encap->encap_dport, + nl_addr2str (sa->encap->encap_oa, dst, sizeof (dst))); + } + + if (sa->ce_mask & XFRM_SA_ATTR_TFCPAD) + nl_dump_line(p, "\tTFC Pad: %u\n", sa->tfcpad); + + if (sa->ce_mask & XFRM_SA_ATTR_COADDR) + nl_dump_line(p, "\tCO Address: %s\n", nl_addr2str (sa->coaddr, dst, sizeof (dst))); + + if (sa->ce_mask & XFRM_SA_ATTR_MARK) + nl_dump_line(p, "\tMark mask: 0x%x Mark value: 0x%x\n", sa->mark.m, sa->mark.v); + + if (sa->ce_mask & XFRM_SA_ATTR_SECCTX) + nl_dump_line(p, "\tDOI: %d Algo: %d Len: %u ctx: %s\n", sa->sec_ctx->ctx_doi, + sa->sec_ctx->ctx_alg, sa->sec_ctx->ctx_len, sa->sec_ctx->ctx); + + nl_dump_line(p, "\treplay info: \n"); + nl_dump_line(p, "\t\tmax age %u max diff %u \n", sa->replay_maxage, sa->replay_maxdiff); + + if (sa->ce_mask & XFRM_SA_ATTR_REPLAY_STATE) + { + nl_dump_line(p, "\treplay state info: \n"); + if (sa->replay_state_esn) + { + nl_dump_line(p, "\t\toseq %u seq %u oseq_hi %u seq_hi %u replay window: %u \n", + sa->replay_state_esn->oseq, sa->replay_state_esn->seq, + sa->replay_state_esn->oseq_hi, sa->replay_state_esn->seq_hi, + sa->replay_state_esn->replay_window); + } + else + { + nl_dump_line(p, "\t\toseq %u seq %u bitmap: %u \n", sa->replay_state.oseq, + sa->replay_state.seq, sa->replay_state.bitmap); + } + } + + nl_dump_line(p, "\tselector info: \n"); + xfrmnl_sel_dump (sa->sel, p); + + nl_dump_line(p, "\tHard: %d\n", sa->hard); + + nl_dump(p, "\n"); +} + +static void xfrm_sa_dump_stats(struct nl_object *a, struct nl_dump_params *p) +{ + struct xfrmnl_sa* sa = (struct xfrmnl_sa*)a; + + nl_dump_line(p, "\tstats: \n"); + nl_dump_line(p, "\t\treplay window: %u replay: %u integrity failed: %u \n", + sa->stats.replay_window, sa->stats.replay, sa->stats.integrity_failed); + + return; +} + +static void xfrm_sa_dump_details(struct nl_object *a, struct nl_dump_params *p) +{ + xfrm_sa_dump_line(a, p); + xfrm_sa_dump_stats (a, p); +} + +/** + * @name XFRM SA Object Allocation/Freeage + * @{ + */ + +struct xfrmnl_sa* xfrmnl_sa_alloc(void) +{ + return (struct xfrmnl_sa*) nl_object_alloc(&xfrm_sa_obj_ops); +} + +void xfrmnl_sa_put(struct xfrmnl_sa* sa) +{ + nl_object_put((struct nl_object *) sa); +} + +/** @} */ + +/** + * @name SA Cache Managament + * @{ + */ + +/** + * Build a SA cache including all SAs currently configured in the kernel. + * @arg sock Netlink socket. + * @arg result Pointer to store resulting cache. + * + * Allocates a new SA cache, initializes it properly and updates it + * to include all SAs currently configured in the kernel. + * + * @return 0 on success or a negative error code. + */ +int xfrmnl_sa_alloc_cache(struct nl_sock *sock, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&xfrmnl_sa_ops, sock, result); +} + +/** + * Look up a SA by destination address, SPI, protocol + * @arg cache SA cache + * @arg daddr destination address of the SA + * @arg spi SPI + * @arg proto protocol + * @return sa handle or NULL if no match was found. + */ +struct xfrmnl_sa* xfrmnl_sa_get(struct nl_cache* cache, struct nl_addr* daddr, + unsigned int spi, unsigned int proto) +{ + struct xfrmnl_sa *sa; + + //nl_list_for_each_entry(sa, &cache->c_items, ce_list) { + for (sa = (struct xfrmnl_sa*)nl_cache_get_first (cache); + sa != NULL; + sa = (struct xfrmnl_sa*)nl_cache_get_next ((struct nl_object*)sa)) + { + if (sa->id.proto == proto && + sa->id.spi == spi && + !nl_addr_cmp(sa->id.daddr, daddr)) + { + nl_object_get((struct nl_object *) sa); + return sa; + } + + } + + return NULL; +} + + +/** @} */ + + +static struct nla_policy xfrm_sa_policy[XFRMA_MAX+1] = { + [XFRMA_SA] = { .minlen = sizeof(struct xfrm_usersa_info)}, + [XFRMA_ALG_AUTH_TRUNC] = { .minlen = sizeof(struct xfrm_algo_auth)}, + [XFRMA_ALG_AEAD] = { .minlen = sizeof(struct xfrm_algo_aead) }, + [XFRMA_ALG_AUTH] = { .minlen = sizeof(struct xfrm_algo) }, + [XFRMA_ALG_CRYPT] = { .minlen = sizeof(struct xfrm_algo) }, + [XFRMA_ALG_COMP] = { .minlen = sizeof(struct xfrm_algo) }, + [XFRMA_ENCAP] = { .minlen = sizeof(struct xfrm_encap_tmpl) }, + [XFRMA_TMPL] = { .minlen = sizeof(struct xfrm_user_tmpl) }, + [XFRMA_SEC_CTX] = { .minlen = sizeof(struct xfrm_sec_ctx) }, + [XFRMA_LTIME_VAL] = { .minlen = sizeof(struct xfrm_lifetime_cur) }, + [XFRMA_REPLAY_VAL] = { .minlen = sizeof(struct xfrm_replay_state) }, + [XFRMA_OFFLOAD_DEV] = { .minlen = sizeof(struct xfrm_user_offload) }, + [XFRMA_REPLAY_THRESH] = { .type = NLA_U32 }, + [XFRMA_ETIMER_THRESH] = { .type = NLA_U32 }, + [XFRMA_SRCADDR] = { .minlen = sizeof(xfrm_address_t) }, + [XFRMA_COADDR] = { .minlen = sizeof(xfrm_address_t) }, + [XFRMA_MARK] = { .minlen = sizeof(struct xfrm_mark) }, + [XFRMA_TFCPAD] = { .type = NLA_U32 }, + [XFRMA_REPLAY_ESN_VAL] = { .minlen = sizeof(struct xfrm_replay_state_esn) }, +}; + +static int xfrm_sa_request_update(struct nl_cache *c, struct nl_sock *h) +{ + return nl_send_simple (h, XFRM_MSG_GETSA, NLM_F_DUMP, NULL, 0); +} + +int xfrmnl_sa_parse(struct nlmsghdr *n, struct xfrmnl_sa **result) +{ + _nl_auto_nl_addr struct nl_addr *addr1 = NULL; + _nl_auto_nl_addr struct nl_addr *addr2 = NULL; + _nl_auto_xfrmnl_sa struct xfrmnl_sa *sa = NULL; + struct nlattr *tb[XFRMA_MAX + 1]; + struct xfrm_usersa_info* sa_info; + struct xfrm_user_expire* ue; + int len, err; + + sa = xfrmnl_sa_alloc(); + if (!sa) + return -NLE_NOMEM; + + sa->ce_msgtype = n->nlmsg_type; + if (n->nlmsg_type == XFRM_MSG_EXPIRE) + { + ue = nlmsg_data(n); + sa_info = &ue->state; + sa->hard = ue->hard; + sa->ce_mask |= XFRM_SA_ATTR_EXPIRE; + } + else if (n->nlmsg_type == XFRM_MSG_DELSA) + { + sa_info = (struct xfrm_usersa_info*)((char *)nlmsg_data(n) + sizeof (struct xfrm_usersa_id) + NLA_HDRLEN); + } + else + { + sa_info = nlmsg_data(n); + } + + err = nlmsg_parse(n, sizeof(struct xfrm_usersa_info), tb, XFRMA_MAX, xfrm_sa_policy); + if (err < 0) + return err; + + if (!(addr1 = _nl_addr_build(sa_info->sel.family, &sa_info->sel.daddr))) + return -NLE_NOMEM; + nl_addr_set_prefixlen (addr1, sa_info->sel.prefixlen_d); + xfrmnl_sel_set_daddr (sa->sel, addr1); + xfrmnl_sel_set_prefixlen_d (sa->sel, sa_info->sel.prefixlen_d); + + if (!(addr2 = _nl_addr_build(sa_info->sel.family, &sa_info->sel.saddr))) + return -NLE_NOMEM; + nl_addr_set_prefixlen (addr2, sa_info->sel.prefixlen_s); + xfrmnl_sel_set_saddr (sa->sel, addr2); + xfrmnl_sel_set_prefixlen_s (sa->sel, sa_info->sel.prefixlen_s); + + xfrmnl_sel_set_dport (sa->sel, ntohs(sa_info->sel.dport)); + xfrmnl_sel_set_dportmask (sa->sel, ntohs(sa_info->sel.dport_mask)); + xfrmnl_sel_set_sport (sa->sel, ntohs(sa_info->sel.sport)); + xfrmnl_sel_set_sportmask (sa->sel, ntohs(sa_info->sel.sport_mask)); + xfrmnl_sel_set_family (sa->sel, sa_info->sel.family); + xfrmnl_sel_set_proto (sa->sel, sa_info->sel.proto); + xfrmnl_sel_set_ifindex (sa->sel, sa_info->sel.ifindex); + xfrmnl_sel_set_userid (sa->sel, sa_info->sel.user); + sa->ce_mask |= XFRM_SA_ATTR_SEL; + + if (!(sa->id.daddr = _nl_addr_build(sa_info->family, &sa_info->id.daddr))) + return -NLE_NOMEM; + sa->id.spi = ntohl(sa_info->id.spi); + sa->id.proto = sa_info->id.proto; + sa->ce_mask |= (XFRM_SA_ATTR_DADDR | XFRM_SA_ATTR_SPI | XFRM_SA_ATTR_PROTO); + + if (!(sa->saddr = _nl_addr_build(sa_info->family, &sa_info->saddr))) + return -NLE_NOMEM; + sa->ce_mask |= XFRM_SA_ATTR_SADDR; + + sa->lft->soft_byte_limit = sa_info->lft.soft_byte_limit; + sa->lft->hard_byte_limit = sa_info->lft.hard_byte_limit; + sa->lft->soft_packet_limit = sa_info->lft.soft_packet_limit; + sa->lft->hard_packet_limit = sa_info->lft.hard_packet_limit; + sa->lft->soft_add_expires_seconds = sa_info->lft.soft_add_expires_seconds; + sa->lft->hard_add_expires_seconds = sa_info->lft.hard_add_expires_seconds; + sa->lft->soft_use_expires_seconds = sa_info->lft.soft_use_expires_seconds; + sa->lft->hard_use_expires_seconds = sa_info->lft.hard_use_expires_seconds; + sa->ce_mask |= XFRM_SA_ATTR_LTIME_CFG; + + sa->curlft.bytes = sa_info->curlft.bytes; + sa->curlft.packets = sa_info->curlft.packets; + sa->curlft.add_time = sa_info->curlft.add_time; + sa->curlft.use_time = sa_info->curlft.use_time; + sa->ce_mask |= XFRM_SA_ATTR_LTIME_CUR; + + sa->stats.replay_window = sa_info->stats.replay_window; + sa->stats.replay = sa_info->stats.replay; + sa->stats.integrity_failed = sa_info->stats.integrity_failed; + sa->ce_mask |= XFRM_SA_ATTR_STATS; + + sa->seq = sa_info->seq; + sa->reqid = sa_info->reqid; + sa->family = sa_info->family; + sa->mode = sa_info->mode; + sa->replay_window = sa_info->replay_window; + sa->flags = sa_info->flags; + sa->ce_mask |= (XFRM_SA_ATTR_SEQ | XFRM_SA_ATTR_REQID | + XFRM_SA_ATTR_FAMILY | XFRM_SA_ATTR_MODE | + XFRM_SA_ATTR_REPLAY_WIN | XFRM_SA_ATTR_FLAGS); + + if (tb[XFRMA_ALG_AEAD]) { + struct xfrm_algo_aead* aead = nla_data(tb[XFRMA_ALG_AEAD]); + + len = sizeof (struct xfrmnl_algo_aead) + ((aead->alg_key_len + 7) / 8); + if ((sa->aead = calloc (1, len)) == NULL) + return -NLE_NOMEM; + memcpy ((void *)sa->aead, (void *)aead, len); + sa->ce_mask |= XFRM_SA_ATTR_ALG_AEAD; + } + + if (tb[XFRMA_ALG_AUTH_TRUNC]) { + struct xfrm_algo_auth* auth = nla_data(tb[XFRMA_ALG_AUTH_TRUNC]); + + len = sizeof (struct xfrmnl_algo_auth) + ((auth->alg_key_len + 7) / 8); + if ((sa->auth = calloc (1, len)) == NULL) + return -NLE_NOMEM; + memcpy ((void *)sa->auth, (void *)auth, len); + sa->ce_mask |= XFRM_SA_ATTR_ALG_AUTH; + } + + if (tb[XFRMA_ALG_AUTH] && !sa->auth) { + struct xfrm_algo* auth = nla_data(tb[XFRMA_ALG_AUTH]); + + len = sizeof (struct xfrmnl_algo_auth) + ((auth->alg_key_len + 7) / 8); + if ((sa->auth = calloc (1, len)) == NULL) + return -NLE_NOMEM; + strcpy(sa->auth->alg_name, auth->alg_name); + memcpy(sa->auth->alg_key, auth->alg_key, (auth->alg_key_len + 7) / 8); + sa->auth->alg_key_len = auth->alg_key_len; + sa->ce_mask |= XFRM_SA_ATTR_ALG_AUTH; + } + + if (tb[XFRMA_ALG_CRYPT]) { + struct xfrm_algo* crypt = nla_data(tb[XFRMA_ALG_CRYPT]); + + len = sizeof (struct xfrmnl_algo) + ((crypt->alg_key_len + 7) / 8); + if ((sa->crypt = calloc (1, len)) == NULL) + return -NLE_NOMEM; + memcpy ((void *)sa->crypt, (void *)crypt, len); + sa->ce_mask |= XFRM_SA_ATTR_ALG_CRYPT; + } + + if (tb[XFRMA_ALG_COMP]) { + struct xfrm_algo* comp = nla_data(tb[XFRMA_ALG_COMP]); + + len = sizeof (struct xfrmnl_algo) + ((comp->alg_key_len + 7) / 8); + if ((sa->comp = calloc (1, len)) == NULL) + return -NLE_NOMEM; + memcpy ((void *)sa->comp, (void *)comp, len); + sa->ce_mask |= XFRM_SA_ATTR_ALG_COMP; + } + + if (tb[XFRMA_ENCAP]) { + struct xfrm_encap_tmpl* encap = nla_data(tb[XFRMA_ENCAP]); + + len = sizeof (struct xfrmnl_encap_tmpl); + if ((sa->encap = calloc (1, len)) == NULL) + return -NLE_NOMEM; + sa->encap->encap_type = encap->encap_type; + sa->encap->encap_sport = ntohs(encap->encap_sport); + sa->encap->encap_dport = ntohs(encap->encap_dport); + if (!(sa->encap->encap_oa = _nl_addr_build(sa_info->family, + &encap->encap_oa))) + return -NLE_NOMEM; + sa->ce_mask |= XFRM_SA_ATTR_ENCAP; + } + + if (tb[XFRMA_TFCPAD]) { + sa->tfcpad = *(uint32_t*)nla_data(tb[XFRMA_TFCPAD]); + sa->ce_mask |= XFRM_SA_ATTR_TFCPAD; + } + + if (tb[XFRMA_COADDR]) { + if (!(sa->coaddr = _nl_addr_build( + sa_info->family, nla_data(tb[XFRMA_COADDR])))) + return -NLE_NOMEM; + sa->ce_mask |= XFRM_SA_ATTR_COADDR; + } + + if (tb[XFRMA_MARK]) { + struct xfrm_mark* m = nla_data(tb[XFRMA_MARK]); + + sa->mark.m = m->m; + sa->mark.v = m->v; + sa->ce_mask |= XFRM_SA_ATTR_MARK; + } + + if (tb[XFRMA_SEC_CTX]) { + struct xfrm_user_sec_ctx* sec_ctx = nla_data(tb[XFRMA_SEC_CTX]); + + len = sizeof (struct xfrmnl_user_sec_ctx) + sec_ctx->ctx_len; + if ((sa->sec_ctx = calloc (1, len)) == NULL) + return -NLE_NOMEM; + memcpy (sa->sec_ctx, sec_ctx, len); + sa->ce_mask |= XFRM_SA_ATTR_SECCTX; + } + + if (tb[XFRMA_ETIMER_THRESH]) { + sa->replay_maxage = *(uint32_t*)nla_data(tb[XFRMA_ETIMER_THRESH]); + sa->ce_mask |= XFRM_SA_ATTR_REPLAY_MAXAGE; + } + + if (tb[XFRMA_REPLAY_THRESH]) { + sa->replay_maxdiff = *(uint32_t*)nla_data(tb[XFRMA_REPLAY_THRESH]); + sa->ce_mask |= XFRM_SA_ATTR_REPLAY_MAXDIFF; + } + + if (tb[XFRMA_REPLAY_ESN_VAL]) { + struct xfrm_replay_state_esn* esn = nla_data (tb[XFRMA_REPLAY_ESN_VAL]); + + len = sizeof (struct xfrmnl_replay_state_esn) + (sizeof (uint32_t) * esn->bmp_len); + if ((sa->replay_state_esn = calloc (1, len)) == NULL) + return -NLE_NOMEM; + memcpy ((void *)sa->replay_state_esn, (void *)esn, len); + sa->ce_mask |= XFRM_SA_ATTR_REPLAY_STATE; + } + else if (tb[XFRMA_REPLAY_VAL]) + { + struct xfrm_replay_state* replay_state = nla_data (tb[XFRMA_REPLAY_VAL]); + sa->replay_state.oseq = replay_state->oseq; + sa->replay_state.seq = replay_state->seq; + sa->replay_state.bitmap = replay_state->bitmap; + sa->ce_mask |= XFRM_SA_ATTR_REPLAY_STATE; + sa->replay_state_esn = NULL; + } + + if (tb[XFRMA_OFFLOAD_DEV]) { + struct xfrm_user_offload *offload; + + len = sizeof(struct xfrmnl_user_offload); + if ((sa->user_offload = calloc(1, len)) == NULL) + return -NLE_NOMEM; + offload = nla_data(tb[XFRMA_OFFLOAD_DEV]); + sa->user_offload->ifindex = offload->ifindex; + sa->user_offload->flags = offload->flags; + sa->ce_mask |= XFRM_SA_ATTR_OFFLOAD_DEV; + } + + *result = _nl_steal_pointer(&sa); + return 0; +} + +static int xfrm_sa_update_cache (struct nl_cache *cache, struct nl_object *obj, + change_func_t change_cb, change_func_v2_t change_cb_v2, + void *data) +{ + _nl_auto_nl_object struct nl_object* old_sa = NULL; + struct xfrmnl_sa* sa = (struct xfrmnl_sa*)obj; + + if (nl_object_get_msgtype (obj) == XFRM_MSG_EXPIRE) + { + /* On hard expiry, the SA gets deleted too from the kernel state without any + * further delete event. On Expire message, we are only updating the cache with + * the SA object's new state. In absence of the explicit delete event, the cache will + * be out of sync with the kernel state. To get around this, expiry messages cache + * operations are handled here (installed with NL_ACT_UNSPEC action) instead of + * in Libnl Cache module. */ + + /* Do we already have this object in the cache? */ + old_sa = nl_cache_search(cache, obj); + if (old_sa) + { + /* Found corresponding SA object in cache. Delete it */ + nl_cache_remove (old_sa); + } + + /* Handle the expiry event now */ + if (sa->hard == 0) + { + /* Soft expiry event: Save the new object to the + * cache and notify application of the expiry event. */ + nl_cache_move (cache, obj); + + if (old_sa == NULL) + { + /* Application CB present, no previous instance of SA object present. + * Notify application CB as a NEW event */ + if (change_cb_v2) + change_cb_v2(cache, NULL, obj, 0, NL_ACT_NEW, data); + else if (change_cb) + change_cb(cache, obj, NL_ACT_NEW, data); + } + else + { + uint64_t diff = 0; + if (change_cb || change_cb_v2) + diff = nl_object_diff64(old_sa, obj); + + /* Application CB present, a previous instance of SA object present. + * Notify application CB as a CHANGE1 event */ + if (diff) { + if (change_cb_v2) { + change_cb_v2(cache, old_sa, obj, diff, NL_ACT_CHANGE, data); + } else if (change_cb) + change_cb(cache, obj, NL_ACT_CHANGE, data); + } + } + } + else + { + /* Hard expiry event: Delete the object from the + * cache and notify application of the expiry event. */ + if (change_cb_v2) + change_cb_v2(cache, obj, NULL, 0, NL_ACT_DEL, data); + else if (change_cb) + change_cb (cache, obj, NL_ACT_DEL, data); + } + + /* Done handling expire message */ + return 0; + } + else + { + /* All other messages other than Expire, let the standard Libnl cache + * module handle it. */ + if (change_cb_v2) + return nl_cache_include_v2(cache, obj, change_cb_v2, data); + else + return nl_cache_include (cache, obj, change_cb, data); + } +} + +static int xfrm_sa_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *n, struct nl_parser_param *pp) +{ + struct xfrmnl_sa* sa; + int err; + + if ((err = xfrmnl_sa_parse(n, &sa)) < 0) + return err; + + err = pp->pp_cb((struct nl_object *) sa, pp); + + xfrmnl_sa_put(sa); + return err; +} + +/** + * @name XFRM SA Get + * @{ + */ + +int xfrmnl_sa_build_get_request(struct nl_addr* daddr, unsigned int spi, unsigned int protocol, unsigned int mark_v, unsigned int mark_m, struct nl_msg **result) +{ + struct nl_msg *msg; + struct xfrm_usersa_id sa_id; + struct xfrm_mark mark; + + if (!daddr || !spi) + { + fprintf(stderr, "APPLICATION BUG: %s:%d:%s: A valid destination address, spi must be specified\n", + __FILE__, __LINE__, __func__); + assert(0); + return -NLE_MISSING_ATTR; + } + + memset(&sa_id, 0, sizeof(sa_id)); + memcpy (&sa_id.daddr, nl_addr_get_binary_addr (daddr), sizeof (uint8_t) * nl_addr_get_len (daddr)); + sa_id.family = nl_addr_get_family (daddr); + sa_id.spi = htonl(spi); + sa_id.proto = protocol; + + if (!(msg = nlmsg_alloc_simple(XFRM_MSG_GETSA, 0))) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &sa_id, sizeof(sa_id), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if ((mark_m & mark_v) != 0) + { + memset(&mark, 0, sizeof(struct xfrm_mark)); + mark.m = mark_m; + mark.v = mark_v; + + NLA_PUT (msg, XFRMA_MARK, sizeof (struct xfrm_mark), &mark); + } + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +int xfrmnl_sa_get_kernel(struct nl_sock* sock, struct nl_addr* daddr, unsigned int spi, unsigned int protocol, unsigned int mark_v, unsigned int mark_m, struct xfrmnl_sa** result) +{ + struct nl_msg *msg = NULL; + struct nl_object *obj; + int err; + + if ((err = xfrmnl_sa_build_get_request(daddr, spi, protocol, mark_m, mark_v, &msg)) < 0) + return err; + + err = nl_send_auto(sock, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + if ((err = nl_pickup(sock, &xfrm_sa_msg_parser, &obj)) < 0) + return err; + + /* We have used xfrm_sa_msg_parser(), object is definitely a xfrm sa */ + *result = (struct xfrmnl_sa *) obj; + + /* If an object has been returned, we also need to wait for the ACK */ + if (err == 0 && obj) + nl_wait_for_ack(sock); + + return 0; +} + +/** @} */ + +static int build_xfrm_sa_message(struct xfrmnl_sa *tmpl, int cmd, int flags, struct nl_msg **result) +{ + struct nl_msg* msg; + struct xfrm_usersa_info sa_info; + uint32_t len; + struct nl_addr* addr; + + if (!(tmpl->ce_mask & XFRM_SA_ATTR_DADDR) || + !(tmpl->ce_mask & XFRM_SA_ATTR_SPI) || + !(tmpl->ce_mask & XFRM_SA_ATTR_PROTO)) + return -NLE_MISSING_ATTR; + + memset ((void*)&sa_info, 0, sizeof (sa_info)); + if (tmpl->ce_mask & XFRM_SA_ATTR_SEL) + { + addr = xfrmnl_sel_get_daddr (tmpl->sel); + memcpy ((void*)&sa_info.sel.daddr, (void*)nl_addr_get_binary_addr (addr), sizeof (uint8_t) * nl_addr_get_len (addr)); + addr = xfrmnl_sel_get_saddr (tmpl->sel); + memcpy ((void*)&sa_info.sel.saddr, (void*)nl_addr_get_binary_addr (addr), sizeof (uint8_t) * nl_addr_get_len (addr)); + sa_info.sel.dport = htons (xfrmnl_sel_get_dport (tmpl->sel)); + sa_info.sel.dport_mask = htons (xfrmnl_sel_get_dportmask (tmpl->sel)); + sa_info.sel.sport = htons (xfrmnl_sel_get_sport (tmpl->sel)); + sa_info.sel.sport_mask = htons (xfrmnl_sel_get_sportmask (tmpl->sel)); + sa_info.sel.family = xfrmnl_sel_get_family (tmpl->sel); + sa_info.sel.prefixlen_d = xfrmnl_sel_get_prefixlen_d (tmpl->sel); + sa_info.sel.prefixlen_s = xfrmnl_sel_get_prefixlen_s (tmpl->sel); + sa_info.sel.proto = xfrmnl_sel_get_proto (tmpl->sel); + sa_info.sel.ifindex = xfrmnl_sel_get_ifindex (tmpl->sel); + sa_info.sel.user = xfrmnl_sel_get_userid (tmpl->sel); + } + + memcpy (&sa_info.id.daddr, nl_addr_get_binary_addr (tmpl->id.daddr), sizeof (uint8_t) * nl_addr_get_len (tmpl->id.daddr)); + sa_info.id.spi = htonl(tmpl->id.spi); + sa_info.id.proto = tmpl->id.proto; + + if (tmpl->ce_mask & XFRM_SA_ATTR_SADDR) + memcpy (&sa_info.saddr, nl_addr_get_binary_addr (tmpl->saddr), sizeof (uint8_t) * nl_addr_get_len (tmpl->saddr)); + + if (tmpl->ce_mask & XFRM_SA_ATTR_LTIME_CFG) + { + sa_info.lft.soft_byte_limit = xfrmnl_ltime_cfg_get_soft_bytelimit (tmpl->lft); + sa_info.lft.hard_byte_limit = xfrmnl_ltime_cfg_get_hard_bytelimit (tmpl->lft); + sa_info.lft.soft_packet_limit = xfrmnl_ltime_cfg_get_soft_packetlimit (tmpl->lft); + sa_info.lft.hard_packet_limit = xfrmnl_ltime_cfg_get_hard_packetlimit (tmpl->lft); + sa_info.lft.soft_add_expires_seconds = xfrmnl_ltime_cfg_get_soft_addexpires (tmpl->lft); + sa_info.lft.hard_add_expires_seconds = xfrmnl_ltime_cfg_get_hard_addexpires (tmpl->lft); + sa_info.lft.soft_use_expires_seconds = xfrmnl_ltime_cfg_get_soft_useexpires (tmpl->lft); + sa_info.lft.hard_use_expires_seconds = xfrmnl_ltime_cfg_get_hard_useexpires (tmpl->lft); + } + + //Skip current lifetime: cur lifetime can be updated only via AE + //Skip stats: stats cant be updated + //Skip seq: seq cant be updated + + if (tmpl->ce_mask & XFRM_SA_ATTR_REQID) + sa_info.reqid = tmpl->reqid; + + if (tmpl->ce_mask & XFRM_SA_ATTR_FAMILY) + sa_info.family = tmpl->family; + + if (tmpl->ce_mask & XFRM_SA_ATTR_MODE) + sa_info.mode = tmpl->mode; + + if (tmpl->ce_mask & XFRM_SA_ATTR_REPLAY_WIN) + sa_info.replay_window = tmpl->replay_window; + + if (tmpl->ce_mask & XFRM_SA_ATTR_FLAGS) + sa_info.flags = tmpl->flags; + + msg = nlmsg_alloc_simple(cmd, flags); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &sa_info, sizeof(sa_info), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (tmpl->ce_mask & XFRM_SA_ATTR_ALG_AEAD) { + len = sizeof (struct xfrm_algo_aead) + ((tmpl->aead->alg_key_len + 7) / 8); + NLA_PUT (msg, XFRMA_ALG_AEAD, len, tmpl->aead); + } + + if (tmpl->ce_mask & XFRM_SA_ATTR_ALG_AUTH) { + /* kernel prefers XFRMA_ALG_AUTH_TRUNC over XFRMA_ALG_AUTH, so only + * one of the attributes needs to be present */ + if (tmpl->auth->alg_trunc_len) { + len = sizeof (struct xfrm_algo_auth) + ((tmpl->auth->alg_key_len + 7) / 8); + NLA_PUT (msg, XFRMA_ALG_AUTH_TRUNC, len, tmpl->auth); + } else { + struct xfrm_algo *auth; + + len = sizeof (struct xfrm_algo) + ((tmpl->auth->alg_key_len + 7) / 8); + auth = malloc(len); + if (!auth) { + nlmsg_free(msg); + return -NLE_NOMEM; + } + + _nl_strncpy_assert(auth->alg_name, tmpl->auth->alg_name, sizeof(auth->alg_name)); + auth->alg_key_len = tmpl->auth->alg_key_len; + memcpy(auth->alg_key, tmpl->auth->alg_key, (tmpl->auth->alg_key_len + 7) / 8); + if (nla_put(msg, XFRMA_ALG_AUTH, len, auth) < 0) { + free(auth); + goto nla_put_failure; + } + free(auth); + } + } + + if (tmpl->ce_mask & XFRM_SA_ATTR_ALG_CRYPT) { + len = sizeof (struct xfrm_algo) + ((tmpl->crypt->alg_key_len + 7) / 8); + NLA_PUT (msg, XFRMA_ALG_CRYPT, len, tmpl->crypt); + } + + if (tmpl->ce_mask & XFRM_SA_ATTR_ALG_COMP) { + len = sizeof (struct xfrm_algo) + ((tmpl->comp->alg_key_len + 7) / 8); + NLA_PUT (msg, XFRMA_ALG_COMP, len, tmpl->comp); + } + + if (tmpl->ce_mask & XFRM_SA_ATTR_ENCAP) { + struct xfrm_encap_tmpl* encap_tmpl; + struct nlattr* encap_attr; + + len = sizeof (struct xfrm_encap_tmpl); + encap_attr = nla_reserve(msg, XFRMA_ENCAP, len); + if (!encap_attr) + goto nla_put_failure; + encap_tmpl = nla_data (encap_attr); + encap_tmpl->encap_type = tmpl->encap->encap_type; + encap_tmpl->encap_sport = htons (tmpl->encap->encap_sport); + encap_tmpl->encap_dport = htons (tmpl->encap->encap_dport); + memcpy (&encap_tmpl->encap_oa, nl_addr_get_binary_addr (tmpl->encap->encap_oa), sizeof (uint8_t) * nl_addr_get_len (tmpl->encap->encap_oa)); + } + + if (tmpl->ce_mask & XFRM_SA_ATTR_TFCPAD) { + NLA_PUT_U32 (msg, XFRMA_TFCPAD, tmpl->tfcpad); + } + + if (tmpl->ce_mask & XFRM_SA_ATTR_COADDR) { + NLA_PUT (msg, XFRMA_COADDR, sizeof (xfrm_address_t), tmpl->coaddr); + } + + if (tmpl->ce_mask & XFRM_SA_ATTR_MARK) { + NLA_PUT (msg, XFRMA_MARK, sizeof (struct xfrm_mark), &tmpl->mark); + } + + if (tmpl->ce_mask & XFRM_SA_ATTR_SECCTX) { + len = sizeof (struct xfrm_sec_ctx) + tmpl->sec_ctx->ctx_len; + NLA_PUT (msg, XFRMA_SEC_CTX, len, tmpl->sec_ctx); + } + + if (tmpl->ce_mask & XFRM_SA_ATTR_REPLAY_MAXAGE) { + NLA_PUT_U32 (msg, XFRMA_ETIMER_THRESH, tmpl->replay_maxage); + } + + if (tmpl->ce_mask & XFRM_SA_ATTR_REPLAY_MAXDIFF) { + NLA_PUT_U32 (msg, XFRMA_REPLAY_THRESH, tmpl->replay_maxdiff); + } + + if (tmpl->ce_mask & XFRM_SA_ATTR_REPLAY_STATE) { + if (tmpl->replay_state_esn) { + len = sizeof (struct xfrm_replay_state_esn) + (sizeof (uint32_t) * tmpl->replay_state_esn->bmp_len); + NLA_PUT (msg, XFRMA_REPLAY_ESN_VAL, len, tmpl->replay_state_esn); + } + else { + NLA_PUT (msg, XFRMA_REPLAY_VAL, sizeof (struct xfrm_replay_state), &tmpl->replay_state); + } + } + + if (tmpl->ce_mask & XFRM_SA_ATTR_OFFLOAD_DEV) { + struct xfrm_user_offload *offload; + struct nlattr *attr; + + len = sizeof(struct xfrm_user_offload); + attr = nla_reserve(msg, XFRMA_OFFLOAD_DEV, len); + + if (!attr) + goto nla_put_failure; + + offload = nla_data(attr); + offload->ifindex = tmpl->user_offload->ifindex; + offload->flags = tmpl->user_offload->flags; + } + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +/** + * @name XFRM SA Add + * @{ + */ + +int xfrmnl_sa_build_add_request(struct xfrmnl_sa* tmpl, int flags, struct nl_msg **result) +{ + return build_xfrm_sa_message (tmpl, XFRM_MSG_NEWSA, flags, result); +} + +int xfrmnl_sa_add(struct nl_sock* sk, struct xfrmnl_sa* tmpl, int flags) +{ + int err; + struct nl_msg *msg; + + if ((err = xfrmnl_sa_build_add_request(tmpl, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return nl_wait_for_ack(sk); +} + +/** + * @name XFRM SA Update + * @{ + */ + +int xfrmnl_sa_build_update_request(struct xfrmnl_sa* tmpl, int flags, struct nl_msg **result) +{ + return build_xfrm_sa_message (tmpl, XFRM_MSG_UPDSA, flags, result); +} + +int xfrmnl_sa_update(struct nl_sock* sk, struct xfrmnl_sa* tmpl, int flags) +{ + int err; + struct nl_msg *msg; + + if ((err = xfrmnl_sa_build_update_request(tmpl, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return nl_wait_for_ack(sk); +} + +/** @} */ + +static int build_xfrm_sa_delete_message(struct xfrmnl_sa *tmpl, int cmd, int flags, struct nl_msg **result) +{ + struct nl_msg* msg; + struct xfrm_usersa_id sa_id; + + if (!(tmpl->ce_mask & XFRM_SA_ATTR_DADDR) || + !(tmpl->ce_mask & XFRM_SA_ATTR_SPI) || + !(tmpl->ce_mask & XFRM_SA_ATTR_PROTO)) + return -NLE_MISSING_ATTR; + + memset(&sa_id, 0, sizeof(struct xfrm_usersa_id)); + memcpy (&sa_id.daddr, nl_addr_get_binary_addr (tmpl->id.daddr), + sizeof (uint8_t) * nl_addr_get_len (tmpl->id.daddr)); + sa_id.family = nl_addr_get_family (tmpl->id.daddr); + sa_id.spi = htonl(tmpl->id.spi); + sa_id.proto = tmpl->id.proto; + + msg = nlmsg_alloc_simple(cmd, flags); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &sa_id, sizeof(sa_id), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (tmpl->ce_mask & XFRM_SA_ATTR_MARK) { + NLA_PUT (msg, XFRMA_MARK, sizeof (struct xfrm_mark), &tmpl->mark); + } + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +/** + * @name XFRM SA Delete + * @{ + */ + +int xfrmnl_sa_build_delete_request(struct xfrmnl_sa* tmpl, int flags, struct nl_msg **result) +{ + return build_xfrm_sa_delete_message (tmpl, XFRM_MSG_DELSA, flags, result); +} + +int xfrmnl_sa_delete(struct nl_sock* sk, struct xfrmnl_sa* tmpl, int flags) +{ + int err; + struct nl_msg *msg; + + if ((err = xfrmnl_sa_build_delete_request(tmpl, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return nl_wait_for_ack(sk); +} + +/** @} */ + + +/** + * @name Attributes + * @{ + */ + +struct xfrmnl_sel* xfrmnl_sa_get_sel (struct xfrmnl_sa* sa) +{ + if (sa->ce_mask & XFRM_SA_ATTR_SEL) + return sa->sel; + else + return NULL; +} + +int xfrmnl_sa_set_sel (struct xfrmnl_sa* sa, struct xfrmnl_sel* sel) +{ + /* Release any previously held selector object from the SA */ + if (sa->sel) + xfrmnl_sel_put (sa->sel); + + /* Increment ref count on new selector and save it in the SA */ + xfrmnl_sel_get (sel); + sa->sel = sel; + sa->ce_mask |= XFRM_SA_ATTR_SEL; + + return 0; +} + +static inline int __assign_addr(struct xfrmnl_sa* sa, struct nl_addr **pos, + struct nl_addr *new, int flag, int nocheck) +{ + if (!nocheck) + { + if (sa->ce_mask & XFRM_SA_ATTR_FAMILY) + { + if (nl_addr_get_family (new) != sa->family) + return -NLE_AF_MISMATCH; + } + } + + if (*pos) + nl_addr_put(*pos); + + nl_addr_get(new); + *pos = new; + + sa->ce_mask |= flag; + + return 0; +} + + +struct nl_addr* xfrmnl_sa_get_daddr (struct xfrmnl_sa* sa) +{ + if (sa->ce_mask & XFRM_SA_ATTR_DADDR) + return sa->id.daddr; + else + return NULL; +} + +int xfrmnl_sa_set_daddr (struct xfrmnl_sa* sa, struct nl_addr* addr) +{ + return __assign_addr(sa, &sa->id.daddr, addr, XFRM_SA_ATTR_DADDR, 0); +} + +int xfrmnl_sa_get_spi (struct xfrmnl_sa* sa) +{ + if (sa->ce_mask & XFRM_SA_ATTR_SPI) + return sa->id.spi; + else + return -1; +} + +int xfrmnl_sa_set_spi (struct xfrmnl_sa* sa, unsigned int spi) +{ + sa->id.spi = spi; + sa->ce_mask |= XFRM_SA_ATTR_SPI; + + return 0; +} + +int xfrmnl_sa_get_proto (struct xfrmnl_sa* sa) +{ + if (sa->ce_mask & XFRM_SA_ATTR_PROTO) + return sa->id.proto; + else + return -1; +} + +int xfrmnl_sa_set_proto (struct xfrmnl_sa* sa, unsigned int protocol) +{ + sa->id.proto = protocol; + sa->ce_mask |= XFRM_SA_ATTR_PROTO; + + return 0; +} + +struct nl_addr* xfrmnl_sa_get_saddr (struct xfrmnl_sa* sa) +{ + if (sa->ce_mask & XFRM_SA_ATTR_SADDR) + return sa->saddr; + else + return NULL; +} + +int xfrmnl_sa_set_saddr (struct xfrmnl_sa* sa, struct nl_addr* addr) +{ + return __assign_addr(sa, &sa->saddr, addr, XFRM_SA_ATTR_SADDR, 1); +} + +struct xfrmnl_ltime_cfg* xfrmnl_sa_get_lifetime_cfg (struct xfrmnl_sa* sa) +{ + if (sa->ce_mask & XFRM_SA_ATTR_LTIME_CFG) + return sa->lft; + else + return NULL; +} + +int xfrmnl_sa_set_lifetime_cfg (struct xfrmnl_sa* sa, struct xfrmnl_ltime_cfg* ltime) +{ + /* Release any previously held lifetime cfg object from the SA */ + if (sa->lft) + xfrmnl_ltime_cfg_put (sa->lft); + + /* Increment ref count on new lifetime object and save it in the SA */ + xfrmnl_ltime_cfg_get (ltime); + sa->lft = ltime; + sa->ce_mask |= XFRM_SA_ATTR_LTIME_CFG; + + return 0; +} + +int xfrmnl_sa_get_curlifetime (struct xfrmnl_sa* sa, unsigned long long int* curr_bytes, + unsigned long long int* curr_packets, unsigned long long int* curr_add_time, unsigned long long int* curr_use_time) +{ + if (sa == NULL || curr_bytes == NULL || curr_packets == NULL || curr_add_time == NULL || curr_use_time == NULL) + return -1; + + if (sa->ce_mask & XFRM_SA_ATTR_LTIME_CUR) + { + *curr_bytes = sa->curlft.bytes; + *curr_packets = sa->curlft.packets; + *curr_add_time = sa->curlft.add_time; + *curr_use_time = sa->curlft.use_time; + } + else + return -1; + + return 0; +} + +int xfrmnl_sa_get_stats (struct xfrmnl_sa* sa, unsigned long long int* replay_window, + unsigned long long int* replay, unsigned long long int* integrity_failed) +{ + if (sa == NULL || replay_window == NULL || replay == NULL || integrity_failed == NULL) + return -1; + + if (sa->ce_mask & XFRM_SA_ATTR_STATS) + { + *replay_window = sa->stats.replay_window; + *replay = sa->stats.replay; + *integrity_failed = sa->stats.integrity_failed; + } + else + return -1; + + return 0; +} + +int xfrmnl_sa_get_seq (struct xfrmnl_sa* sa) +{ + if (sa->ce_mask & XFRM_SA_ATTR_SEQ) + return sa->seq; + else + return -1; +} + +int xfrmnl_sa_get_reqid (struct xfrmnl_sa* sa) +{ + if (sa->ce_mask & XFRM_SA_ATTR_REQID) + return sa->reqid; + else + return -1; +} + +int xfrmnl_sa_set_reqid (struct xfrmnl_sa* sa, unsigned int reqid) +{ + sa->reqid = reqid; + sa->ce_mask |= XFRM_SA_ATTR_REQID; + + return 0; +} + +int xfrmnl_sa_get_family (struct xfrmnl_sa* sa) +{ + if (sa->ce_mask & XFRM_SA_ATTR_FAMILY) + return sa->family; + else + return -1; +} + +int xfrmnl_sa_set_family (struct xfrmnl_sa* sa, unsigned int family) +{ + sa->family = family; + sa->ce_mask |= XFRM_SA_ATTR_FAMILY; + + return 0; +} + +int xfrmnl_sa_get_mode (struct xfrmnl_sa* sa) +{ + if (sa->ce_mask & XFRM_SA_ATTR_MODE) + return sa->mode; + else + return -1; +} + +int xfrmnl_sa_set_mode (struct xfrmnl_sa* sa, unsigned int mode) +{ + sa->mode = mode; + sa->ce_mask |= XFRM_SA_ATTR_MODE; + + return 0; +} + +int xfrmnl_sa_get_replay_window (struct xfrmnl_sa* sa) +{ + if (sa->ce_mask & XFRM_SA_ATTR_REPLAY_WIN) + return sa->replay_window; + else + return -1; +} + +int xfrmnl_sa_set_replay_window (struct xfrmnl_sa* sa, unsigned int replay_window) +{ + sa->replay_window = replay_window; + sa->ce_mask |= XFRM_SA_ATTR_REPLAY_WIN; + + return 0; +} + +int xfrmnl_sa_get_flags (struct xfrmnl_sa* sa) +{ + if (sa->ce_mask & XFRM_SA_ATTR_FLAGS) + return sa->flags; + else + return -1; +} + +int xfrmnl_sa_set_flags (struct xfrmnl_sa* sa, unsigned int flags) +{ + sa->flags = flags; + sa->ce_mask |= XFRM_SA_ATTR_FLAGS; + + return 0; +} + +/** + * Get the aead-params + * @arg sa the xfrmnl_sa object + * @arg alg_name an optional output buffer for the algorithm name. Must be at least 64 bytes. + * @arg key_len an optional output value for the key length in bits. + * @arg icv_len an optional output value for the alt-icv-len. + * @arg key an optional buffer large enough for the key. It must contain at least + * ((@key_len + 7) / 8) bytes. + * + * Warning: you must ensure that @key is large enough. If you don't know the key_len before-hand, + * call xfrmnl_sa_get_aead_params() without @key argument to query only the required buffer size. + * This modified API is available in all versions of libnl3 that support the capability + * @def NL_CAPABILITY_XFRM_SA_KEY_SIZE (@see nl_has_capability for further information). + * + * @return 0 on success or a negative error code. + */ +int xfrmnl_sa_get_aead_params (struct xfrmnl_sa* sa, char* alg_name, unsigned int* key_len, unsigned int* icv_len, char* key) +{ + if (sa->ce_mask & XFRM_SA_ATTR_ALG_AEAD) + { + if (alg_name) + strcpy (alg_name, sa->aead->alg_name); + if (key_len) + *key_len = sa->aead->alg_key_len; + if (icv_len) + *icv_len = sa->aead->alg_icv_len; + if (key) + memcpy (key, sa->aead->alg_key, ((sa->aead->alg_key_len + 7)/8)); + } + else + return -1; + + return 0; +} + +int xfrmnl_sa_set_aead_params (struct xfrmnl_sa* sa, const char* alg_name, unsigned int key_len, unsigned int icv_len, const char* key) +{ + _nl_auto_free struct xfrmnl_algo_aead *b = NULL; + size_t keysize = sizeof (uint8_t) * ((key_len + 7)/8); + uint32_t newlen = sizeof (struct xfrmnl_algo_aead) + keysize; + + /* Free up the old key and allocate memory to hold new key */ + if (strlen (alg_name) >= sizeof (sa->aead->alg_name)) + return -1; + if (!(b = calloc (1, newlen))) + return -1; + + strcpy (b->alg_name, alg_name); + b->alg_key_len = key_len; + b->alg_icv_len = icv_len; + memcpy (b->alg_key, key, keysize); + + free (sa->aead); + sa->aead = _nl_steal_pointer (&b); + sa->ce_mask |= XFRM_SA_ATTR_ALG_AEAD; + return 0; +} + +/** + * Get the auth-params + * @arg sa the xfrmnl_sa object + * @arg alg_name an optional output buffer for the algorithm name. Must be at least 64 bytes. + * @arg key_len an optional output value for the key length in bits. + * @arg trunc_len an optional output value for the alg-trunc-len. + * @arg key an optional buffer large enough for the key. It must contain at least + * ((@key_len + 7) / 8) bytes. + * + * Warning: you must ensure that @key is large enough. If you don't know the key_len before-hand, + * call xfrmnl_sa_get_auth_params() without @key argument to query only the required buffer size. + * This modified API is available in all versions of libnl3 that support the capability + * @def NL_CAPABILITY_XFRM_SA_KEY_SIZE (@see nl_has_capability for further information). + * + * @return 0 on success or a negative error code. + */ +int xfrmnl_sa_get_auth_params (struct xfrmnl_sa* sa, char* alg_name, unsigned int* key_len, unsigned int* trunc_len, char* key) +{ + if (!(sa->ce_mask & XFRM_SA_ATTR_ALG_AUTH)) + return -NLE_MISSING_ATTR; + + if (alg_name) + strcpy(alg_name, sa->auth->alg_name); + if (key_len) + *key_len = sa->auth->alg_key_len; + if (trunc_len) + *trunc_len = sa->auth->alg_trunc_len; + if (key) + memcpy(key, sa->auth->alg_key, (sa->auth->alg_key_len + 7) / 8); + return 0; +} + +int xfrmnl_sa_set_auth_params (struct xfrmnl_sa* sa, const char* alg_name, unsigned int key_len, unsigned int trunc_len, const char* key) +{ + _nl_auto_free struct xfrmnl_algo_auth *b = NULL; + size_t keysize = sizeof (uint8_t) * ((key_len + 7)/8); + uint32_t newlen = sizeof (struct xfrmnl_algo_auth) + keysize; + + if (strlen (alg_name) >= sizeof (sa->auth->alg_name)) + return -1; + if (!(b = calloc (1, newlen))) + return -1; + + strcpy (b->alg_name, alg_name); + b->alg_key_len = key_len; + b->alg_trunc_len = trunc_len; + memcpy (b->alg_key, key, keysize); + + free (sa->auth); + sa->auth = _nl_steal_pointer (&b); + sa->ce_mask |= XFRM_SA_ATTR_ALG_AUTH; + return 0; +} + +/** + * Get the crypto-params + * @arg sa the xfrmnl_sa object + * @arg alg_name an optional output buffer for the algorithm name. Must be at least 64 bytes. + * @arg key_len an optional output value for the key length in bits. + * @arg key an optional buffer large enough for the key. It must contain at least + * ((@key_len + 7) / 8) bytes. + * + * Warning: you must ensure that @key is large enough. If you don't know the key_len before-hand, + * call xfrmnl_sa_get_crypto_params() without @key argument to query only the required buffer size. + * This modified API is available in all versions of libnl3 that support the capability + * @def NL_CAPABILITY_XFRM_SA_KEY_SIZE (@see nl_has_capability for further information). + * + * @return 0 on success or a negative error code. + */ +int xfrmnl_sa_get_crypto_params (struct xfrmnl_sa* sa, char* alg_name, unsigned int* key_len, char* key) +{ + if (sa->ce_mask & XFRM_SA_ATTR_ALG_CRYPT) + { + if (alg_name) + strcpy (alg_name, sa->crypt->alg_name); + if (key_len) + *key_len = sa->crypt->alg_key_len; + if (key) + memcpy (key, sa->crypt->alg_key, ((sa->crypt->alg_key_len + 7)/8)); + } + else + return -1; + + return 0; +} + +int xfrmnl_sa_set_crypto_params (struct xfrmnl_sa* sa, const char* alg_name, unsigned int key_len, const char* key) +{ + _nl_auto_free struct xfrmnl_algo *b = NULL; + size_t keysize = sizeof (uint8_t) * ((key_len + 7)/8); + uint32_t newlen = sizeof (struct xfrmnl_algo) + keysize; + + if (strlen (alg_name) >= sizeof (sa->crypt->alg_name)) + return -1; + if (!(b = calloc (1, newlen))) + return -1; + + strcpy (b->alg_name, alg_name); + b->alg_key_len = key_len; + memcpy (b->alg_key, key, keysize); + + free(sa->crypt); + sa->crypt = _nl_steal_pointer(&b); + sa->ce_mask |= XFRM_SA_ATTR_ALG_CRYPT; + return 0; +} + +/** + * Get the comp-params + * @arg sa the xfrmnl_sa object + * @arg alg_name an optional output buffer for the algorithm name. Must be at least 64 bytes. + * @arg key_len an optional output value for the key length in bits. + * @arg key an optional buffer large enough for the key. It must contain at least + * ((@key_len + 7) / 8) bytes. + * + * Warning: you must ensure that @key is large enough. If you don't know the key_len before-hand, + * call xfrmnl_sa_get_comp_params() without @key argument to query only the required buffer size. + * This modified API is available in all versions of libnl3 that support the capability + * @def NL_CAPABILITY_XFRM_SA_KEY_SIZE (@see nl_has_capability for further information). + * + * @return 0 on success or a negative error code. + */ +int xfrmnl_sa_get_comp_params (struct xfrmnl_sa* sa, char* alg_name, unsigned int* key_len, char* key) +{ + if (sa->ce_mask & XFRM_SA_ATTR_ALG_COMP) + { + if (alg_name) + strcpy (alg_name, sa->comp->alg_name); + if (key_len) + *key_len = sa->comp->alg_key_len; + if (key) + memcpy (key, sa->comp->alg_key, ((sa->comp->alg_key_len + 7)/8)); + } + else + return -1; + + return 0; +} + +int xfrmnl_sa_set_comp_params (struct xfrmnl_sa* sa, const char* alg_name, unsigned int key_len, const char* key) +{ + _nl_auto_free struct xfrmnl_algo *b = NULL; + size_t keysize = sizeof (uint8_t) * ((key_len + 7)/8); + uint32_t newlen = sizeof (struct xfrmnl_algo) + keysize; + + if (strlen (alg_name) >= sizeof (sa->comp->alg_name)) + return -1; + if (!(b = calloc (1, newlen))) + return -1; + + strcpy (b->alg_name, alg_name); + b->alg_key_len = key_len; + memcpy (b->alg_key, key, keysize); + + free(sa->comp); + sa->comp = _nl_steal_pointer(&b); + sa->ce_mask |= XFRM_SA_ATTR_ALG_COMP; + return 0; +} + +int xfrmnl_sa_get_encap_tmpl (struct xfrmnl_sa* sa, unsigned int* encap_type, unsigned int* encap_sport, unsigned int* encap_dport, struct nl_addr** encap_oa) +{ + if (sa->ce_mask & XFRM_SA_ATTR_ENCAP) + { + *encap_type = sa->encap->encap_type; + *encap_sport = sa->encap->encap_sport; + *encap_dport = sa->encap->encap_dport; + *encap_oa = nl_addr_clone (sa->encap->encap_oa); + } + else + return -1; + + return 0; +} + +int xfrmnl_sa_set_encap_tmpl (struct xfrmnl_sa* sa, unsigned int encap_type, unsigned int encap_sport, unsigned int encap_dport, struct nl_addr* encap_oa) +{ + if (sa->encap) { + /* Free up the old encap OA */ + if (sa->encap->encap_oa) + nl_addr_put(sa->encap->encap_oa); + memset(sa->encap, 0, sizeof (*sa->encap)); + } else if ((sa->encap = calloc(1, sizeof(*sa->encap))) == NULL) + return -1; + + /* Save the new info */ + sa->encap->encap_type = encap_type; + sa->encap->encap_sport = encap_sport; + sa->encap->encap_dport = encap_dport; + nl_addr_get (encap_oa); + sa->encap->encap_oa = encap_oa; + + sa->ce_mask |= XFRM_SA_ATTR_ENCAP; + + return 0; +} + +int xfrmnl_sa_get_tfcpad (struct xfrmnl_sa* sa) +{ + if (sa->ce_mask & XFRM_SA_ATTR_TFCPAD) + return sa->tfcpad; + else + return -1; +} + +int xfrmnl_sa_set_tfcpad (struct xfrmnl_sa* sa, unsigned int tfcpad) +{ + sa->tfcpad = tfcpad; + sa->ce_mask |= XFRM_SA_ATTR_TFCPAD; + + return 0; +} + +struct nl_addr* xfrmnl_sa_get_coaddr (struct xfrmnl_sa* sa) +{ + if (sa->ce_mask & XFRM_SA_ATTR_COADDR) + return sa->coaddr; + else + return NULL; +} + +int xfrmnl_sa_set_coaddr (struct xfrmnl_sa* sa, struct nl_addr* coaddr) +{ + /* Free up the old coaddr */ + if (sa->coaddr) + nl_addr_put (sa->coaddr); + + /* Save the new info */ + nl_addr_get (coaddr); + sa->coaddr = coaddr; + + sa->ce_mask |= XFRM_SA_ATTR_COADDR; + + return 0; +} + +int xfrmnl_sa_get_mark (struct xfrmnl_sa* sa, unsigned int* mark_mask, unsigned int* mark_value) +{ + if (mark_mask == NULL || mark_value == NULL) + return -1; + + if (sa->ce_mask & XFRM_SA_ATTR_MARK) + { + *mark_mask = sa->mark.m; + *mark_value = sa->mark.v; + + return 0; + } + else + return -1; +} + +int xfrmnl_sa_set_mark (struct xfrmnl_sa* sa, unsigned int value, unsigned int mask) +{ + sa->mark.v = value; + sa->mark.m = mask; + sa->ce_mask |= XFRM_SA_ATTR_MARK; + + return 0; +} + +/** + * Get the security context. + * + * @arg sa The xfrmnl_sa object. + * @arg doi An optional output value for the security context domain of interpretation. + * @arg alg An optional output value for the security context algorithm. + * @arg len An optional output value for the security context length, including the + * terminating null byte ('\0'). + * @arg sid Unused parameter. + * @arg ctx_str An optional buffer large enough for the security context string. It must + * contain at least @len bytes. + * + * Warning: you must ensure that @ctx_str is large enough. If you don't know the length before-hand, + * call xfrmnl_sa_get_sec_ctx() without @ctx_str argument to query only the required buffer size. + * This modified API is available in all versions of libnl3 that support the capability + * @def NL_CAPABILITY_XFRM_SEC_CTX_LEN (@see nl_has_capability for further information). + * + * @return 0 on success or a negative error code. + */ +int xfrmnl_sa_get_sec_ctx (struct xfrmnl_sa* sa, unsigned int* doi, unsigned int* alg, + unsigned int* len, unsigned int* sid, char* ctx_str) +{ + if (sa->ce_mask & XFRM_SA_ATTR_SECCTX) + { + if (doi) + *doi = sa->sec_ctx->ctx_doi; + if (alg) + *alg = sa->sec_ctx->ctx_alg; + if (len) + *len = sa->sec_ctx->ctx_len; + if (ctx_str) + memcpy (ctx_str, sa->sec_ctx->ctx, sa->sec_ctx->ctx_len); + } + else + return -1; + + return 0; +} + +/** + * Set the security context. + * + * @arg sa The xfrmnl_sa object. + * @arg doi Parameter for the security context domain of interpretation. + * @arg alg Parameter for the security context algorithm. + * @arg len Parameter for the length of the security context string containing + * the terminating null byte ('\0'). + * @arg sid Unused parameter. + * @arg ctx_str Buffer containing the security context string. + * + * @return 0 on success or a negative error code. + */ +int xfrmnl_sa_set_sec_ctx (struct xfrmnl_sa* sa, unsigned int doi, unsigned int alg, unsigned int len, + unsigned int sid, const char* ctx_str) +{ + _nl_auto_free struct xfrmnl_user_sec_ctx *b = NULL; + + if (!(b = calloc(1, sizeof (struct xfrmnl_user_sec_ctx) + 1 + len))) + return -1; + + b->len = sizeof(struct xfrmnl_user_sec_ctx) + len; + b->exttype = XFRMA_SEC_CTX; + b->ctx_alg = alg; + b->ctx_doi = doi; + b->ctx_len = len; + memcpy (b->ctx, ctx_str, len); + b->ctx[len] = '\0'; + + free(sa->sec_ctx); + sa->sec_ctx = _nl_steal_pointer(&b); + sa->ce_mask |= XFRM_SA_ATTR_SECCTX; + return 0; +} + + +int xfrmnl_sa_get_replay_maxage (struct xfrmnl_sa* sa) +{ + if (sa->ce_mask & XFRM_SA_ATTR_REPLAY_MAXAGE) + return sa->replay_maxage; + else + return -1; +} + +int xfrmnl_sa_set_replay_maxage (struct xfrmnl_sa* sa, unsigned int replay_maxage) +{ + sa->replay_maxage = replay_maxage; + sa->ce_mask |= XFRM_SA_ATTR_REPLAY_MAXAGE; + + return 0; +} + +int xfrmnl_sa_get_replay_maxdiff (struct xfrmnl_sa* sa) +{ + if (sa->ce_mask & XFRM_SA_ATTR_REPLAY_MAXDIFF) + return sa->replay_maxdiff; + else + return -1; +} + +int xfrmnl_sa_set_replay_maxdiff (struct xfrmnl_sa* sa, unsigned int replay_maxdiff) +{ + sa->replay_maxdiff = replay_maxdiff; + sa->ce_mask |= XFRM_SA_ATTR_REPLAY_MAXDIFF; + + return 0; +} + +int xfrmnl_sa_get_replay_state (struct xfrmnl_sa* sa, unsigned int* oseq, unsigned int* seq, unsigned int* bmp) +{ + if (sa->ce_mask & XFRM_SA_ATTR_REPLAY_STATE) + { + if (sa->replay_state_esn == NULL) + { + *oseq = sa->replay_state.oseq; + *seq = sa->replay_state.seq; + *bmp = sa->replay_state.bitmap; + + return 0; + } + else + { + return -1; + } + } + else + return -1; +} + +int xfrmnl_sa_set_replay_state (struct xfrmnl_sa* sa, unsigned int oseq, unsigned int seq, unsigned int bitmap) +{ + sa->replay_state.oseq = oseq; + sa->replay_state.seq = seq; + sa->replay_state.bitmap = bitmap; + sa->ce_mask |= XFRM_SA_ATTR_REPLAY_STATE; + + return 0; +} + +int xfrmnl_sa_get_replay_state_esn (struct xfrmnl_sa* sa, unsigned int* oseq, unsigned int* seq, unsigned int* oseq_hi, + unsigned int* seq_hi, unsigned int* replay_window, unsigned int* bmp_len, unsigned int* bmp) +{ + if (sa->ce_mask & XFRM_SA_ATTR_REPLAY_STATE) + { + if (sa->replay_state_esn) + { + *oseq = sa->replay_state_esn->oseq; + *seq = sa->replay_state_esn->seq; + *oseq_hi= sa->replay_state_esn->oseq_hi; + *seq_hi = sa->replay_state_esn->seq_hi; + *replay_window = sa->replay_state_esn->replay_window; + *bmp_len = sa->replay_state_esn->bmp_len; // In number of 32 bit words + memcpy (bmp, sa->replay_state_esn->bmp, sa->replay_state_esn->bmp_len * sizeof (uint32_t)); + + return 0; + } + else + { + return -1; + } + } + else + return -1; +} + +int xfrmnl_sa_set_replay_state_esn (struct xfrmnl_sa* sa, unsigned int oseq, unsigned int seq, + unsigned int oseq_hi, unsigned int seq_hi, unsigned int replay_window, + unsigned int bmp_len, unsigned int* bmp) +{ + _nl_auto_free struct xfrmnl_replay_state_esn *b = NULL; + + if (!(b = calloc (1, sizeof (struct xfrmnl_replay_state_esn) + (sizeof (uint32_t) * bmp_len)))) + return -1; + + b->oseq = oseq; + b->seq = seq; + b->oseq_hi = oseq_hi; + b->seq_hi = seq_hi; + b->replay_window = replay_window; + b->bmp_len = bmp_len; // In number of 32 bit words + memcpy (b->bmp, bmp, bmp_len * sizeof (uint32_t)); + + free(sa->replay_state_esn); + sa->replay_state_esn = _nl_steal_pointer(&b); + sa->ce_mask |= XFRM_SA_ATTR_REPLAY_STATE; + return 0; +} + + +/** + * Get interface id and flags from xfrm_user_offload. + * + * @arg sa The xfrmnl_sa object. + * @arg ifindex An optional output value for the offload interface index. + * @arg flags An optional output value for the offload flags. + * + * @return 0 on success or a negative error code. + */ +int xfrmnl_sa_get_user_offload(struct xfrmnl_sa *sa, int *ifindex, uint8_t *flags) +{ + int ret = -1; + + if (sa->ce_mask & XFRM_SA_ATTR_OFFLOAD_DEV && sa->user_offload) { + if (ifindex) + *ifindex = sa->user_offload->ifindex; + if (flags) + *flags = sa->user_offload->flags; + ret = 0; + } + + return ret; +} + + +/** + * Set interface id and flags for xfrm_user_offload. + * + * @arg sa The xfrmnl_sa object. + * @arg ifindex Id of the offload interface. + * @arg flags Offload flags for the state. + * + * @return 0 on success or a negative error code. + */ +int xfrmnl_sa_set_user_offload(struct xfrmnl_sa *sa, int ifindex, uint8_t flags) +{ + _nl_auto_free struct xfrmnl_user_offload *b = NULL; + + if (!(b = calloc(1, sizeof(*b)))) + return -1; + + b->ifindex = ifindex; + b->flags = flags; + + free(sa->user_offload); + sa->user_offload = _nl_steal_pointer(&b); + sa->ce_mask |= XFRM_SA_ATTR_OFFLOAD_DEV; + + return 0; +} + +int xfrmnl_sa_is_hardexpiry_reached (struct xfrmnl_sa* sa) +{ + if (sa->ce_mask & XFRM_SA_ATTR_EXPIRE) + return (sa->hard > 0 ? 1: 0); + else + return 0; +} + +int xfrmnl_sa_is_expiry_reached (struct xfrmnl_sa* sa) +{ + if (sa->ce_mask & XFRM_SA_ATTR_EXPIRE) + return 1; + else + return 0; +} + +/** @} */ + +static struct nl_object_ops xfrm_sa_obj_ops = { + .oo_name = "xfrm/sa", + .oo_size = sizeof(struct xfrmnl_sa), + .oo_constructor = xfrm_sa_alloc_data, + .oo_free_data = xfrm_sa_free_data, + .oo_clone = xfrm_sa_clone, + .oo_dump = { + [NL_DUMP_LINE] = xfrm_sa_dump_line, + [NL_DUMP_DETAILS] = xfrm_sa_dump_details, + [NL_DUMP_STATS] = xfrm_sa_dump_stats, + }, + .oo_compare = xfrm_sa_compare, + .oo_attrs2str = xfrm_sa_attrs2str, + .oo_id_attrs = (XFRM_SA_ATTR_DADDR | XFRM_SA_ATTR_SPI | XFRM_SA_ATTR_PROTO), +}; + +static struct nl_af_group xfrm_sa_groups[] = { + { AF_UNSPEC, XFRMNLGRP_SA }, + { AF_UNSPEC, XFRMNLGRP_EXPIRE }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops xfrmnl_sa_ops = { + .co_name = "xfrm/sa", + .co_hdrsize = sizeof(struct xfrm_usersa_info), + .co_msgtypes = { + { XFRM_MSG_NEWSA, NL_ACT_NEW, "new" }, + { XFRM_MSG_DELSA, NL_ACT_DEL, "del" }, + { XFRM_MSG_GETSA, NL_ACT_GET, "get" }, + { XFRM_MSG_EXPIRE, NL_ACT_UNSPEC, "expire"}, + { XFRM_MSG_UPDSA, NL_ACT_NEW, "update"}, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_XFRM, + .co_groups = xfrm_sa_groups, + .co_request_update = xfrm_sa_request_update, + .co_msg_parser = xfrm_sa_msg_parser, + .co_obj_ops = &xfrm_sa_obj_ops, + .co_include_event = &xfrm_sa_update_cache +}; + +/** + * @name XFRM SA Cache Managament + * @{ + */ + +static void _nl_init xfrm_sa_init(void) +{ + nl_cache_mngt_register(&xfrmnl_sa_ops); +} + +static void _nl_exit xfrm_sa_exit(void) +{ + nl_cache_mngt_unregister(&xfrmnl_sa_ops); +} + +/** @} */ diff --git a/libnl/lib/xfrm/selector.c b/libnl/lib/xfrm/selector.c new file mode 100644 index 0000000..4f14b7b --- /dev/null +++ b/libnl/lib/xfrm/selector.c @@ -0,0 +1,371 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * Neither the name of Texas Instruments Incorporated 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 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. + * + */ +/** + * @ingroup xfrmnl + * @defgroup XFRM Address Selector + * + * Abstract data type representing XFRM SA/SP selector properties + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#include "nl-default.h" + +#include + +/* Selector, used as selector both on policy rules (SPD) and SAs. */ +struct xfrmnl_sel { + uint32_t refcnt; + struct nl_addr* daddr; + struct nl_addr* saddr; + uint16_t dport; + uint16_t dport_mask; + uint16_t sport; + uint16_t sport_mask; + uint16_t family; + uint8_t prefixlen_d; + uint8_t prefixlen_s; + uint8_t proto; + int32_t ifindex; + uint32_t user; +}; + +static void sel_destroy(struct xfrmnl_sel* sel) +{ + if (!sel) + return; + + if (sel->refcnt != 1) + { + fprintf(stderr, "BUG: %s:%d\n", __FILE__, __LINE__); + assert(0); + } + + nl_addr_put (sel->daddr); + nl_addr_put (sel->saddr); + free(sel); +} + +/** + * @name Creating Selector + * @{ + */ + +/** + * Allocate new selector object. + * @return Newly allocated selector object or NULL + */ +struct xfrmnl_sel* xfrmnl_sel_alloc() +{ + struct xfrmnl_sel* sel; + + sel = calloc(1, sizeof(struct xfrmnl_sel)); + if (!sel) + return NULL; + + sel->refcnt = 1; + + return sel; +} + +/** + * Clone existing selector object. + * @arg sel Selector object. + * @return Newly allocated selector object being a duplicate of the + * specified selector object or NULL if a failure occured. + */ +struct xfrmnl_sel* xfrmnl_sel_clone(struct xfrmnl_sel* sel) +{ + struct xfrmnl_sel* new; + + new = xfrmnl_sel_alloc(); + if (!new) + return NULL; + + memcpy(new, sel, sizeof(struct xfrmnl_sel)); + new->daddr = nl_addr_clone(sel->daddr); + new->saddr = nl_addr_clone(sel->saddr); + + return new; +} + +/** @} */ + +/** + * @name Managing Usage References + * @{ + */ + +struct xfrmnl_sel* xfrmnl_sel_get(struct xfrmnl_sel* sel) +{ + sel->refcnt++; + + return sel; +} + +void xfrmnl_sel_put(struct xfrmnl_sel* sel) +{ + if (!sel) + return; + + if (sel->refcnt == 1) + sel_destroy(sel); + else + sel->refcnt--; +} + +/** + * Check whether an selector object is shared. + * @arg addr Selector object. + * @return Non-zero if the selector object is shared, otherwise 0. + */ +int xfrmnl_sel_shared(struct xfrmnl_sel* sel) +{ + return sel->refcnt > 1; +} + +/** @} */ + +/** + * @name Miscellaneous + * @{ + */ + +/** + * Compares two selector objects. + * @arg a A selector object. + * @arg b Another selector object. + * + * @return Non zero if difference is found, 0 otherwise if both + * the objects are identical. + */ +int xfrmnl_sel_cmp(struct xfrmnl_sel* a, struct xfrmnl_sel* b) +{ + /* Check for any differences */ + if ((nl_addr_cmp_prefix (a->daddr, b->daddr) != 0) || + (nl_addr_cmp_prefix (a->saddr, b->saddr) != 0) || + ((a->sport & a->sport_mask) != (b->sport & b->sport_mask)) || + ((a->dport & a->dport_mask) != (b->dport & b->dport_mask)) || + (a->family != b->family) || + (a->proto && (a->proto != b->proto)) || + (a->ifindex && a->ifindex != b->ifindex) || + (a->user != b->user)) + return 1; + + /* The objects are identical */ + return 0; +} + +void xfrmnl_sel_dump(struct xfrmnl_sel* sel, struct nl_dump_params *p) +{ + char dst[INET6_ADDRSTRLEN+5], src[INET6_ADDRSTRLEN+5]; + char buf [128]; + + nl_dump_line(p, "\t\tsrc %s dst %s family: %s\n", nl_addr2str(sel->saddr, src, sizeof(src)), + nl_addr2str (sel->daddr, dst, sizeof (dst)), nl_af2str (sel->family, buf, 128)); + nl_dump_line (p, "\t\tsrc port/mask: %d/%d dst port/mask: %d/%d\n", + sel->dport, sel->dport_mask, sel->sport, sel->sport_mask); + nl_dump_line (p, "\t\tprotocol: %s ifindex: %u user: %u\n", + nl_ip_proto2str (sel->proto, buf, sizeof(buf)), sel->ifindex, sel->user); + + return; +} + + +/** @} */ + +/** + * @name Attributes + * @{ + */ +struct nl_addr* xfrmnl_sel_get_daddr (struct xfrmnl_sel* sel) +{ + return sel->daddr; +} + +int xfrmnl_sel_set_daddr (struct xfrmnl_sel* sel, struct nl_addr* addr) +{ + /* Increment reference counter on this to keep this address + * object around while selector in use */ + nl_addr_get(addr); + + sel->daddr = addr; + + return 0; +} + +struct nl_addr* xfrmnl_sel_get_saddr (struct xfrmnl_sel* sel) +{ + return sel->saddr; +} + +int xfrmnl_sel_set_saddr (struct xfrmnl_sel* sel, struct nl_addr* addr) +{ + /* Increment reference counter on this to keep this address + * object around while selector in use */ + nl_addr_get(addr); + + sel->saddr = addr; + + return 0; +} + +int xfrmnl_sel_get_dport (struct xfrmnl_sel* sel) +{ + return sel->dport; +} + +int xfrmnl_sel_set_dport (struct xfrmnl_sel* sel, unsigned int dport) +{ + sel->dport = dport; + + return 0; +} + +int xfrmnl_sel_get_dportmask (struct xfrmnl_sel* sel) +{ + return sel->dport_mask; +} + +int xfrmnl_sel_set_dportmask (struct xfrmnl_sel* sel, unsigned int dport_mask) +{ + sel->dport_mask = dport_mask; + + return 0; +} + +int xfrmnl_sel_get_sport (struct xfrmnl_sel* sel) +{ + return sel->sport; +} + +int xfrmnl_sel_set_sport (struct xfrmnl_sel* sel, unsigned int sport) +{ + sel->sport = sport; + + return 0; +} + +int xfrmnl_sel_get_sportmask (struct xfrmnl_sel* sel) +{ + return sel->sport_mask; +} + +int xfrmnl_sel_set_sportmask (struct xfrmnl_sel* sel, unsigned int sport_mask) +{ + sel->sport_mask = sport_mask; + + return 0; +} + +int xfrmnl_sel_get_family(struct xfrmnl_sel *sel) +{ + return sel->family; +} + +int xfrmnl_sel_set_family(struct xfrmnl_sel *sel, unsigned int family) +{ + sel->family = family; + + return 0; +} + +int xfrmnl_sel_get_prefixlen_d (struct xfrmnl_sel* sel) +{ + return sel->prefixlen_d; +} + +int xfrmnl_sel_set_prefixlen_d (struct xfrmnl_sel* sel, unsigned int prefixlen) +{ + sel->prefixlen_d = prefixlen; + + return 0; +} + +int xfrmnl_sel_get_prefixlen_s (struct xfrmnl_sel* sel) +{ + return sel->prefixlen_s; +} + +int xfrmnl_sel_set_prefixlen_s (struct xfrmnl_sel* sel, unsigned int prefixlen) +{ + sel->prefixlen_s = prefixlen; + + return 0; +} + +int xfrmnl_sel_get_proto (struct xfrmnl_sel* sel) +{ + return sel->proto; +} + +int xfrmnl_sel_set_proto (struct xfrmnl_sel* sel, unsigned int protocol) +{ + sel->proto = protocol; + + return 0; +} + +int xfrmnl_sel_get_ifindex (struct xfrmnl_sel* sel) +{ + return sel->ifindex; +} + +int xfrmnl_sel_set_ifindex (struct xfrmnl_sel* sel, unsigned int ifindex) +{ + sel->ifindex = ifindex; + + return 0; +} + +int xfrmnl_sel_get_userid (struct xfrmnl_sel* sel) +{ + return sel->user; +} + +int xfrmnl_sel_set_userid (struct xfrmnl_sel* sel, unsigned int userid) +{ + sel->user = userid; + return 0; +} + + +/** @} */ diff --git a/libnl/lib/xfrm/sp.c b/libnl/lib/xfrm/sp.c new file mode 100644 index 0000000..814ac48 --- /dev/null +++ b/libnl/lib/xfrm/sp.c @@ -0,0 +1,1466 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * Neither the name of Texas Instruments Incorporated 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 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. + * + */ + +/** + * @ingroup xfrmnl + * @defgroup sp Security Policy + * @brief + */ + +#include "nl-default.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nl-xfrm.h" +#include "nl-priv-dynamic-core/object-api.h" +#include "nl-priv-dynamic-core/nl-core.h" +#include "nl-priv-dynamic-core/cache-api.h" +#include "nl-aux-core/nl-core.h" +#include "nl-aux-xfrm/nl-xfrm.h" + +struct xfrmnl_userpolicy_type { + uint8_t type; + uint16_t reserved1; + uint16_t reserved2; +}; + +struct xfrmnl_sp { + NLHDR_COMMON + + struct xfrmnl_sel* sel; + struct xfrmnl_ltime_cfg* lft; + struct xfrmnl_lifetime_cur curlft; + uint32_t priority; + uint32_t index; + uint8_t dir; + uint8_t action; + uint8_t flags; + uint8_t share; + struct xfrmnl_user_sec_ctx* sec_ctx; + struct xfrmnl_userpolicy_type uptype; + uint32_t nr_user_tmpl; + struct nl_list_head usertmpl_list; + struct xfrmnl_mark mark; +}; + +/** @cond SKIP */ +#define XFRM_SP_ATTR_SEL 0x01 +#define XFRM_SP_ATTR_LTIME_CFG 0x02 +#define XFRM_SP_ATTR_LTIME_CUR 0x04 +#define XFRM_SP_ATTR_PRIO 0x08 +#define XFRM_SP_ATTR_INDEX 0x10 +#define XFRM_SP_ATTR_DIR 0x20 +#define XFRM_SP_ATTR_ACTION 0x40 +#define XFRM_SP_ATTR_FLAGS 0x80 +#define XFRM_SP_ATTR_SHARE 0x100 +#define XFRM_SP_ATTR_POLTYPE 0x200 +#define XFRM_SP_ATTR_SECCTX 0x400 +#define XFRM_SP_ATTR_TMPL 0x800 +#define XFRM_SP_ATTR_MARK 0x1000 + +static struct nl_cache_ops xfrmnl_sp_ops; +static struct nl_object_ops xfrm_sp_obj_ops; +/** @endcond */ + +static void xfrm_sp_alloc_data(struct nl_object *c) +{ + struct xfrmnl_sp* sp = nl_object_priv (c); + + if ((sp->sel = xfrmnl_sel_alloc ()) == NULL) + return; + + if ((sp->lft = xfrmnl_ltime_cfg_alloc ()) == NULL) + return; + + nl_init_list_head(&sp->usertmpl_list); + + return; +} + +static void xfrm_sp_free_data(struct nl_object *c) +{ + struct xfrmnl_sp* sp = nl_object_priv (c); + struct xfrmnl_user_tmpl *utmpl, *tmp; + + if (sp == NULL) + return; + + xfrmnl_sel_put (sp->sel); + xfrmnl_ltime_cfg_put (sp->lft); + + if (sp->sec_ctx) { + free(sp->sec_ctx); + } + + nl_list_for_each_entry_safe(utmpl, tmp, &sp->usertmpl_list, utmpl_list) { + xfrmnl_sp_remove_usertemplate (sp, utmpl); + xfrmnl_user_tmpl_free (utmpl); + } +} + +static int xfrm_sp_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct xfrmnl_sp* dst = nl_object_priv(_dst); + struct xfrmnl_sp* src = nl_object_priv(_src); + struct xfrmnl_user_tmpl *utmpl; + struct xfrmnl_user_tmpl *new; + + dst->sel = NULL; + dst->lft = NULL; + dst->sec_ctx = NULL; + nl_init_list_head(&dst->usertmpl_list); + + if (src->sel) { + if ((dst->sel = xfrmnl_sel_clone (src->sel)) == NULL) + return -NLE_NOMEM; + } + + if (src->lft) { + if ((dst->lft = xfrmnl_ltime_cfg_clone (src->lft)) == NULL) + return -NLE_NOMEM; + } + + if (src->sec_ctx) { + uint32_t len = sizeof (struct xfrmnl_user_sec_ctx) + src->sec_ctx->ctx_len; + + if ((dst->sec_ctx = malloc (len)) == NULL) + return -NLE_NOMEM; + memcpy(dst->sec_ctx, src->sec_ctx, len); + } + + nl_list_for_each_entry(utmpl, &src->usertmpl_list, utmpl_list) { + new = xfrmnl_user_tmpl_clone (utmpl); + if (!new) + return -NLE_NOMEM; + xfrmnl_sp_add_usertemplate(dst, new); + } + + return 0; +} + +static uint64_t xfrm_sp_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct xfrmnl_sp* a = (struct xfrmnl_sp *) _a; + struct xfrmnl_sp* b = (struct xfrmnl_sp *) _b; + struct xfrmnl_user_tmpl *tmpl_a, *tmpl_b; + uint64_t diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) + diff |= _DIFF(XFRM_SP_ATTR_SEL, xfrmnl_sel_cmp(a->sel, b->sel)); + diff |= _DIFF(XFRM_SP_ATTR_LTIME_CFG, + xfrmnl_ltime_cfg_cmp(a->lft, b->lft)); + diff |= _DIFF(XFRM_SP_ATTR_PRIO, a->priority != b->priority); + diff |= _DIFF(XFRM_SP_ATTR_INDEX, a->index != b->index); + diff |= _DIFF(XFRM_SP_ATTR_DIR, a->dir != b->dir); + diff |= _DIFF(XFRM_SP_ATTR_ACTION, a->action != b->action); + diff |= _DIFF(XFRM_SP_ATTR_FLAGS, a->flags != b->flags); + diff |= _DIFF(XFRM_SP_ATTR_SHARE, a->share != b->share); + diff |= _DIFF(XFRM_SP_ATTR_SECCTX, + ((a->sec_ctx->len != b->sec_ctx->len) || + (a->sec_ctx->exttype != b->sec_ctx->exttype) || + (a->sec_ctx->ctx_alg != b->sec_ctx->ctx_alg) || + (a->sec_ctx->ctx_doi != b->sec_ctx->ctx_doi) || + (a->sec_ctx->ctx_len != b->sec_ctx->ctx_len) || + strcmp(a->sec_ctx->ctx, b->sec_ctx->ctx))); + diff |= _DIFF(XFRM_SP_ATTR_POLTYPE, (a->uptype.type != b->uptype.type)); + diff |= _DIFF(XFRM_SP_ATTR_TMPL, (a->nr_user_tmpl != b->nr_user_tmpl)); + diff |= _DIFF(XFRM_SP_ATTR_MARK, + (a->mark.m != b->mark.m) || (a->mark.v != b->mark.v)); + + /* Compare the templates */ + nl_list_for_each_entry(tmpl_b, &b->usertmpl_list, utmpl_list) + nl_list_for_each_entry(tmpl_a, &a->usertmpl_list, utmpl_list) + diff |= xfrmnl_user_tmpl_cmp (tmpl_a, tmpl_b); +#undef _DIFF + + return diff; +} + +/** + * @name XFRM SP Attribute Translations + * @{ + */ +static const struct trans_tbl sp_attrs[] = { + __ADD(XFRM_SP_ATTR_SEL, selector), + __ADD(XFRM_SP_ATTR_LTIME_CFG, lifetime_cfg), + __ADD(XFRM_SP_ATTR_LTIME_CUR, lifetime_cur), + __ADD(XFRM_SP_ATTR_PRIO, priority), + __ADD(XFRM_SP_ATTR_INDEX, index), + __ADD(XFRM_SP_ATTR_DIR, direction), + __ADD(XFRM_SP_ATTR_ACTION, action), + __ADD(XFRM_SP_ATTR_FLAGS, flags), + __ADD(XFRM_SP_ATTR_SHARE, share), + __ADD(XFRM_SP_ATTR_POLTYPE, policy_type), + __ADD(XFRM_SP_ATTR_SECCTX, security_context), + __ADD(XFRM_SP_ATTR_TMPL, user_template), + __ADD(XFRM_SP_ATTR_MARK, mark), +}; + +static char* xfrm_sp_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str (attrs, buf, len, sp_attrs, ARRAY_SIZE(sp_attrs)); +} +/** @} */ + +/** + * @name XFRM SP Action Translations + * @{ + */ +static const struct trans_tbl sa_actions[] = { + __ADD(XFRM_POLICY_ALLOW, allow), + __ADD(XFRM_POLICY_BLOCK, block), +}; + +char* xfrmnl_sp_action2str(int action, char *buf, size_t len) +{ + return __type2str (action, buf, len, sa_actions, ARRAY_SIZE(sa_actions)); +} + +int xfrmnl_sp_str2action(const char *name) +{ + return __str2type (name, sa_actions, ARRAY_SIZE(sa_actions)); +} +/** @} */ + +/** + * @name XFRM SP Flags Translations + * @{ + */ +static const struct trans_tbl sp_flags[] = { + __ADD(XFRM_POLICY_LOCALOK, allow policy override by user), + __ADD(XFRM_POLICY_ICMP, auto include ICMP in policy), +}; + +char* xfrmnl_sp_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str (flags, buf, len, sp_flags, ARRAY_SIZE(sp_flags)); +} + +int xfrmnl_sp_str2flag(const char *name) +{ + return __str2flags(name, sp_flags, ARRAY_SIZE(sp_flags)); +} +/** @} */ + +/** + * @name XFRM SP Type Translations + * @{ + */ +static const struct trans_tbl sp_types[] = { + __ADD(XFRM_POLICY_TYPE_MAIN, main), + __ADD(XFRM_POLICY_TYPE_SUB, sub), + __ADD(XFRM_POLICY_TYPE_MAX, max), + __ADD(XFRM_POLICY_TYPE_ANY, any), +}; + +char* xfrmnl_sp_type2str(int type, char *buf, size_t len) +{ + return __type2str(type, buf, len, sp_types, ARRAY_SIZE(sp_types)); +} + +int xfrmnl_sp_str2type(const char *name) +{ + return __str2type(name, sp_types, ARRAY_SIZE(sp_types)); +} +/** @} */ + +/** + * @name XFRM SP Direction Translations + * @{ + */ +static const struct trans_tbl sp_dir[] = { + __ADD(XFRM_POLICY_IN, in), + __ADD(XFRM_POLICY_OUT, out), + __ADD(XFRM_POLICY_FWD, fwd), + __ADD(XFRM_POLICY_MASK, mask), +}; + +char* xfrmnl_sp_dir2str(int dir, char *buf, size_t len) +{ + return __type2str (dir, buf, len, sp_dir, ARRAY_SIZE(sp_dir)); +} + +int xfrmnl_sp_str2dir(const char *name) +{ + return __str2type (name, sp_dir, ARRAY_SIZE(sp_dir)); +} + +int xfrmnl_sp_index2dir (unsigned int index) +{ + return index & 0x7; +} +/** @} */ + +/** + * @name XFRM SP Share Translations + * @{ + */ +static const struct trans_tbl sp_share[] = { + __ADD(XFRM_SHARE_ANY, any), + __ADD(XFRM_SHARE_SESSION, session), + __ADD(XFRM_SHARE_USER, user), + __ADD(XFRM_SHARE_UNIQUE, unique), +}; + +char* xfrmnl_sp_share2str(int share, char *buf, size_t len) +{ + return __type2str (share, buf, len, sp_share, ARRAY_SIZE(sp_share)); +} + +int xfrmnl_sp_str2share(const char *name) +{ + return __str2type (name, sp_share, ARRAY_SIZE(sp_share)); +} +/** @} */ + +static void xfrm_sp_dump_line(struct nl_object *a, struct nl_dump_params *p) +{ + struct xfrmnl_sp* sp = (struct xfrmnl_sp *) a; + char dir[32], action[32], share[32], flags[32]; + char dst[INET6_ADDRSTRLEN+5], src[INET6_ADDRSTRLEN+5]; + time_t add_time, use_time; + struct tm *add_time_tm, *use_time_tm; + struct tm tm_buf; + + nl_addr2str(xfrmnl_sel_get_saddr (sp->sel), src, sizeof(src)); + nl_addr2str (xfrmnl_sel_get_daddr (sp->sel), dst, sizeof (dst)); + nl_af2str (xfrmnl_sel_get_family (sp->sel), dir, 32); + nl_dump_line(p, "src %s dst %s family: %s\n", src, dst, dir); + nl_dump_line (p, "src port/mask: %d/%d dst port/mask: %d/%d\n", + xfrmnl_sel_get_dport (sp->sel), xfrmnl_sel_get_dportmask (sp->sel), + xfrmnl_sel_get_sport (sp->sel), xfrmnl_sel_get_sportmask (sp->sel)); + nl_dump_line (p, "protocol: %s ifindex: %u uid: %u\n", + nl_ip_proto2str (xfrmnl_sel_get_proto (sp->sel), dir, sizeof(dir)), + xfrmnl_sel_get_ifindex (sp->sel), + xfrmnl_sel_get_userid (sp->sel)); + + xfrmnl_sp_dir2str (sp->dir, dir, 32); + xfrmnl_sp_action2str (sp->action, action, 32); + xfrmnl_sp_share2str (sp->share, share, 32); + xfrmnl_sp_flags2str (sp->flags, flags, 32); + nl_dump_line(p, "\tdir: %s action: %s index: %u priority: %u share: %s flags: %s(0x%x) \n", + dir, action, sp->index, sp->priority, share, flags, sp->flags); + + nl_dump_line(p, "\tlifetime configuration: \n"); + if (sp->lft->soft_byte_limit == XFRM_INF) + sprintf (dir, "INF"); + else + sprintf (dir, "%" PRIu64, sp->lft->soft_byte_limit); + if (sp->lft->soft_packet_limit == XFRM_INF) + sprintf (action, "INF"); + else + sprintf (action, "%" PRIu64, sp->lft->soft_packet_limit); + if (sp->lft->hard_byte_limit == XFRM_INF) + sprintf (flags, "INF"); + else + sprintf (flags, "%" PRIu64, sp->lft->hard_byte_limit); + if (sp->lft->hard_packet_limit == XFRM_INF) + sprintf (share, "INF"); + else + sprintf (share, "%" PRIu64, sp->lft->hard_packet_limit); + nl_dump_line(p, "\t\tsoft limit: %s (bytes), %s (packets) \n", dir, + action); + nl_dump_line(p, "\t\thard limit: %s (bytes), %s (packets) \n", flags, + share); + nl_dump_line( + p, + "\t\tsoft add_time: %llu (seconds), soft use_time: %llu (seconds) \n", + (long long unsigned)sp->lft->soft_add_expires_seconds, + (long long unsigned)sp->lft->soft_use_expires_seconds); + nl_dump_line( + p, + "\t\thard add_time: %llu (seconds), hard use_time: %llu (seconds) \n", + (long long unsigned)sp->lft->hard_add_expires_seconds, + (long long unsigned)sp->lft->hard_use_expires_seconds); + + nl_dump_line(p, "\tlifetime current: \n"); + nl_dump_line(p, "\t\t%llu bytes, %llu packets\n", + (long long unsigned)sp->curlft.bytes, + (long long unsigned)sp->curlft.packets); + + if (sp->curlft.add_time != 0) + { + add_time = sp->curlft.add_time; + add_time_tm = gmtime_r (&add_time, &tm_buf); + strftime (dst, INET6_ADDRSTRLEN+5, "%Y-%m-%d %H-%M-%S", add_time_tm); + } + else + { + sprintf (dst, "%s", "-"); + } + + if (sp->curlft.use_time != 0) + { + use_time = sp->curlft.use_time; + use_time_tm = gmtime_r (&use_time, &tm_buf); + strftime (src, INET6_ADDRSTRLEN+5, "%Y-%m-%d %H-%M-%S", use_time_tm); + } + else + { + sprintf (src, "%s", "-"); + } + nl_dump_line(p, "\t\tadd_time: %s, use_time: %s\n", dst, src); + + if (sp->ce_mask & XFRM_SP_ATTR_SECCTX) + { + nl_dump_line(p, "\tUser security context: \n"); + nl_dump_line(p, "\t\tlen: %d exttype: %d Algo: %d DOI: %d ctxlen: %d\n", + sp->sec_ctx->len, sp->sec_ctx->exttype, + sp->sec_ctx->ctx_alg, sp->sec_ctx->ctx_doi, sp->sec_ctx->ctx_len); + nl_dump_line (p, "\t\tctx: %s \n", sp->sec_ctx->ctx); + } + + xfrmnl_sp_type2str (sp->uptype.type, flags, 32); + if (sp->ce_mask & XFRM_SP_ATTR_POLTYPE) + nl_dump_line(p, "\tUser policy type: %s\n", flags); + + if (sp->ce_mask & XFRM_SP_ATTR_TMPL) + { + struct xfrmnl_user_tmpl* utmpl; + + nl_dump_line(p, "\tUser template: \n"); + + nl_list_for_each_entry(utmpl, &sp->usertmpl_list, utmpl_list) + xfrmnl_user_tmpl_dump (utmpl, p); + } + + if (sp->ce_mask & XFRM_SP_ATTR_MARK) + nl_dump_line(p, "\tMark mask: 0x%x Mark value: 0x%x\n", sp->mark.m, sp->mark.v); + + nl_dump(p, "\n"); +} + +static void xfrm_sp_dump_details(struct nl_object *a, struct nl_dump_params *p) +{ + xfrm_sp_dump_line(a, p); +} + +static void xfrm_sp_dump_stats(struct nl_object *a, struct nl_dump_params *p) +{ + xfrm_sp_dump_details(a, p); + + return; +} + +/** + * @name XFRM SP Object Allocation/Freeage + * @{ + */ + +struct xfrmnl_sp* xfrmnl_sp_alloc(void) +{ + return (struct xfrmnl_sp*) nl_object_alloc(&xfrm_sp_obj_ops); +} + +void xfrmnl_sp_put(struct xfrmnl_sp* sp) +{ + nl_object_put((struct nl_object *) sp); +} + +/** @} */ + +/** + * @name SP Cache Managament + * @{ + */ + +/** + * Build a SP cache including all SPs currently configured in the kernel. + * @arg sock Netlink socket. + * @arg result Pointer to store resulting cache. + * + * Allocates a new SP cache, initializes it properly and updates it + * to include all SPs currently configured in the kernel. + * + * @return 0 on success or a negative error code. + */ +int xfrmnl_sp_alloc_cache(struct nl_sock *sock, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&xfrmnl_sp_ops, sock, result); +} + +/** + * Look up a SP by policy id and direction + * @arg cache SP cache + * @arg index Policy Id + * @arg dir direction + * @return sp handle or NULL if no match was found. + */ +struct xfrmnl_sp* xfrmnl_sp_get(struct nl_cache* cache, unsigned int index, unsigned int dir) +{ + struct xfrmnl_sp *sp; + + //nl_list_for_each_entry(sp, &cache->c_items, ce_list) { + for (sp = (struct xfrmnl_sp*)nl_cache_get_first (cache); + sp != NULL; + sp = (struct xfrmnl_sp*)nl_cache_get_next ((struct nl_object*)sp)) + { + if (sp->index == index && sp->dir == dir) + { + nl_object_get((struct nl_object *) sp); + return sp; + } + } + + return NULL; +} + + +/** @} */ + + +static struct nla_policy xfrm_sp_policy[XFRMA_MAX+1] = { + [XFRMA_POLICY] = { .minlen = sizeof(struct xfrm_userpolicy_info)}, + [XFRMA_SEC_CTX] = { .minlen = sizeof(struct xfrm_sec_ctx) }, + [XFRMA_TMPL] = { .minlen = sizeof(struct xfrm_user_tmpl) }, + [XFRMA_POLICY_TYPE] = { .minlen = sizeof(struct xfrm_userpolicy_type)}, + [XFRMA_MARK] = { .minlen = sizeof(struct xfrm_mark) }, +}; + +static int xfrm_sp_request_update(struct nl_cache *c, struct nl_sock *h) +{ + return nl_send_simple (h, XFRM_MSG_GETPOLICY, NLM_F_DUMP, NULL, 0); +} + +int xfrmnl_sp_parse(struct nlmsghdr *n, struct xfrmnl_sp **result) +{ + _nl_auto_nl_addr struct nl_addr *addr1 = NULL; + _nl_auto_nl_addr struct nl_addr *addr2 = NULL; + _nl_auto_xfrmnl_sp struct xfrmnl_sp *sp = NULL; + struct nlattr *tb[XFRMA_MAX + 1]; + struct xfrm_userpolicy_info *sp_info; + int len, err; + + sp = xfrmnl_sp_alloc(); + if (!sp) + return -NLE_NOMEM; + + sp->ce_msgtype = n->nlmsg_type; + if (n->nlmsg_type == XFRM_MSG_DELPOLICY) + sp_info = (struct xfrm_userpolicy_info*)((char *)nlmsg_data(n) + sizeof (struct xfrm_userpolicy_id) + NLA_HDRLEN); + else + sp_info = nlmsg_data(n); + + err = nlmsg_parse(n, sizeof(struct xfrm_userpolicy_info), tb, XFRMA_MAX, xfrm_sp_policy); + if (err < 0) + return err; + + if (!(addr1 = _nl_addr_build(sp_info->sel.family, &sp_info->sel.daddr))) + return -NLE_NOMEM; + nl_addr_set_prefixlen (addr1, sp_info->sel.prefixlen_d); + xfrmnl_sel_set_daddr (sp->sel, addr1); + xfrmnl_sel_set_prefixlen_d (sp->sel, sp_info->sel.prefixlen_d); + + if (!(addr2 = _nl_addr_build(sp_info->sel.family, &sp_info->sel.saddr))) + return -NLE_NOMEM; + nl_addr_set_prefixlen (addr2, sp_info->sel.prefixlen_s); + xfrmnl_sel_set_saddr (sp->sel, addr2); + xfrmnl_sel_set_prefixlen_s (sp->sel, sp_info->sel.prefixlen_s); + + xfrmnl_sel_set_dport (sp->sel, ntohs (sp_info->sel.dport)); + xfrmnl_sel_set_dportmask (sp->sel, ntohs (sp_info->sel.dport_mask)); + xfrmnl_sel_set_sport (sp->sel, ntohs (sp_info->sel.sport)); + xfrmnl_sel_set_sportmask (sp->sel, ntohs (sp_info->sel.sport_mask)); + xfrmnl_sel_set_family (sp->sel, sp_info->sel.family); + xfrmnl_sel_set_proto (sp->sel, sp_info->sel.proto); + xfrmnl_sel_set_ifindex (sp->sel, sp_info->sel.ifindex); + xfrmnl_sel_set_userid (sp->sel, sp_info->sel.user); + sp->ce_mask |= XFRM_SP_ATTR_SEL; + + sp->lft->soft_byte_limit = sp_info->lft.soft_byte_limit; + sp->lft->hard_byte_limit = sp_info->lft.hard_byte_limit; + sp->lft->soft_packet_limit = sp_info->lft.soft_packet_limit; + sp->lft->hard_packet_limit = sp_info->lft.hard_packet_limit; + sp->lft->soft_add_expires_seconds = sp_info->lft.soft_add_expires_seconds; + sp->lft->hard_add_expires_seconds = sp_info->lft.hard_add_expires_seconds; + sp->lft->soft_use_expires_seconds = sp_info->lft.soft_use_expires_seconds; + sp->lft->hard_use_expires_seconds = sp_info->lft.hard_use_expires_seconds; + sp->ce_mask |= XFRM_SP_ATTR_LTIME_CFG; + + sp->curlft.bytes = sp_info->curlft.bytes; + sp->curlft.packets = sp_info->curlft.packets; + sp->curlft.add_time = sp_info->curlft.add_time; + sp->curlft.use_time = sp_info->curlft.use_time; + sp->ce_mask |= XFRM_SP_ATTR_LTIME_CUR; + + sp->priority = sp_info->priority; + sp->index = sp_info->index; + sp->dir = sp_info->dir; + sp->action = sp_info->action; + sp->flags = sp_info->flags; + sp->share = sp_info->share; + sp->ce_mask |= (XFRM_SP_ATTR_PRIO | XFRM_SP_ATTR_INDEX | + XFRM_SP_ATTR_DIR | XFRM_SP_ATTR_ACTION | + XFRM_SP_ATTR_FLAGS | XFRM_SP_ATTR_SHARE); + + if (tb[XFRMA_SEC_CTX]) { + struct xfrm_user_sec_ctx* ctx = nla_data(tb[XFRMA_SEC_CTX]); + + len = sizeof (struct xfrmnl_user_sec_ctx) + ctx->ctx_len; + if ((sp->sec_ctx = calloc (1, len)) == NULL) + return -NLE_NOMEM; + memcpy ((void *)sp->sec_ctx, (void *)ctx, len); + sp->ce_mask |= XFRM_SP_ATTR_SECCTX; + } + + if (tb[XFRMA_POLICY_TYPE]) { + struct xfrm_userpolicy_type* up = nla_data(tb[XFRMA_POLICY_TYPE]); + + memcpy ((void *)&sp->uptype, (void *)up, sizeof (struct xfrm_userpolicy_type)); + sp->ce_mask |= XFRM_SP_ATTR_POLTYPE; + } + + if (tb[XFRMA_TMPL]) { + struct xfrm_user_tmpl* tmpl = nla_data(tb[XFRMA_TMPL]); + uint32_t i; + uint32_t num_tmpls = nla_len(tb[XFRMA_TMPL]) / sizeof (*tmpl); + + for (i = 0; (i < num_tmpls) && (tmpl); i ++, tmpl++) + { + _nl_auto_xfrmnl_user_tmpl struct xfrmnl_user_tmpl *sputmpl = NULL; + _nl_auto_nl_addr struct nl_addr *addr1 = NULL; + _nl_auto_nl_addr struct nl_addr *addr2 = NULL; + + if ((sputmpl = xfrmnl_user_tmpl_alloc ()) == NULL) + return -NLE_NOMEM; + + if (!(addr1 = _nl_addr_build(tmpl->family, &tmpl->id.daddr))) + return -NLE_NOMEM; + xfrmnl_user_tmpl_set_daddr (sputmpl, addr1); + xfrmnl_user_tmpl_set_spi (sputmpl, ntohl(tmpl->id.spi)); + xfrmnl_user_tmpl_set_proto (sputmpl, tmpl->id.proto); + xfrmnl_user_tmpl_set_family (sputmpl, tmpl->family); + + if (!(addr2 = _nl_addr_build(tmpl->family, &tmpl->saddr))) + return -NLE_NOMEM; + xfrmnl_user_tmpl_set_saddr (sputmpl, addr2); + + xfrmnl_user_tmpl_set_reqid (sputmpl, tmpl->reqid); + xfrmnl_user_tmpl_set_mode (sputmpl, tmpl->mode); + xfrmnl_user_tmpl_set_share (sputmpl, tmpl->share); + xfrmnl_user_tmpl_set_optional (sputmpl, tmpl->optional); + xfrmnl_user_tmpl_set_aalgos (sputmpl, tmpl->aalgos); + xfrmnl_user_tmpl_set_ealgos (sputmpl, tmpl->ealgos); + xfrmnl_user_tmpl_set_calgos (sputmpl, tmpl->calgos); + xfrmnl_sp_add_usertemplate (sp, _nl_steal_pointer(&sputmpl)); + + sp->ce_mask |= XFRM_SP_ATTR_TMPL; + } + } + + if (tb[XFRMA_MARK]) { + struct xfrm_mark* m = nla_data(tb[XFRMA_MARK]); + sp->mark.m = m->m; + sp->mark.v = m->v; + sp->ce_mask |= XFRM_SP_ATTR_MARK; + } + + *result = _nl_steal_pointer(&sp); + return 0; +} + +static int xfrm_sp_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *n, struct nl_parser_param *pp) +{ + struct xfrmnl_sp* sp; + int err; + + if ((err = xfrmnl_sp_parse(n, &sp)) < 0) + { + printf ("received error: %d \n", err); + return err; + } + + err = pp->pp_cb((struct nl_object *) sp, pp); + + xfrmnl_sp_put(sp); + return err; +} + +/** + * @name XFRM SP Get + * @{ + */ + +int xfrmnl_sp_build_get_request(unsigned int index, unsigned int dir, unsigned int mark_v, unsigned int mark_m, struct nl_msg **result) +{ + struct nl_msg *msg; + struct xfrm_userpolicy_id spid; + struct xfrm_mark mark; + + memset(&spid, 0, sizeof(spid)); + spid.index = index; + spid.dir = dir; + + if (!(msg = nlmsg_alloc_simple(XFRM_MSG_GETPOLICY, 0))) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &spid, sizeof(spid), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if ((mark_m & mark_v) != 0) + { + memset(&mark, 0, sizeof(struct xfrm_mark)); + mark.m = mark_m; + mark.v = mark_v; + + NLA_PUT (msg, XFRMA_MARK, sizeof (struct xfrm_mark), &mark); + } + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +int xfrmnl_sp_get_kernel(struct nl_sock* sock, unsigned int index, unsigned int dir, unsigned int mark_v, unsigned int mark_m, struct xfrmnl_sp** result) +{ + struct nl_msg *msg = NULL; + struct nl_object *obj; + int err; + + if ((err = xfrmnl_sp_build_get_request(index, dir, mark_m, mark_v, &msg)) < 0) + return err; + + err = nl_send_auto(sock, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + if ((err = nl_pickup(sock, &xfrm_sp_msg_parser, &obj)) < 0) + return err; + + /* We have used xfrm_sp_msg_parser(), object is definitely a xfrm ae */ + *result = (struct xfrmnl_sp *) obj; + + /* If an object has been returned, we also need to wait for the ACK */ + if (err == 0 && obj) + nl_wait_for_ack(sock); + + return 0; +} + +/** @} */ + +static int build_xfrm_sp_message(struct xfrmnl_sp *tmpl, int cmd, int flags, struct nl_msg **result) +{ + struct nl_msg* msg; + struct xfrm_userpolicy_info sp_info; + uint32_t len; + struct nl_addr* addr; + + if (!(tmpl->ce_mask & XFRM_SP_ATTR_DIR) || + (!(tmpl->ce_mask & XFRM_SP_ATTR_INDEX) && + !(tmpl->ce_mask & XFRM_SP_ATTR_SEL))) + return -NLE_MISSING_ATTR; + + memset ((void*)&sp_info, 0, sizeof (sp_info)); + if (tmpl->ce_mask & XFRM_SP_ATTR_SEL) + { + addr = xfrmnl_sel_get_daddr (tmpl->sel); + memcpy ((void*)&sp_info.sel.daddr, (void*)nl_addr_get_binary_addr (addr), sizeof (uint8_t) * nl_addr_get_len (addr)); + addr = xfrmnl_sel_get_saddr (tmpl->sel); + memcpy ((void*)&sp_info.sel.saddr, (void*)nl_addr_get_binary_addr (addr), sizeof (uint8_t) * nl_addr_get_len (addr)); + sp_info.sel.dport = htons (xfrmnl_sel_get_dport (tmpl->sel)); + sp_info.sel.dport_mask = htons (xfrmnl_sel_get_dportmask (tmpl->sel)); + sp_info.sel.sport = htons (xfrmnl_sel_get_sport (tmpl->sel)); + sp_info.sel.sport_mask = htons (xfrmnl_sel_get_sportmask (tmpl->sel)); + sp_info.sel.family = xfrmnl_sel_get_family (tmpl->sel); + sp_info.sel.prefixlen_d = xfrmnl_sel_get_prefixlen_d (tmpl->sel); + sp_info.sel.prefixlen_s = xfrmnl_sel_get_prefixlen_s (tmpl->sel); + sp_info.sel.proto = xfrmnl_sel_get_proto (tmpl->sel); + sp_info.sel.ifindex = xfrmnl_sel_get_ifindex (tmpl->sel); + sp_info.sel.user = xfrmnl_sel_get_userid (tmpl->sel); + } + + if (tmpl->ce_mask & XFRM_SP_ATTR_LTIME_CFG) + { + sp_info.lft.soft_byte_limit = xfrmnl_ltime_cfg_get_soft_bytelimit (tmpl->lft); + sp_info.lft.hard_byte_limit = xfrmnl_ltime_cfg_get_hard_bytelimit (tmpl->lft); + sp_info.lft.soft_packet_limit = xfrmnl_ltime_cfg_get_soft_packetlimit (tmpl->lft); + sp_info.lft.hard_packet_limit = xfrmnl_ltime_cfg_get_hard_packetlimit (tmpl->lft); + sp_info.lft.soft_add_expires_seconds = xfrmnl_ltime_cfg_get_soft_addexpires (tmpl->lft); + sp_info.lft.hard_add_expires_seconds = xfrmnl_ltime_cfg_get_hard_addexpires (tmpl->lft); + sp_info.lft.soft_use_expires_seconds = xfrmnl_ltime_cfg_get_soft_useexpires (tmpl->lft); + sp_info.lft.hard_use_expires_seconds = xfrmnl_ltime_cfg_get_hard_useexpires (tmpl->lft); + } + + //Skip current lifetime: cur lifetime can be updated only via AE + + if (tmpl->ce_mask & XFRM_SP_ATTR_PRIO) + sp_info.priority = tmpl->priority; + + if (tmpl->ce_mask & XFRM_SP_ATTR_INDEX) + sp_info.index = tmpl->index; + + if (tmpl->ce_mask & XFRM_SP_ATTR_DIR) + sp_info.dir = tmpl->dir; + + if (tmpl->ce_mask & XFRM_SP_ATTR_ACTION) + sp_info.action = tmpl->action; + + if (tmpl->ce_mask & XFRM_SP_ATTR_FLAGS) + sp_info.flags = tmpl->flags; + + if (tmpl->ce_mask & XFRM_SP_ATTR_SHARE) + sp_info.share = tmpl->share; + + msg = nlmsg_alloc_simple(cmd, flags); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &sp_info, sizeof(sp_info), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (tmpl->ce_mask & XFRM_SP_ATTR_SECCTX) { + len = (sizeof (struct xfrm_user_sec_ctx)) + tmpl->sec_ctx->ctx_len; + NLA_PUT (msg, XFRMA_SEC_CTX, len, tmpl->sec_ctx); + } + + if (tmpl->ce_mask & XFRM_SP_ATTR_POLTYPE) { + len = sizeof (struct xfrm_userpolicy_type); + NLA_PUT (msg, XFRMA_POLICY_TYPE, len, &tmpl->uptype); + } + + if (tmpl->ce_mask & XFRM_SP_ATTR_TMPL) { + struct nlattr* tmpls; + struct xfrmnl_user_tmpl* utmpl; + struct nl_addr* addr; + + if (!(tmpls = nla_nest_start(msg, XFRMA_TMPL))) + goto nla_put_failure; + + nl_list_for_each_entry(utmpl, &tmpl->usertmpl_list, utmpl_list) { + struct xfrm_user_tmpl* tmpl; + + tmpl = nlmsg_reserve(msg, sizeof(*tmpl), NLMSG_ALIGNTO); + if (!tmpl) + goto nla_put_failure; + addr = xfrmnl_user_tmpl_get_daddr (utmpl); + memcpy ((void *)&tmpl->id.daddr, nl_addr_get_binary_addr (addr), + nl_addr_get_len (addr)); + tmpl->id.spi = htonl(xfrmnl_user_tmpl_get_spi (utmpl)); + tmpl->id.proto = xfrmnl_user_tmpl_get_proto (utmpl); + tmpl->family = xfrmnl_user_tmpl_get_family (utmpl); + addr = xfrmnl_user_tmpl_get_saddr (utmpl); + memcpy ((void *)&tmpl->saddr, nl_addr_get_binary_addr (addr), + nl_addr_get_len (addr)); + tmpl->reqid = xfrmnl_user_tmpl_get_reqid (utmpl); + tmpl->mode = xfrmnl_user_tmpl_get_mode (utmpl); + tmpl->share = xfrmnl_user_tmpl_get_share (utmpl); + tmpl->optional = xfrmnl_user_tmpl_get_optional (utmpl); + tmpl->aalgos = xfrmnl_user_tmpl_get_aalgos (utmpl); + tmpl->ealgos = xfrmnl_user_tmpl_get_ealgos (utmpl); + tmpl->calgos = xfrmnl_user_tmpl_get_calgos (utmpl); + } + nla_nest_end(msg, tmpls); + } + + if (tmpl->ce_mask & XFRM_SP_ATTR_MARK) { + NLA_PUT (msg, XFRMA_MARK, sizeof (struct xfrm_mark), &tmpl->mark); + } + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +/** + * @name XFRM SP Add + * @{ + */ + +int xfrmnl_sp_build_add_request(struct xfrmnl_sp* tmpl, int flags, struct nl_msg **result) +{ + return build_xfrm_sp_message (tmpl, XFRM_MSG_NEWPOLICY, flags, result); +} + +int xfrmnl_sp_add(struct nl_sock* sk, struct xfrmnl_sp* tmpl, int flags) +{ + int err; + struct nl_msg *msg; + + if ((err = xfrmnl_sp_build_add_request(tmpl, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return nl_wait_for_ack(sk); +} + +/** + * @name XFRM SP Update + * @{ + */ + +int xfrmnl_sp_build_update_request(struct xfrmnl_sp* tmpl, int flags, struct nl_msg **result) +{ + return build_xfrm_sp_message (tmpl, XFRM_MSG_UPDPOLICY, flags, result); +} + +int xfrmnl_sp_update(struct nl_sock* sk, struct xfrmnl_sp* tmpl, int flags) +{ + int err; + struct nl_msg *msg; + + if ((err = xfrmnl_sp_build_update_request(tmpl, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return nl_wait_for_ack(sk); +} + +/** @} */ + +/** + * \brief Builds a xfrm_sp_delete_message. Uses either index and direction + * or security-context (not set is a valid value), selector and + * direction for identification. + * Returns error if necessary values aren't set. + * + * \param tmpl The policy template. + * \param cmd The command. Should be XFRM_MSG_DELPOLICY. + * \param flags Additional flags + * \param result Resulting message. + * + * \return 0 if successful, else error value < 0 + */ +static int build_xfrm_sp_delete_message(struct xfrmnl_sp *tmpl, int cmd, int flags, struct nl_msg **result) +{ + struct nl_msg* msg; + struct xfrm_userpolicy_id spid; + struct nl_addr* addr; + uint32_t len; + + if (!(tmpl->ce_mask & XFRM_SP_ATTR_DIR) || + (!(tmpl->ce_mask & XFRM_SP_ATTR_INDEX) && + !(tmpl->ce_mask & XFRM_SP_ATTR_SEL))) + return -NLE_MISSING_ATTR; + + memset(&spid, 0, sizeof(spid)); + spid.dir = tmpl->dir; + if(tmpl->ce_mask & XFRM_SP_ATTR_INDEX) + spid.index = tmpl->index; + + if (tmpl->ce_mask & XFRM_SP_ATTR_SEL) + { + addr = xfrmnl_sel_get_daddr (tmpl->sel); + memcpy ((void*)&spid.sel.daddr, (void*)nl_addr_get_binary_addr (addr), sizeof (uint8_t) * nl_addr_get_len (addr)); + addr = xfrmnl_sel_get_saddr (tmpl->sel); + memcpy ((void*)&spid.sel.saddr, (void*)nl_addr_get_binary_addr (addr), sizeof (uint8_t) * nl_addr_get_len (addr)); + spid.sel.dport = htons (xfrmnl_sel_get_dport (tmpl->sel)); + spid.sel.dport_mask = htons (xfrmnl_sel_get_dportmask (tmpl->sel)); + spid.sel.sport = htons (xfrmnl_sel_get_sport (tmpl->sel)); + spid.sel.sport_mask = htons (xfrmnl_sel_get_sportmask (tmpl->sel)); + spid.sel.family = xfrmnl_sel_get_family (tmpl->sel); + spid.sel.prefixlen_d = xfrmnl_sel_get_prefixlen_d (tmpl->sel); + spid.sel.prefixlen_s = xfrmnl_sel_get_prefixlen_s (tmpl->sel); + spid.sel.proto = xfrmnl_sel_get_proto (tmpl->sel); + spid.sel.ifindex = xfrmnl_sel_get_ifindex (tmpl->sel); + spid.sel.user = xfrmnl_sel_get_userid (tmpl->sel); + } + + msg = nlmsg_alloc_simple(cmd, flags); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &spid, sizeof(spid), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (tmpl->ce_mask & XFRM_SP_ATTR_SECCTX) { + len = (sizeof (struct xfrm_user_sec_ctx)) + tmpl->sec_ctx->ctx_len; + NLA_PUT (msg, XFRMA_SEC_CTX, len, tmpl->sec_ctx); + } + + if (tmpl->ce_mask & XFRM_SP_ATTR_MARK) { + len = sizeof (struct xfrm_mark); + NLA_PUT (msg, XFRMA_MARK, len, &tmpl->mark); + } + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +/** + * @name XFRM SA Delete + * @{ + */ + +int xfrmnl_sp_build_delete_request(struct xfrmnl_sp* tmpl, int flags, struct nl_msg **result) +{ + return build_xfrm_sp_delete_message (tmpl, XFRM_MSG_DELPOLICY, flags, result); +} + +int xfrmnl_sp_delete(struct nl_sock* sk, struct xfrmnl_sp* tmpl, int flags) +{ + int err; + struct nl_msg *msg; + + if ((err = xfrmnl_sp_build_delete_request(tmpl, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return nl_wait_for_ack(sk); +} + +/** @} */ + + +/** + * @name Attributes + * @{ + */ + +struct xfrmnl_sel* xfrmnl_sp_get_sel (struct xfrmnl_sp* sp) +{ + if (sp->ce_mask & XFRM_SP_ATTR_SEL) + return sp->sel; + else + return NULL; +} + +int xfrmnl_sp_set_sel (struct xfrmnl_sp* sp, struct xfrmnl_sel* sel) +{ + /* Release any previously held selector object from the SP */ + if (sp->sel) + xfrmnl_sel_put (sp->sel); + + /* Increment ref count on new selector and save it in the SP */ + xfrmnl_sel_get (sel); + sp->sel = sel; + sp->ce_mask |= XFRM_SP_ATTR_SEL; + + return 0; +} + +struct xfrmnl_ltime_cfg* xfrmnl_sp_get_lifetime_cfg (struct xfrmnl_sp* sp) +{ + if (sp->ce_mask & XFRM_SP_ATTR_LTIME_CFG) + return sp->lft; + else + return NULL; +} + +int xfrmnl_sp_set_lifetime_cfg (struct xfrmnl_sp* sp, struct xfrmnl_ltime_cfg* ltime) +{ + /* Release any previously held lifetime cfg object from the SP */ + if (sp->lft) + xfrmnl_ltime_cfg_put (sp->lft); + + /* Increment ref count on new lifetime object and save it in the SP */ + xfrmnl_ltime_cfg_get (ltime); + sp->lft = ltime; + sp->ce_mask |= XFRM_SP_ATTR_LTIME_CFG; + + return 0; +} + +int xfrmnl_sp_get_curlifetime (struct xfrmnl_sp* sp, unsigned long long int* curr_bytes, + unsigned long long int* curr_packets, unsigned long long int* curr_add_time, unsigned long long int* curr_use_time) +{ + if (sp == NULL || curr_bytes == NULL || curr_packets == NULL || curr_add_time == NULL || curr_use_time == NULL) + return -1; + + *curr_bytes = sp->curlft.bytes; + *curr_packets = sp->curlft.packets; + *curr_add_time = sp->curlft.add_time; + *curr_use_time = sp->curlft.use_time; + + return 0; +} + +int xfrmnl_sp_get_priority (struct xfrmnl_sp* sp) +{ + if (sp->ce_mask & XFRM_SP_ATTR_PRIO) + return sp->priority; + else + return -1; +} + +int xfrmnl_sp_set_priority (struct xfrmnl_sp* sp, unsigned int prio) +{ + sp->priority = prio; + sp->ce_mask |= XFRM_SP_ATTR_PRIO; + + return 0; +} + +int xfrmnl_sp_get_index (struct xfrmnl_sp* sp) +{ + if (sp->ce_mask & XFRM_SP_ATTR_INDEX) + return sp->index; + else + return -1; +} + +int xfrmnl_sp_set_index (struct xfrmnl_sp* sp, unsigned int index) +{ + sp->index = index; + sp->ce_mask |= XFRM_SP_ATTR_INDEX; + + return 0; +} + +int xfrmnl_sp_get_dir (struct xfrmnl_sp* sp) +{ + if (sp->ce_mask & XFRM_SP_ATTR_DIR) + return sp->dir; + else + return -1; +} + +int xfrmnl_sp_set_dir (struct xfrmnl_sp* sp, unsigned int dir) +{ + sp->dir = dir; + sp->ce_mask |= XFRM_SP_ATTR_DIR; + + return 0; +} + +int xfrmnl_sp_get_action (struct xfrmnl_sp* sp) +{ + if (sp->ce_mask & XFRM_SP_ATTR_ACTION) + return sp->action; + else + return -1; +} + +int xfrmnl_sp_set_action (struct xfrmnl_sp* sp, unsigned int action) +{ + sp->action = action; + sp->ce_mask |= XFRM_SP_ATTR_ACTION; + + return 0; +} + +int xfrmnl_sp_get_flags (struct xfrmnl_sp* sp) +{ + if (sp->ce_mask & XFRM_SP_ATTR_FLAGS) + return sp->flags; + else + return -1; +} + +int xfrmnl_sp_set_flags (struct xfrmnl_sp* sp, unsigned int flags) +{ + sp->flags = flags; + sp->ce_mask |= XFRM_SP_ATTR_FLAGS; + + return 0; +} + +int xfrmnl_sp_get_share (struct xfrmnl_sp* sp) +{ + if (sp->ce_mask & XFRM_SP_ATTR_SHARE) + return sp->share; + else + return -1; +} + +int xfrmnl_sp_set_share (struct xfrmnl_sp* sp, unsigned int share) +{ + sp->share = share; + sp->ce_mask |= XFRM_SP_ATTR_SHARE; + + return 0; +} + +/** + * Get the security context. + * + * @arg sp The xfrmnl_sp object. + * @arg len An optional output value for the ctx_str length including the xfrmnl_sp header. + * @arg exttype An optional output value. + * @arg alg An optional output value for the security context algorithm. + * @arg doi An optional output value for the security context domain of interpretation. + * @arg ctx_len An optional output value for the security context length, including the + * terminating null byte ('\0'). + * @arg ctx_str An optional buffer large enough for the security context string. It must + * contain at least @ctx_len bytes. You are advised to create the ctx_str + * buffer one element larger and ensure NUL termination yourself. + * + * Warning: you must ensure that @ctx_str is large enough. If you don't know the length before-hand, + * call xfrmnl_sp_get_sec_ctx() without @ctx_str argument to query only the required buffer size. + * This modified API is available in all versions of libnl3 that support the capability + * @def NL_CAPABILITY_XFRM_SP_SEC_CTX_LEN (@see nl_has_capability for further information). + * + * @return 0 on success or a negative error code. + */ +int xfrmnl_sp_get_sec_ctx (struct xfrmnl_sp* sp, unsigned int* len, unsigned int* exttype, unsigned int* alg, unsigned int* doi, unsigned int* ctx_len, char* ctx_str) +{ + if (sp->ce_mask & XFRM_SP_ATTR_SECCTX) + { + if (len) + *len = sizeof (struct xfrmnl_user_sec_ctx) + sp->sec_ctx->ctx_len; + if (exttype) + *exttype = sp->sec_ctx->exttype; + if (alg) + *alg = sp->sec_ctx->ctx_alg; + if (doi) + *doi = sp->sec_ctx->ctx_doi; + if (ctx_len) + *ctx_len = sp->sec_ctx->ctx_len; + if (ctx_str) + memcpy ((void *)ctx_str, (void *)sp->sec_ctx->ctx, sp->sec_ctx->ctx_len); + } + else + return -1; + + return 0; +} +/** + * @brief Set security context (ctx_str) for XFRM Polixy. + * + * @param sp XFRM Policy + * @param len !!! depricated unused parameter !!! + * @param exttype netlink message attribute - probably XFRMA_SEC_CTX + * @param alg security context algorithm + * @param doi security context domain interpretation + * @param ctx_len Length of the context string. + * @param ctx_str The context string. + * + * @return 0 if sucessfull, else -1 + */ +int xfrmnl_sp_set_sec_ctx (struct xfrmnl_sp* sp, unsigned int len, unsigned int exttype, unsigned int alg, unsigned int doi, unsigned int ctx_len, char* ctx_str) +{ + /* Free up the old context string and allocate new one */ + if (sp->sec_ctx) + free (sp->sec_ctx); + if ((sp->sec_ctx = calloc (1, sizeof (struct xfrmnl_user_sec_ctx) + 1 + ctx_len)) == NULL) + return -1; + + /* Save the new info */ + sp->sec_ctx->len = sizeof (struct xfrmnl_user_sec_ctx) + ctx_len; + sp->sec_ctx->exttype = exttype; + sp->sec_ctx->ctx_alg = alg; + sp->sec_ctx->ctx_doi = doi; + sp->sec_ctx->ctx_len = ctx_len; + memcpy ((void *)sp->sec_ctx->ctx, (void *)ctx_str, ctx_len); + sp->sec_ctx->ctx[ctx_len] = '\0'; + + sp->ce_mask |= XFRM_SP_ATTR_SECCTX; + + return 0; +} + +int xfrmnl_sp_get_userpolicy_type (struct xfrmnl_sp* sp) +{ + if (sp->ce_mask & XFRM_SP_ATTR_POLTYPE) + return sp->uptype.type; + else + return -1; +} + +int xfrmnl_sp_set_userpolicy_type (struct xfrmnl_sp* sp, unsigned int type) +{ + sp->uptype.type = type; + sp->ce_mask |= XFRM_SP_ATTR_POLTYPE; + + return 0; +} + +void xfrmnl_sp_add_usertemplate(struct xfrmnl_sp *sp, struct xfrmnl_user_tmpl *utmpl) +{ + nl_list_add_tail(&utmpl->utmpl_list, &sp->usertmpl_list); + sp->nr_user_tmpl++; + sp->ce_mask |= XFRM_SP_ATTR_TMPL; +} + +void xfrmnl_sp_remove_usertemplate(struct xfrmnl_sp *sp, struct xfrmnl_user_tmpl *utmpl) +{ + if (sp->ce_mask & XFRM_SP_ATTR_TMPL) { + sp->nr_user_tmpl--; + nl_list_del(&utmpl->utmpl_list); + if (sp->nr_user_tmpl == 0) + sp->ce_mask &= ~XFRM_SP_ATTR_TMPL; + } +} + +struct nl_list_head *xfrmnl_sp_get_usertemplates(struct xfrmnl_sp *sp) +{ + if (sp->ce_mask & XFRM_SP_ATTR_TMPL) + return &sp->usertmpl_list; + + return NULL; +} + +int xfrmnl_sp_get_nusertemplates(struct xfrmnl_sp *sp) +{ + if (sp->ce_mask & XFRM_SP_ATTR_TMPL) + return sp->nr_user_tmpl; + + return 0; +} + +void xfrmnl_sp_foreach_usertemplate(struct xfrmnl_sp *r, + void (*cb)(struct xfrmnl_user_tmpl *, void *), + void *arg) +{ + struct xfrmnl_user_tmpl *utmpl; + + if (r->ce_mask & XFRM_SP_ATTR_TMPL) { + nl_list_for_each_entry(utmpl, &r->usertmpl_list, utmpl_list) { + cb(utmpl, arg); + } + } +} + +struct xfrmnl_user_tmpl *xfrmnl_sp_usertemplate_n(struct xfrmnl_sp *r, int n) +{ + struct xfrmnl_user_tmpl *utmpl; + + if (r->ce_mask & XFRM_SP_ATTR_TMPL && n >= 0 && + ((unsigned)n) < r->nr_user_tmpl) { + uint32_t i; + + i = 0; + nl_list_for_each_entry(utmpl, &r->usertmpl_list, utmpl_list) { + if (i == ((unsigned)n)) + return utmpl; + i++; + } + } + return NULL; +} + +int xfrmnl_sp_get_mark (struct xfrmnl_sp* sp, unsigned int* mark_mask, unsigned int* mark_value) +{ + if (mark_mask == NULL || mark_value == NULL) + return -1; + + if (sp->ce_mask & XFRM_SP_ATTR_MARK) + { + *mark_mask = sp->mark.m; + *mark_value = sp->mark.v; + + return 0; + } + else + return -1; +} + +int xfrmnl_sp_set_mark (struct xfrmnl_sp* sp, unsigned int value, unsigned int mask) +{ + sp->mark.v = value; + sp->mark.m = mask; + sp->ce_mask |= XFRM_SP_ATTR_MARK; + + return 0; +} + +/** @} */ + +static struct nl_object_ops xfrm_sp_obj_ops = { + .oo_name = "xfrm/sp", + .oo_size = sizeof(struct xfrmnl_sp), + .oo_constructor = xfrm_sp_alloc_data, + .oo_free_data = xfrm_sp_free_data, + .oo_clone = xfrm_sp_clone, + .oo_dump = { + [NL_DUMP_LINE] = xfrm_sp_dump_line, + [NL_DUMP_DETAILS] = xfrm_sp_dump_details, + [NL_DUMP_STATS] = xfrm_sp_dump_stats, + }, + .oo_compare = xfrm_sp_compare, + .oo_attrs2str = xfrm_sp_attrs2str, + .oo_id_attrs = (XFRM_SP_ATTR_SEL | XFRM_SP_ATTR_INDEX | XFRM_SP_ATTR_DIR), +}; + +static struct nl_af_group xfrm_sp_groups[] = { + { AF_UNSPEC, XFRMNLGRP_POLICY }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops xfrmnl_sp_ops = { + .co_name = "xfrm/sp", + .co_hdrsize = sizeof(struct xfrm_userpolicy_info), + .co_msgtypes = { + { XFRM_MSG_NEWPOLICY, NL_ACT_NEW, "new" }, + { XFRM_MSG_DELPOLICY, NL_ACT_DEL, "del" }, + { XFRM_MSG_GETPOLICY, NL_ACT_GET, "get" }, + { XFRM_MSG_UPDPOLICY, NL_ACT_NEW, "update" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_XFRM, + .co_groups = xfrm_sp_groups, + .co_request_update = xfrm_sp_request_update, + .co_msg_parser = xfrm_sp_msg_parser, + .co_obj_ops = &xfrm_sp_obj_ops, +}; + +/** + * @name XFRM SA Cache Managament + * @{ + */ + +static void _nl_init xfrm_sp_init(void) +{ + nl_cache_mngt_register(&xfrmnl_sp_ops); +} + +static void _nl_exit xfrm_sp_exit(void) +{ + nl_cache_mngt_unregister(&xfrmnl_sp_ops); +} + +/** @} */ diff --git a/libnl/lib/xfrm/template.c b/libnl/lib/xfrm/template.c new file mode 100644 index 0000000..2a42b28 --- /dev/null +++ b/libnl/lib/xfrm/template.c @@ -0,0 +1,345 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * Neither the name of Texas Instruments Incorporated 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 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. + * + */ +/** + * @ingroup xfrmnl + * @defgroup XFRM User Template Object + * + * Abstract data type representing XFRM SA properties + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#include "nl-default.h" + +#include + +#include "nl-xfrm.h" +#include "nl-priv-dynamic-core/nl-core.h" + +void xfrmnl_user_tmpl_free(struct xfrmnl_user_tmpl* utmpl) +{ + if (!utmpl) + return; + + nl_addr_put (utmpl->id.daddr); + nl_addr_put (utmpl->saddr); + free(utmpl); +} + +/** + * @name Creating User Template Object + * @{ + */ + +/** + * Allocate new user template object. + * @return Newly allocated user template object or NULL + */ +struct xfrmnl_user_tmpl* xfrmnl_user_tmpl_alloc() +{ + struct xfrmnl_user_tmpl* utmpl; + + utmpl = calloc(1, sizeof(struct xfrmnl_user_tmpl)); + if (!utmpl) + return NULL; + + nl_init_list_head(&utmpl->utmpl_list); + + return utmpl; +} + +/** + * Clone existing user template object. + * @arg utmpl Selector object. + * @return Newly allocated user template object being a duplicate of the + * specified user template object or NULL if a failure occured. + */ +struct xfrmnl_user_tmpl* xfrmnl_user_tmpl_clone(struct xfrmnl_user_tmpl* utmpl) +{ + struct xfrmnl_user_tmpl* new; + + new = xfrmnl_user_tmpl_alloc(); + if (!new) + return NULL; + + memcpy(new, utmpl, sizeof(struct xfrmnl_user_tmpl)); + new->id.daddr = nl_addr_clone (utmpl->id.daddr); + new->saddr = nl_addr_clone (utmpl->saddr); + + return new; +} + +/** @} */ + +/** + * @name XFRM Template Mode Translations + * @{ + */ +static const struct trans_tbl tmpl_modes[] = { + __ADD(XFRM_MODE_TRANSPORT, transport), + __ADD(XFRM_MODE_TUNNEL, tunnel), + __ADD(XFRM_MODE_ROUTEOPTIMIZATION, route optimization), + __ADD(XFRM_MODE_IN_TRIGGER, in trigger), + __ADD(XFRM_MODE_BEET, beet), +}; + +char* xfrmnl_user_tmpl_mode2str(int mode, char *buf, size_t len) +{ + return __type2str (mode, buf, len, tmpl_modes, ARRAY_SIZE(tmpl_modes)); +} + +int xfrmnl_user_tmpl_str2mode(const char *name) +{ + return __str2type (name, tmpl_modes, ARRAY_SIZE(tmpl_modes)); +} +/** @} */ + +/** + * @name Miscellaneous + * @{ + */ + +/** + * Compares two user template objects. + * @arg a A user template object. + * @arg b Another user template object. + * + * @return Non zero if difference is found, 0 otherwise if both + * the objects are identical. + */ +int xfrmnl_user_tmpl_cmp(struct xfrmnl_user_tmpl* a, struct xfrmnl_user_tmpl* b) +{ + /* Check for any differences */ + if ((nl_addr_cmp_prefix (a->id.daddr, b->id.daddr) != 0) || + (a->id.spi != b->id.spi) || + (a->id.proto && (a->id.proto != b->id.proto)) || + (nl_addr_cmp_prefix (a->saddr, b->saddr) != 0) || + (a->family != b->family) || + (a->reqid != b->reqid) || + (a->mode != b->mode) || + (a->share != b->share) || + (a->aalgos != b->aalgos) || + (a->ealgos != b->ealgos) || + (a->calgos != b->calgos)) + return 1; + + /* The objects are identical */ + return 0; +} + +void xfrmnl_user_tmpl_dump(struct xfrmnl_user_tmpl* tmpl, struct nl_dump_params *p) +{ + char dst[INET6_ADDRSTRLEN+5], src[INET6_ADDRSTRLEN+5]; + char buf [128]; + + nl_dump_line(p, "\t\tsrc %s dst %s family: %s \n", + nl_addr2str(tmpl->saddr, src, sizeof(src)), + nl_addr2str (tmpl->id.daddr, dst, sizeof (dst)), + nl_af2str (tmpl->family, buf, 128)); + nl_dump_line (p, "\t\tprotocol: %s spi: 0x%x reqid: %u mode: %s\n", + nl_ip_proto2str (tmpl->id.proto, buf, sizeof(buf)), + tmpl->id.spi, tmpl->reqid, + xfrmnl_user_tmpl_mode2str (tmpl->mode, buf, 128)); + nl_dump_line (p, "\t\tAuth Algo: 0x%x Crypto Algo: 0x%x Compr Algo: 0x%x\n", + tmpl->aalgos, tmpl->ealgos, tmpl->calgos); + + return; +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ +struct nl_addr* xfrmnl_user_tmpl_get_daddr (struct xfrmnl_user_tmpl* utmpl) +{ + return utmpl->id.daddr; +} + +int xfrmnl_user_tmpl_set_daddr (struct xfrmnl_user_tmpl* utmpl, struct nl_addr* addr) +{ + /* Increment reference counter on this to keep this address + * object around while user template in use */ + nl_addr_get(addr); + + utmpl->id.daddr = addr; + + return 0; +} + +int xfrmnl_user_tmpl_get_spi (struct xfrmnl_user_tmpl* utmpl) +{ + return utmpl->id.spi; +} + +int xfrmnl_user_tmpl_set_spi (struct xfrmnl_user_tmpl* utmpl, unsigned int spi) +{ + utmpl->id.spi = spi; + + return 0; +} + +int xfrmnl_user_tmpl_get_proto (struct xfrmnl_user_tmpl* utmpl) +{ + return utmpl->id.proto; +} + +int xfrmnl_user_tmpl_set_proto (struct xfrmnl_user_tmpl* utmpl, unsigned int protocol) +{ + utmpl->id.proto = protocol; + + return 0; +} + +int xfrmnl_user_tmpl_get_family(struct xfrmnl_user_tmpl *utmpl) +{ + return utmpl->family; +} + +int xfrmnl_user_tmpl_set_family(struct xfrmnl_user_tmpl *utmpl, unsigned int family) +{ + utmpl->family = family; + + return 0; +} + +struct nl_addr* xfrmnl_user_tmpl_get_saddr (struct xfrmnl_user_tmpl* utmpl) +{ + return utmpl->saddr; +} + +int xfrmnl_user_tmpl_set_saddr (struct xfrmnl_user_tmpl* utmpl, struct nl_addr* addr) +{ + /* Increment reference counter on this to keep this address + * object around while user template in use */ + nl_addr_get(addr); + + utmpl->saddr = addr; + + return 0; +} + +int xfrmnl_user_tmpl_get_reqid (struct xfrmnl_user_tmpl* utmpl) +{ + return utmpl->reqid; +} + +int xfrmnl_user_tmpl_set_reqid (struct xfrmnl_user_tmpl* utmpl, unsigned int reqid) +{ + utmpl->reqid = reqid; + + return 0; +} + +int xfrmnl_user_tmpl_get_mode (struct xfrmnl_user_tmpl* utmpl) +{ + return utmpl->mode; +} + +int xfrmnl_user_tmpl_set_mode (struct xfrmnl_user_tmpl* utmpl, unsigned int mode) +{ + utmpl->mode = mode; + + return 0; +} + +int xfrmnl_user_tmpl_get_share (struct xfrmnl_user_tmpl* utmpl) +{ + return utmpl->share; +} + +int xfrmnl_user_tmpl_set_share (struct xfrmnl_user_tmpl* utmpl, unsigned int share) +{ + utmpl->share = share; + + return 0; +} + +int xfrmnl_user_tmpl_get_optional (struct xfrmnl_user_tmpl* utmpl) +{ + return utmpl->optional; +} + +int xfrmnl_user_tmpl_set_optional (struct xfrmnl_user_tmpl* utmpl, unsigned int optional) +{ + utmpl->optional = optional; + + return 0; +} + +int xfrmnl_user_tmpl_get_aalgos (struct xfrmnl_user_tmpl* utmpl) +{ + return utmpl->aalgos; +} + +int xfrmnl_user_tmpl_set_aalgos (struct xfrmnl_user_tmpl* utmpl, unsigned int aalgos) +{ + utmpl->aalgos = aalgos; + + return 0; +} + +int xfrmnl_user_tmpl_get_ealgos (struct xfrmnl_user_tmpl* utmpl) +{ + return utmpl->ealgos; +} + +int xfrmnl_user_tmpl_set_ealgos (struct xfrmnl_user_tmpl* utmpl, unsigned int ealgos) +{ + utmpl->ealgos = ealgos; + + return 0; +} + +int xfrmnl_user_tmpl_get_calgos (struct xfrmnl_user_tmpl* utmpl) +{ + return utmpl->calgos; +} + +int xfrmnl_user_tmpl_set_calgos (struct xfrmnl_user_tmpl* utmpl, unsigned int calgos) +{ + utmpl->calgos = calgos; + + return 0; +} + +/** @} */ diff --git a/libnl/libnl-3.0.pc.in b/libnl/libnl-3.0.pc.in new file mode 100644 index 0000000..ddbc999 --- /dev/null +++ b/libnl/libnl-3.0.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libnl +Description: Convenience library for netlink sockets +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lnl-@MAJ_VERSION@ +Libs.private: @LIBS@ +Cflags: -I${includedir}/libnl@MAJ_VERSION@ diff --git a/libnl/libnl-3.sym b/libnl/libnl-3.sym new file mode 100644 index 0000000..b5a33bb --- /dev/null +++ b/libnl/libnl-3.sym @@ -0,0 +1,387 @@ +# The linker version script controls which symbols are exported. +# Version tags that correspond to past releases group the symbols +# that were part of that release. Hence, such a version tag is sealed +# and MUST not be extended. +# Any new symbols MUST only be added to the bottom of the file, in +# the version tag for the next upcoming release. +libnl_3 { +global: + # these functions are in private header files and should have never + # been exported. We might hide them later. + nl_cache_parse; + + # these functions are in private header files and should have never + # been exported. They are used by libnl internals + __flags2str; + __list_str2type; + __list_type2str; + __nl_read_num_str_file; + __str2flags; + __str2type; + __trans_list_add; + __trans_list_clear; + __type2str; + + # internal symbols that are in public headers + __nl_cache_mngt_require; + + # variables + nl_debug; + nl_debug_dp; + + nl_addr2str; + nl_addr_alloc; + nl_addr_alloc_attr; + nl_addr_build; + nl_addr_clone; + nl_addr_cmp; + nl_addr_cmp_prefix; + nl_addr_fill_sockaddr; + nl_addr_get; + nl_addr_get_binary_addr; + nl_addr_get_family; + nl_addr_get_len; + nl_addr_get_prefixlen; + nl_addr_guess_family; + nl_addr_info; + nl_addr_iszero; + nl_addr_parse; + nl_addr_put; + nl_addr_resolve; + nl_addr_set_binary_addr; + nl_addr_set_family; + nl_addr_set_prefixlen; + nl_addr_shared; + nl_addr_valid; + nl_af2str; + nl_auto_complete; + nl_cache_add; + nl_cache_alloc; + nl_cache_alloc_and_fill; + nl_cache_alloc_name; + nl_cache_clear; + nl_cache_clone; + nl_cache_dump; + nl_cache_dump_filter; + nl_cache_find; + nl_cache_foreach; + nl_cache_foreach_filter; + nl_cache_free; + nl_cache_get; + nl_cache_get_first; + nl_cache_get_last; + nl_cache_get_next; + nl_cache_get_ops; + nl_cache_get_prev; + nl_cache_include; + nl_cache_is_empty; + nl_cache_mark_all; + nl_cache_mngr_add; + nl_cache_mngr_add_cache; + nl_cache_mngr_alloc; + nl_cache_mngr_data_ready; + nl_cache_mngr_free; + nl_cache_mngr_get_fd; + nl_cache_mngr_info; + nl_cache_mngr_poll; + nl_cache_mngt_provide; + nl_cache_mngt_register; + nl_cache_mngt_require; + nl_cache_mngt_require_safe; + nl_cache_mngt_unprovide; + nl_cache_mngt_unregister; + nl_cache_move; + nl_cache_nitems; + nl_cache_nitems_filter; + nl_cache_ops_associate; + nl_cache_ops_associate_safe; + nl_cache_ops_foreach; + nl_cache_ops_get; + nl_cache_ops_lookup; + nl_cache_ops_lookup_safe; + nl_cache_ops_put; + nl_cache_ops_set_flags; + nl_cache_parse_and_add; + nl_cache_pickup; + nl_cache_put; + nl_cache_refill; + nl_cache_remove; + nl_cache_resync; + nl_cache_search; + nl_cache_set_arg1; + nl_cache_set_arg2; + nl_cache_set_flags; + nl_cache_subset; + nl_cancel_down_bits; + nl_cancel_down_bytes; + nl_cancel_down_us; + nl_cb_active_type; + nl_cb_alloc; + nl_cb_clone; + nl_cb_err; + nl_cb_get; + nl_cb_overwrite_recv; + nl_cb_overwrite_recvmsgs; + nl_cb_overwrite_send; + nl_cb_put; + nl_cb_set; + nl_cb_set_all; + nl_close; + nl_complete_msg; + nl_connect; + nl_data_alloc; + nl_data_alloc_attr; + nl_data_append; + nl_data_clone; + nl_data_cmp; + nl_data_free; + nl_data_get; + nl_data_get_size; + nl_dump; + nl_dump_line; + nl_ether_proto2str; + nl_get_psched_hz; + nl_get_user_hz; + nl_geterror; + nl_has_capability; + nl_hash; + nl_hash_any; + nl_hash_table_add; + nl_hash_table_alloc; + nl_hash_table_del; + nl_hash_table_free; + nl_hash_table_lookup; + nl_ip_proto2str; + nl_join_groups; + nl_llproto2str; + nl_msec2str; + nl_msg_dump; + nl_msg_parse; + nl_msgtype_lookup; + nl_new_line; + nl_nlfamily2str; + nl_nlmsg_flags2str; + nl_nlmsgtype2str; + nl_object_alloc; + nl_object_alloc_name; + nl_object_attr_list; + nl_object_attrs2str; + nl_object_clone; + nl_object_diff; + nl_object_dump; + nl_object_dump_buf; + nl_object_free; + nl_object_get; + nl_object_get_cache; + nl_object_get_id_attrs; + nl_object_get_msgtype; + nl_object_get_ops; + nl_object_get_refcnt; + nl_object_get_type; + nl_object_identical; + nl_object_is_marked; + nl_object_keygen; + nl_object_mark; + nl_object_match_filter; + nl_object_put; + nl_object_shared; + nl_object_unmark; + nl_object_update; + nl_perror; + nl_pickup; + nl_prob2int; + nl_rate2str; + nl_recv; + nl_recvmsgs; + nl_recvmsgs_default; + nl_recvmsgs_report; + nl_send; + nl_send_auto; + nl_send_auto_complete; + nl_send_iovec; + nl_send_simple; + nl_send_sync; + nl_sendmsg; + nl_sendto; + nl_size2int; + nl_size2str; + nl_socket_add_membership; + nl_socket_add_memberships; + nl_socket_alloc; + nl_socket_alloc_cb; + nl_socket_disable_auto_ack; + nl_socket_disable_msg_peek; + nl_socket_disable_seq_check; + nl_socket_drop_membership; + nl_socket_drop_memberships; + nl_socket_enable_auto_ack; + nl_socket_enable_msg_peek; + nl_socket_free; + nl_socket_get_cb; + nl_socket_get_fd; + nl_socket_get_local_port; + nl_socket_get_msg_buf_size; + nl_socket_get_peer_groups; + nl_socket_get_peer_port; + nl_socket_modify_cb; + nl_socket_modify_err_cb; + nl_socket_recv_pktinfo; + nl_socket_set_buffer_size; + nl_socket_set_cb; + nl_socket_set_local_port; + nl_socket_set_msg_buf_size; + nl_socket_set_nonblocking; + nl_socket_set_passcred; + nl_socket_set_peer_groups; + nl_socket_set_peer_port; + nl_socket_use_seq; + nl_str2af; + nl_str2ether_proto; + nl_str2ip_proto; + nl_str2llproto; + nl_str2msec; + nl_str2nlfamily; + nl_str2nlmsgtype; + nl_syserr2nlerr; + nl_ticks2us; + nl_us2ticks; + nl_ver_maj; + nl_ver_mic; + nl_ver_min; + nl_ver_num; + nl_wait_for_ack; + nla_attr_size; + nla_data; + nla_find; + nla_get_flag; + nla_get_msecs; + nla_get_string; + nla_get_u16; + nla_get_u32; + nla_get_u64; + nla_get_u8; + nla_is_nested; + nla_len; + nla_memcmp; + nla_memcpy; + nla_nest_cancel; + nla_nest_end; + nla_nest_start; + nla_next; + nla_ok; + nla_padlen; + nla_parse; + nla_parse_nested; + nla_put; + nla_put_addr; + nla_put_data; + nla_put_flag; + nla_put_msecs; + nla_put_nested; + nla_put_string; + nla_put_u16; + nla_put_u32; + nla_put_u64; + nla_put_u8; + nla_reserve; + nla_strcmp; + nla_strdup; + nla_strlcpy; + nla_total_size; + nla_type; + nla_validate; + nlmsg_alloc; + nlmsg_alloc_simple; + nlmsg_alloc_size; + nlmsg_append; + nlmsg_attrdata; + nlmsg_attrlen; + nlmsg_convert; + nlmsg_data; + nlmsg_datalen; + nlmsg_expand; + nlmsg_find_attr; + nlmsg_free; + nlmsg_get; + nlmsg_get_creds; + nlmsg_get_dst; + nlmsg_get_max_size; + nlmsg_get_proto; + nlmsg_get_src; + nlmsg_hdr; + nlmsg_inherit; + nlmsg_next; + nlmsg_ok; + nlmsg_padlen; + nlmsg_parse; + nlmsg_put; + nlmsg_reserve; + nlmsg_set_creds; + nlmsg_set_default_size; + nlmsg_set_dst; + nlmsg_set_proto; + nlmsg_set_src; + nlmsg_size; + nlmsg_tail; + nlmsg_total_size; + nlmsg_valid_hdr; + nlmsg_validate; + + # The following symbols were added during the development of 3.2.26. + # Keep them in libnl_3 to avoid breaking users. + nl_cache_pickup_checkdup; + nl_pickup_keep_syserr; + +local: + *; +}; + +libnl_3_2_26 { +global: + nl_socket_set_fd; +} libnl_3; + +libnl_3_2_27 { +global: + nla_get_s8; + nla_put_s8; + nla_get_s16; + nla_put_s16; + nla_get_s32; + nla_put_s32; + nla_get_s64; + nla_put_s64; +} libnl_3_2_26; + +libnl_3_2_28 { +global: + nl_object_diff64; +} libnl_3_2_27; + +libnl_3_2_29 { +global: + nl_cache_include_v2; + nl_cache_mngr_add_cache_v2; + nl_strerror_l; +} libnl_3_2_28; + +libnl_3_5 { +global: + nla_nest_end_keep_empty; +} libnl_3_2_29; + +libnl_3_6 { +} libnl_3_5; + +libnl_3_10 { +global: + nl_cache_mngr_alloc_ex; +} libnl_3_6; + +libnl_3_11 { +global: + nla_get_sint; + nla_put_sint; + nla_get_uint; + nla_put_uint; +} libnl_3_10; diff --git a/libnl/libnl-cli-3.0.pc.in b/libnl/libnl-cli-3.0.pc.in new file mode 100644 index 0000000..d3638ba --- /dev/null +++ b/libnl/libnl-cli-3.0.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libnl-cli +Description: Command Line Interface library for netlink sockets +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lnl-cli-@MAJ_VERSION@ +Cflags: -I${includedir} +Requires: libnl-3.0 libnl-genl-3.0 libnl-nf-3.0 libnl-route-3.0 diff --git a/libnl/libnl-cli-3.sym b/libnl/libnl-cli-3.sym new file mode 100644 index 0000000..75f03cd --- /dev/null +++ b/libnl/libnl-cli-3.sym @@ -0,0 +1,129 @@ +# The linker version script controls which symbols are exported. +# Version tags that correspond to past releases group the symbols +# that were part of that release. Hence, such a version tag is sealed +# and MUST not be extended. +# Any new symbols MUST only be added to the bottom of the file, in +# the version tag for the next upcoming release. +libnl_3 { +global: + nl_cli_addr_alloc; + nl_cli_addr_parse; + nl_cli_addr_parse_broadcast; + nl_cli_addr_parse_dev; + nl_cli_addr_parse_family; + nl_cli_addr_parse_label; + nl_cli_addr_parse_local; + nl_cli_addr_parse_peer; + nl_cli_addr_parse_preferred; + nl_cli_addr_parse_scope; + nl_cli_addr_parse_valid; + nl_cli_alloc_cache; + nl_cli_alloc_socket; + nl_cli_class_alloc; + nl_cli_class_alloc_cache; + nl_cli_cls_alloc; + nl_cli_cls_alloc_cache; + nl_cli_cls_parse_ematch; + nl_cli_cls_parse_proto; + nl_cli_confirm; + nl_cli_connect; + nl_cli_ct_alloc; + nl_cli_ct_alloc_cache; + nl_cli_ct_parse_dst; + nl_cli_ct_parse_dst_port; + nl_cli_ct_parse_family; + nl_cli_ct_parse_id; + nl_cli_ct_parse_mark; + nl_cli_ct_parse_protocol; + nl_cli_ct_parse_src; + nl_cli_ct_parse_src_port; + nl_cli_ct_parse_status; + nl_cli_ct_parse_tcp_state; + nl_cli_ct_parse_timeout; + nl_cli_ct_parse_use; + nl_cli_ct_parse_zone; + nl_cli_exp_alloc; + nl_cli_exp_alloc_cache; + nl_cli_exp_parse_class; + nl_cli_exp_parse_dst; + nl_cli_exp_parse_dst_port; + nl_cli_exp_parse_family; + nl_cli_exp_parse_flags; + nl_cli_exp_parse_fn; + nl_cli_exp_parse_helper_name; + nl_cli_exp_parse_icmp_code; + nl_cli_exp_parse_icmp_id; + nl_cli_exp_parse_icmp_type; + nl_cli_exp_parse_id; + nl_cli_exp_parse_l4protonum; + nl_cli_exp_parse_nat_dir; + nl_cli_exp_parse_src; + nl_cli_exp_parse_src_port; + nl_cli_exp_parse_timeout; + nl_cli_exp_parse_zone; + nl_cli_fatal; + nl_cli_link_alloc; + nl_cli_link_alloc_cache; + nl_cli_link_alloc_cache_family; + nl_cli_link_parse_family; + nl_cli_link_parse_ifalias; + nl_cli_link_parse_ifindex; + nl_cli_link_parse_mtu; + nl_cli_link_parse_name; + nl_cli_link_parse_txqlen; + nl_cli_link_parse_weight; + nl_cli_load_module; + nl_cli_neigh_alloc; + nl_cli_neigh_parse_dev; + nl_cli_neigh_parse_dst; + nl_cli_neigh_parse_family; + nl_cli_neigh_parse_lladdr; + nl_cli_neigh_parse_state; + nl_cli_parse_dumptype; + nl_cli_parse_u32; + nl_cli_print_version; + nl_cli_qdisc_alloc; + nl_cli_route_alloc; + nl_cli_route_alloc_cache; + nl_cli_route_parse_dst; + nl_cli_route_parse_family; + nl_cli_route_parse_iif; + nl_cli_route_parse_metric; + nl_cli_route_parse_nexthop; + nl_cli_route_parse_pref_src; + nl_cli_route_parse_prio; + nl_cli_route_parse_protocol; + nl_cli_route_parse_scope; + nl_cli_route_parse_src; + nl_cli_route_parse_table; + nl_cli_route_parse_type; + nl_cli_rule_alloc; + nl_cli_rule_alloc_cache; + nl_cli_rule_parse_family; + nl_cli_tc_lookup; + nl_cli_tc_parse_dev; + nl_cli_tc_parse_handle; + nl_cli_tc_parse_kind; + nl_cli_tc_parse_linktype; + nl_cli_tc_parse_mpu; + nl_cli_tc_parse_mtu; + nl_cli_tc_parse_overhead; + nl_cli_tc_parse_parent; + nl_cli_tc_register; + nl_cli_tc_unregister; +local: + *; +}; + +libnl_3_2_28 { +global: + nl_cli_alloc_cache_flags; + nl_cli_link_alloc_cache_flags; + nl_cli_link_alloc_cache_family_flags; +} libnl_3; + +libnl_3_8 { +global: + nl_cli_nh_alloc; + nl_cli_nh_alloc_cache; +} libnl_3_2_28; diff --git a/libnl/libnl-genl-3.0.pc.in b/libnl/libnl-genl-3.0.pc.in new file mode 100644 index 0000000..d6b69b8 --- /dev/null +++ b/libnl/libnl-genl-3.0.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libnl-genl +Description: Generic Netlink Library +Version: @PACKAGE_VERSION@ +Requires: libnl-3.0 +Libs: -L${libdir} -lnl-genl-@MAJ_VERSION@ +Cflags: -I${includedir}/libnl@MAJ_VERSION@ diff --git a/libnl/libnl-genl-3.sym b/libnl/libnl-genl-3.sym new file mode 100644 index 0000000..1b26127 --- /dev/null +++ b/libnl/libnl-genl-3.sym @@ -0,0 +1,57 @@ +# The linker version script controls which symbols are exported. +# Version tags that correspond to past releases group the symbols +# that were part of that release. Hence, such a version tag is sealed +# and MUST not be extended. +# Any new symbols MUST only be added to the bottom of the file, in +# the version tag for the next upcoming release. +libnl_3 { +global: + # these functions are in private header files and should have never + # been exported. We might hide them later. + genl_resolve_id; + + genl_connect; + genl_ctrl_alloc_cache; + genl_ctrl_resolve; + genl_ctrl_resolve_grp; + genl_ctrl_search; + genl_ctrl_search_by_name; + genl_family_add_grp; + genl_family_add_op; + genl_family_alloc; + genl_family_get_hdrsize; + genl_family_get_id; + genl_family_get_maxattr; + genl_family_get_name; + genl_family_get_version; + genl_family_ops; + genl_family_put; + genl_family_set_hdrsize; + genl_family_set_id; + genl_family_set_maxattr; + genl_family_set_name; + genl_family_set_version; + genl_handle_msg; + genl_mngt_resolve; + genl_op2name; + genl_ops_resolve; + genl_register; + genl_register_family; + genl_send_simple; + genl_unregister; + genl_unregister_family; + genlmsg_attrdata; + genlmsg_attrlen; + genlmsg_data; + genlmsg_hdr; + genlmsg_len; + genlmsg_parse; + genlmsg_put; + genlmsg_user_data; + genlmsg_user_datalen; + genlmsg_user_hdr; + genlmsg_valid_hdr; + genlmsg_validate; +local: + *; +}; diff --git a/libnl/libnl-idiag-3.0.pc.in b/libnl/libnl-idiag-3.0.pc.in new file mode 100644 index 0000000..9ce5100 --- /dev/null +++ b/libnl/libnl-idiag-3.0.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libnl-idiag +Description: Netlink Inet Diag Family Library +Version: @PACKAGE_VERSION@ +Requires: libnl-3.0 +Libs: -L${libdir} -lnl-idiag-@MAJ_VERSION@ +Cflags: -I${includedir}/libnl@MAJ_VERSION@ + diff --git a/libnl/libnl-idiag-3.sym b/libnl/libnl-idiag-3.sym new file mode 100644 index 0000000..3391f19 --- /dev/null +++ b/libnl/libnl-idiag-3.sym @@ -0,0 +1,115 @@ +# The linker version script controls which symbols are exported. +# Version tags that correspond to past releases group the symbols +# that were part of that release. Hence, such a version tag is sealed +# and MUST not be extended. +# Any new symbols MUST only be added to the bottom of the file, in +# the version tag for the next upcoming release. +libnl_3 { +global: + # ops structure + idiagnl_meminfo_obj_ops; + idiagnl_msg_obj_ops; + idiagnl_req_obj_ops; + idiagnl_vegasinfo_obj_ops; + + idiagnl_attrs2str; + idiagnl_connect; + idiagnl_exts2str; + idiagnl_meminfo_alloc; + idiagnl_meminfo_get; + idiagnl_meminfo_get_fmem; + idiagnl_meminfo_get_rmem; + idiagnl_meminfo_get_tmem; + idiagnl_meminfo_get_wmem; + idiagnl_meminfo_put; + idiagnl_meminfo_set_fmem; + idiagnl_meminfo_set_rmem; + idiagnl_meminfo_set_tmem; + idiagnl_meminfo_set_wmem; + idiagnl_msg_alloc; + idiagnl_msg_alloc_cache; + idiagnl_msg_get; + idiagnl_msg_get_cong; + idiagnl_msg_get_dport; + idiagnl_msg_get_dst; + idiagnl_msg_get_expires; + idiagnl_msg_get_family; + idiagnl_msg_get_ifindex; + idiagnl_msg_get_inode; + idiagnl_msg_get_meminfo; + idiagnl_msg_get_retrans; + idiagnl_msg_get_rqueue; + idiagnl_msg_get_shutdown; + idiagnl_msg_get_sport; + idiagnl_msg_get_src; + idiagnl_msg_get_state; + idiagnl_msg_get_tclass; + idiagnl_msg_get_tcpinfo; + idiagnl_msg_get_timer; + idiagnl_msg_get_tos; + idiagnl_msg_get_uid; + idiagnl_msg_get_vegasinfo; + idiagnl_msg_get_wqueue; + idiagnl_msg_parse; + idiagnl_msg_put; + idiagnl_msg_set_cong; + idiagnl_msg_set_dport; + idiagnl_msg_set_dst; + idiagnl_msg_set_expires; + idiagnl_msg_set_family; + idiagnl_msg_set_ifindex; + idiagnl_msg_set_inode; + idiagnl_msg_set_meminfo; + idiagnl_msg_set_retrans; + idiagnl_msg_set_rqueue; + idiagnl_msg_set_shutdown; + idiagnl_msg_set_sport; + idiagnl_msg_set_src; + idiagnl_msg_set_state; + idiagnl_msg_set_tclass; + idiagnl_msg_set_tcpinfo; + idiagnl_msg_set_timer; + idiagnl_msg_set_tos; + idiagnl_msg_set_uid; + idiagnl_msg_set_vegasinfo; + idiagnl_msg_set_wqueue; + idiagnl_req_alloc; + idiagnl_req_get; + idiagnl_req_get_dbs; + idiagnl_req_get_dst; + idiagnl_req_get_ext; + idiagnl_req_get_family; + idiagnl_req_get_ifindex; + idiagnl_req_get_src; + idiagnl_req_get_states; + idiagnl_req_parse; + idiagnl_req_put; + idiagnl_req_set_dbs; + idiagnl_req_set_dst; + idiagnl_req_set_ext; + idiagnl_req_set_family; + idiagnl_req_set_ifindex; + idiagnl_req_set_src; + idiagnl_req_set_states; + idiagnl_send_simple; + idiagnl_shutdown2str; + idiagnl_state2str; + idiagnl_str2state; + idiagnl_str2timer; + idiagnl_tcpopts2str; + idiagnl_tcpstate2str; + idiagnl_timer2str; + idiagnl_vegasinfo_alloc; + idiagnl_vegasinfo_get; + idiagnl_vegasinfo_get_enabled; + idiagnl_vegasinfo_get_minrtt; + idiagnl_vegasinfo_get_rtt; + idiagnl_vegasinfo_get_rttcnt; + idiagnl_vegasinfo_put; + idiagnl_vegasinfo_set_enabled; + idiagnl_vegasinfo_set_minrtt; + idiagnl_vegasinfo_set_rtt; + idiagnl_vegasinfo_set_rttcnt; +local: + *; +}; diff --git a/libnl/libnl-nf-3.0.pc.in b/libnl/libnl-nf-3.0.pc.in new file mode 100644 index 0000000..d82e1a6 --- /dev/null +++ b/libnl/libnl-nf-3.0.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libnl-nf +Description: Netfilter Netlink Library +Version: @PACKAGE_VERSION@ +Requires: libnl-route-3.0 +Libs: -L${libdir} -lnl-nf-@MAJ_VERSION@ +Cflags: -I${includedir}/libnl@MAJ_VERSION@ diff --git a/libnl/libnl-nf-3.sym b/libnl/libnl-nf-3.sym new file mode 100644 index 0000000..7563624 --- /dev/null +++ b/libnl/libnl-nf-3.sym @@ -0,0 +1,351 @@ +# The linker version script controls which symbols are exported. +# Version tags that correspond to past releases group the symbols +# that were part of that release. Hence, such a version tag is sealed +# and MUST not be extended. +# Any new symbols MUST only be added to the bottom of the file, in +# the version tag for the next upcoming release. +libnl_3 { +global: + # ops structure + ct_obj_ops; + exp_obj_ops; + log_msg_obj_ops; + log_obj_ops; + queue_msg_obj_ops; + queue_obj_ops; + + nfnl_connect; + nfnl_ct_add; + nfnl_ct_alloc; + nfnl_ct_alloc_cache; + nfnl_ct_build_add_request; + nfnl_ct_build_delete_request; + nfnl_ct_build_query_request; + nfnl_ct_del; + nfnl_ct_dump_request; + nfnl_ct_get; + nfnl_ct_get_bytes; + nfnl_ct_get_dst; + nfnl_ct_get_dst_port; + nfnl_ct_get_family; + nfnl_ct_get_icmp_code; + nfnl_ct_get_icmp_id; + nfnl_ct_get_icmp_type; + nfnl_ct_get_id; + nfnl_ct_get_mark; + nfnl_ct_get_packets; + nfnl_ct_get_proto; + nfnl_ct_get_src; + nfnl_ct_get_src_port; + nfnl_ct_get_status; + nfnl_ct_get_tcp_state; + nfnl_ct_get_timeout; + nfnl_ct_get_timestamp; + nfnl_ct_get_use; + nfnl_ct_get_zone; + nfnl_ct_put; + nfnl_ct_query; + nfnl_ct_set_bytes; + nfnl_ct_set_dst; + nfnl_ct_set_dst_port; + nfnl_ct_set_family; + nfnl_ct_set_icmp_code; + nfnl_ct_set_icmp_id; + nfnl_ct_set_icmp_type; + nfnl_ct_set_id; + nfnl_ct_set_mark; + nfnl_ct_set_packets; + nfnl_ct_set_proto; + nfnl_ct_set_src; + nfnl_ct_set_src_port; + nfnl_ct_set_status; + nfnl_ct_set_tcp_state; + nfnl_ct_set_timeout; + nfnl_ct_set_timestamp; + nfnl_ct_set_use; + nfnl_ct_set_zone; + nfnl_ct_status2str; + nfnl_ct_str2status; + nfnl_ct_str2tcp_state; + nfnl_ct_tcp_state2str; + nfnl_ct_test_bytes; + nfnl_ct_test_dst_port; + nfnl_ct_test_icmp_code; + nfnl_ct_test_icmp_id; + nfnl_ct_test_icmp_type; + nfnl_ct_test_id; + nfnl_ct_test_mark; + nfnl_ct_test_packets; + nfnl_ct_test_proto; + nfnl_ct_test_src_port; + nfnl_ct_test_status; + nfnl_ct_test_tcp_state; + nfnl_ct_test_timeout; + nfnl_ct_test_timestamp; + nfnl_ct_test_use; + nfnl_ct_test_zone; + nfnl_ct_unset_status; + nfnl_exp_add; + nfnl_exp_alloc; + nfnl_exp_alloc_cache; + nfnl_exp_build_add_request; + nfnl_exp_build_delete_request; + nfnl_exp_build_query_request; + nfnl_exp_del; + nfnl_exp_dump_request; + nfnl_exp_flags2str; + nfnl_exp_get; + nfnl_exp_get_class; + nfnl_exp_get_dst; + nfnl_exp_get_dst_port; + nfnl_exp_get_family; + nfnl_exp_get_flags; + nfnl_exp_get_fn; + nfnl_exp_get_helper_name; + nfnl_exp_get_icmp_code; + nfnl_exp_get_icmp_id; + nfnl_exp_get_icmp_type; + nfnl_exp_get_id; + nfnl_exp_get_l4protonum; + nfnl_exp_get_nat_dir; + nfnl_exp_get_src; + nfnl_exp_get_src_port; + nfnl_exp_get_timeout; + nfnl_exp_get_zone; + nfnl_exp_put; + nfnl_exp_query; + nfnl_exp_set_class; + nfnl_exp_set_dst; + nfnl_exp_set_family; + nfnl_exp_set_flags; + nfnl_exp_set_fn; + nfnl_exp_set_helper_name; + nfnl_exp_set_icmp; + nfnl_exp_set_id; + nfnl_exp_set_l4protonum; + nfnl_exp_set_nat_dir; + nfnl_exp_set_ports; + nfnl_exp_set_src; + nfnl_exp_set_timeout; + nfnl_exp_set_zone; + nfnl_exp_str2flags; + nfnl_exp_test_class; + nfnl_exp_test_dst; + nfnl_exp_test_flags; + nfnl_exp_test_fn; + nfnl_exp_test_helper_name; + nfnl_exp_test_icmp; + nfnl_exp_test_id; + nfnl_exp_test_l4protonum; + nfnl_exp_test_nat_dir; + nfnl_exp_test_ports; + nfnl_exp_test_src; + nfnl_exp_test_timeout; + nfnl_exp_test_zone; + nfnl_exp_unset_flags; + nfnl_inet_hook2str; + nfnl_log_alloc; + nfnl_log_build_change_request; + nfnl_log_build_create_request; + nfnl_log_build_delete_request; + nfnl_log_build_pf_bind; + nfnl_log_build_pf_unbind; + nfnl_log_change; + nfnl_log_copy_mode2str; + nfnl_log_create; + nfnl_log_delete; + nfnl_log_flags2str; + nfnl_log_get; + nfnl_log_get_alloc_size; + nfnl_log_get_copy_mode; + nfnl_log_get_copy_range; + nfnl_log_get_flush_timeout; + nfnl_log_get_group; + nfnl_log_get_queue_threshold; + nfnl_log_msg_alloc; + nfnl_log_msg_get; + nfnl_log_msg_get_family; + nfnl_log_msg_get_gid; + nfnl_log_msg_get_hook; + nfnl_log_msg_get_hwaddr; + nfnl_log_msg_get_hwproto; + nfnl_log_msg_get_indev; + nfnl_log_msg_get_mark; + nfnl_log_msg_get_outdev; + nfnl_log_msg_get_payload; + nfnl_log_msg_get_physindev; + nfnl_log_msg_get_physoutdev; + nfnl_log_msg_get_prefix; + nfnl_log_msg_get_seq; + nfnl_log_msg_get_seq_global; + nfnl_log_msg_get_timestamp; + nfnl_log_msg_get_uid; + nfnl_log_msg_put; + nfnl_log_msg_set_family; + nfnl_log_msg_set_gid; + nfnl_log_msg_set_hook; + nfnl_log_msg_set_hwaddr; + nfnl_log_msg_set_hwproto; + nfnl_log_msg_set_indev; + nfnl_log_msg_set_mark; + nfnl_log_msg_set_outdev; + nfnl_log_msg_set_payload; + nfnl_log_msg_set_physindev; + nfnl_log_msg_set_physoutdev; + nfnl_log_msg_set_prefix; + nfnl_log_msg_set_seq; + nfnl_log_msg_set_seq_global; + nfnl_log_msg_set_timestamp; + nfnl_log_msg_set_uid; + nfnl_log_msg_test_gid; + nfnl_log_msg_test_hook; + nfnl_log_msg_test_hwproto; + nfnl_log_msg_test_mark; + nfnl_log_msg_test_seq; + nfnl_log_msg_test_seq_global; + nfnl_log_msg_test_uid; + nfnl_log_pf_bind; + nfnl_log_pf_unbind; + nfnl_log_put; + nfnl_log_set_alloc_size; + nfnl_log_set_copy_mode; + nfnl_log_set_copy_range; + nfnl_log_set_flags; + nfnl_log_set_flush_timeout; + nfnl_log_set_group; + nfnl_log_set_queue_threshold; + nfnl_log_str2copy_mode; + nfnl_log_str2flags; + nfnl_log_test_alloc_size; + nfnl_log_test_copy_mode; + nfnl_log_test_copy_range; + nfnl_log_test_flush_timeout; + nfnl_log_test_group; + nfnl_log_test_queue_threshold; + nfnl_log_unset_flags; + nfnl_queue_alloc; + nfnl_queue_build_change_request; + nfnl_queue_build_create_request; + nfnl_queue_build_delete_request; + nfnl_queue_build_pf_bind; + nfnl_queue_build_pf_unbind; + nfnl_queue_change; + nfnl_queue_copy_mode2str; + nfnl_queue_create; + nfnl_queue_delete; + nfnl_queue_get; + nfnl_queue_get_copy_mode; + nfnl_queue_get_copy_range; + nfnl_queue_get_group; + nfnl_queue_get_maxlen; + nfnl_queue_msg_alloc; + nfnl_queue_msg_build_verdict; + nfnl_queue_msg_build_verdict_batch; + nfnl_queue_msg_get; + nfnl_queue_msg_get_family; + nfnl_queue_msg_get_group; + nfnl_queue_msg_get_hook; + nfnl_queue_msg_get_hwaddr; + nfnl_queue_msg_get_hwproto; + nfnl_queue_msg_get_indev; + nfnl_queue_msg_get_mark; + nfnl_queue_msg_get_outdev; + nfnl_queue_msg_get_packetid; + nfnl_queue_msg_get_payload; + nfnl_queue_msg_get_physindev; + nfnl_queue_msg_get_physoutdev; + nfnl_queue_msg_get_timestamp; + nfnl_queue_msg_get_verdict; + nfnl_queue_msg_put; + nfnl_queue_msg_send_verdict; + nfnl_queue_msg_send_verdict_batch; + nfnl_queue_msg_send_verdict_payload; + nfnl_queue_msg_set_family; + nfnl_queue_msg_set_group; + nfnl_queue_msg_set_hook; + nfnl_queue_msg_set_hwaddr; + nfnl_queue_msg_set_hwproto; + nfnl_queue_msg_set_indev; + nfnl_queue_msg_set_mark; + nfnl_queue_msg_set_outdev; + nfnl_queue_msg_set_packetid; + nfnl_queue_msg_set_payload; + nfnl_queue_msg_set_physindev; + nfnl_queue_msg_set_physoutdev; + nfnl_queue_msg_set_timestamp; + nfnl_queue_msg_set_verdict; + nfnl_queue_msg_test_family; + nfnl_queue_msg_test_group; + nfnl_queue_msg_test_hook; + nfnl_queue_msg_test_hwaddr; + nfnl_queue_msg_test_hwproto; + nfnl_queue_msg_test_indev; + nfnl_queue_msg_test_mark; + nfnl_queue_msg_test_outdev; + nfnl_queue_msg_test_packetid; + nfnl_queue_msg_test_payload; + nfnl_queue_msg_test_physindev; + nfnl_queue_msg_test_physoutdev; + nfnl_queue_msg_test_timestamp; + nfnl_queue_msg_test_verdict; + nfnl_queue_pf_bind; + nfnl_queue_pf_unbind; + nfnl_queue_put; + nfnl_queue_set_copy_mode; + nfnl_queue_set_copy_range; + nfnl_queue_set_group; + nfnl_queue_set_maxlen; + nfnl_queue_socket_alloc; + nfnl_queue_str2copy_mode; + nfnl_queue_test_copy_mode; + nfnl_queue_test_copy_range; + nfnl_queue_test_group; + nfnl_queue_test_maxlen; + nfnl_send_simple; + nfnl_str2inet_hook; + nfnl_str2verdict; + nfnl_verdict2str; + nfnlmsg_alloc_simple; + nfnlmsg_ct_group; + nfnlmsg_ct_parse; + nfnlmsg_exp_group; + nfnlmsg_exp_parse; + nfnlmsg_family; + nfnlmsg_log_msg_parse; + nfnlmsg_put; + nfnlmsg_queue_msg_parse; + nfnlmsg_res_id; + nfnlmsg_subsys; + nfnlmsg_subtype; +local: + *; +}; + +libnl_3_6 { +global: + nfnl_log_msg_get_ct; + nfnl_log_msg_get_ct_info; + nfnl_log_msg_get_hwheader; + nfnl_log_msg_get_hwlen; + nfnl_log_msg_get_hwtype; + nfnl_log_msg_get_vlan_cfi; + nfnl_log_msg_get_vlan_id; + nfnl_log_msg_get_vlan_prio; + nfnl_log_msg_get_vlan_proto; + nfnl_log_msg_get_vlan_tag; + nfnl_log_msg_set_ct; + nfnl_log_msg_set_ct_info; + nfnl_log_msg_set_hwheader; + nfnl_log_msg_set_hwlen; + nfnl_log_msg_set_hwtype; + nfnl_log_msg_set_vlan_proto; + nfnl_log_msg_set_vlan_tag; + nfnl_log_msg_test_ct; + nfnl_log_msg_test_ct_info; + nfnl_log_msg_test_hwheader; + nfnl_log_msg_test_hwlen; + nfnl_log_msg_test_hwtype; + nfnl_log_msg_test_vlan_proto; + nfnl_log_msg_test_vlan_tag; + nfnlmsg_ct_parse_nested; +} libnl_3; diff --git a/libnl/libnl-route-3.0.pc.in b/libnl/libnl-route-3.0.pc.in new file mode 100644 index 0000000..372a4f4 --- /dev/null +++ b/libnl/libnl-route-3.0.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libnl-route +Description: Netlink Routing Family Library +Version: @PACKAGE_VERSION@ +Requires: libnl-3.0 +Libs: -L${libdir} -lnl-route-@MAJ_VERSION@ +Cflags: -I${includedir}/libnl@MAJ_VERSION@ diff --git a/libnl/libnl-route-3.sym b/libnl/libnl-route-3.sym new file mode 100644 index 0000000..7588332 --- /dev/null +++ b/libnl/libnl-route-3.sym @@ -0,0 +1,1366 @@ +# The linker version script controls which symbols are exported. +# Version tags that correspond to past releases group the symbols +# that were part of that release. Hence, such a version tag is sealed +# and MUST not be extended. +# Any new symbols MUST only be added to the bottom of the file, in +# the version tag for the next upcoming release. +libnl_3 { +global: + # these functions are in private header files and should have never + # been exported. We might hide them later. + rtnl_link_af_alloc; + rtnl_link_af_data; + rtnl_link_af_data_compare; + rtnl_link_af_ops_lookup; + rtnl_link_af_ops_put; + rtnl_link_af_register; + rtnl_link_af_unregister; + rtnl_link_info_ops_lookup; + rtnl_link_info_ops_put; + rtnl_link_register_info; + rtnl_link_unregister_info; + + # these functions are in private header files and should have never + # been exported. + # declared in "lib/route/tc-api.h". + rtnl_tc_build_rate_table; + rtnl_tc_clone; + rtnl_tc_compare; + rtnl_tc_data; + rtnl_tc_data_check; + rtnl_tc_dump_details; + rtnl_tc_dump_line; + rtnl_tc_dump_stats; + rtnl_tc_free_data; + rtnl_tc_msg_build; + rtnl_tc_msg_parse; + rtnl_tc_register; + rtnl_tc_type_register; + rtnl_tc_type_unregister; + rtnl_tc_unregister; + + # these functions are in private header files and should have never + # been exported. They are used by libnl internals. + # declared in "include/nl-priv-dynamic-route/nl-priv-dynamic-route.h". + rtnl_tc_get_ops; + rtnl_tc_lookup_ops; + + # internal symbols that are in public headers + rtln_link_policy; + + # ops structure + route_obj_ops; + + flnl_lookup; + flnl_lookup_build_request; + flnl_request_alloc; + flnl_request_get_addr; + flnl_request_get_fwmark; + flnl_request_get_scope; + flnl_request_get_table; + flnl_request_get_tos; + flnl_request_set_addr; + flnl_request_set_fwmark; + flnl_request_set_scope; + flnl_request_set_table; + flnl_request_set_tos; + flnl_result_alloc; + flnl_result_alloc_cache; + flnl_result_get_error; + flnl_result_get_nexthop_sel; + flnl_result_get_prefixlen; + flnl_result_get_scope; + flnl_result_get_table_id; + flnl_result_get_type; + flnl_result_put; + nl_ovl_strategy2str; + nl_police2str; + nl_rtgen_request; + nl_rtntype2str; + nl_str2ovl_strategy; + nl_str2police; + nl_str2rtntype; + rtnl_act_add; + rtnl_act_alloc; + rtnl_act_append; + rtnl_act_build_add_request; + rtnl_act_build_change_request; + rtnl_act_build_delete_request; + rtnl_act_change; + rtnl_act_delete; + rtnl_act_fill; + rtnl_act_get; + rtnl_act_parse; + rtnl_act_put; + rtnl_act_put_all; + rtnl_act_remove; + rtnl_addr_add; + rtnl_addr_alloc; + rtnl_addr_alloc_cache; + rtnl_addr_build_add_request; + rtnl_addr_build_delete_request; + rtnl_addr_delete; + rtnl_addr_flags2str; + rtnl_addr_get; + rtnl_addr_get_anycast; + rtnl_addr_get_broadcast; + rtnl_addr_get_create_time; + rtnl_addr_get_family; + rtnl_addr_get_flags; + rtnl_addr_get_ifindex; + rtnl_addr_get_label; + rtnl_addr_get_last_update_time; + rtnl_addr_get_link; + rtnl_addr_get_local; + rtnl_addr_get_multicast; + rtnl_addr_get_peer; + rtnl_addr_get_preferred_lifetime; + rtnl_addr_get_prefixlen; + rtnl_addr_get_scope; + rtnl_addr_get_valid_lifetime; + rtnl_addr_put; + rtnl_addr_set_anycast; + rtnl_addr_set_broadcast; + rtnl_addr_set_family; + rtnl_addr_set_flags; + rtnl_addr_set_ifindex; + rtnl_addr_set_label; + rtnl_addr_set_link; + rtnl_addr_set_local; + rtnl_addr_set_multicast; + rtnl_addr_set_peer; + rtnl_addr_set_preferred_lifetime; + rtnl_addr_set_prefixlen; + rtnl_addr_set_scope; + rtnl_addr_set_valid_lifetime; + rtnl_addr_str2flags; + rtnl_addr_unset_flags; + rtnl_basic_add_action; + rtnl_basic_del_action; + rtnl_basic_get_ematch; + rtnl_basic_get_target; + rtnl_basic_set_ematch; + rtnl_basic_set_target; + rtnl_cgroup_get_ematch; + rtnl_cgroup_set_ematch; + rtnl_class_add; + rtnl_class_alloc; + rtnl_class_alloc_cache; + rtnl_class_build_add_request; + rtnl_class_build_delete_request; + rtnl_class_delete; + rtnl_class_dsmark_get_bitmask; + rtnl_class_dsmark_get_value; + rtnl_class_dsmark_set_bitmask; + rtnl_class_dsmark_set_value; + rtnl_class_foreach_child; + rtnl_class_foreach_cls; + rtnl_class_get; + rtnl_class_leaf_qdisc; + rtnl_class_put; + rtnl_classid_generate; + rtnl_cls_add; + rtnl_cls_alloc; + rtnl_cls_alloc_cache; + rtnl_cls_build_add_request; + rtnl_cls_build_change_request; + rtnl_cls_build_delete_request; + rtnl_cls_change; + rtnl_cls_delete; + rtnl_cls_get_prio; + rtnl_cls_get_protocol; + rtnl_cls_put; + rtnl_cls_set_prio; + rtnl_cls_set_protocol; + rtnl_ematch_add_child; + rtnl_ematch_alloc; + rtnl_ematch_cmp_get; + rtnl_ematch_cmp_set; + rtnl_ematch_data; + rtnl_ematch_fill_attr; + rtnl_ematch_free; + rtnl_ematch_get_flags; + rtnl_ematch_lookup_ops; + rtnl_ematch_lookup_ops_by_name; + rtnl_ematch_meta_set_lvalue; + rtnl_ematch_meta_set_operand; + rtnl_ematch_meta_set_rvalue; + rtnl_ematch_nbyte_get_layer; + rtnl_ematch_nbyte_get_len; + rtnl_ematch_nbyte_get_offset; + rtnl_ematch_nbyte_get_pattern; + rtnl_ematch_nbyte_set_offset; + rtnl_ematch_nbyte_set_pattern; + rtnl_ematch_offset2txt; + rtnl_ematch_opnd2txt; + rtnl_ematch_parse_attr; + rtnl_ematch_parse_expr; + rtnl_ematch_register; + rtnl_ematch_set_flags; + rtnl_ematch_set_kind; + rtnl_ematch_set_name; + rtnl_ematch_set_ops; + rtnl_ematch_text_get_algo; + rtnl_ematch_text_get_from_layer; + rtnl_ematch_text_get_from_offset; + rtnl_ematch_text_get_len; + rtnl_ematch_text_get_pattern; + rtnl_ematch_text_get_to_layer; + rtnl_ematch_text_get_to_offset; + rtnl_ematch_text_set_algo; + rtnl_ematch_text_set_from; + rtnl_ematch_text_set_pattern; + rtnl_ematch_text_set_to; + rtnl_ematch_tree_add; + rtnl_ematch_tree_alloc; + rtnl_ematch_tree_dump; + rtnl_ematch_tree_free; + rtnl_ematch_unlink; + rtnl_ematch_unset_flags; + rtnl_fw_set_classid; + rtnl_fw_set_mask; + rtnl_htb_get_cbuffer; + rtnl_htb_get_ceil; + rtnl_htb_get_defcls; + rtnl_htb_get_level; + rtnl_htb_get_prio; + rtnl_htb_get_quantum; + rtnl_htb_get_rate2quantum; + rtnl_htb_get_rate; + rtnl_htb_get_rbuffer; + rtnl_htb_set_cbuffer; + rtnl_htb_set_ceil; + rtnl_htb_set_defcls; + rtnl_htb_set_level; + rtnl_htb_set_prio; + rtnl_htb_set_quantum; + rtnl_htb_set_rate2quantum; + rtnl_htb_set_rate; + rtnl_htb_set_rbuffer; + rtnl_link_add; + rtnl_link_alloc; + rtnl_link_alloc_cache; + rtnl_link_bond_add; + rtnl_link_bond_alloc; + rtnl_link_bond_enslave; + rtnl_link_bond_enslave_ifindex; + rtnl_link_bond_release; + rtnl_link_bond_release_ifindex; + rtnl_link_bridge_add; + rtnl_link_bridge_alloc; + rtnl_link_bridge_flags2str; + rtnl_link_bridge_get_cost; + rtnl_link_bridge_get_flags; + rtnl_link_bridge_get_port_state; + rtnl_link_bridge_get_priority; + rtnl_link_bridge_has_ext_info; + rtnl_link_bridge_set_cost; + rtnl_link_bridge_set_flags; + rtnl_link_bridge_set_port_state; + rtnl_link_bridge_set_priority; + rtnl_link_bridge_str2flags; + rtnl_link_bridge_unset_flags; + rtnl_link_build_add_request; + rtnl_link_build_change_request; + rtnl_link_build_delete_request; + rtnl_link_build_get_request; + rtnl_link_can_berr; + rtnl_link_can_berr_rx; + rtnl_link_can_berr_tx; + rtnl_link_can_ctrlmode2str; + rtnl_link_can_freq; + rtnl_link_can_get_bitrate; + rtnl_link_can_get_bittiming; + rtnl_link_can_get_bt_const; + rtnl_link_can_get_ctrlmode; + rtnl_link_can_get_restart_ms; + rtnl_link_can_get_sample_point; + rtnl_link_can_restart; + rtnl_link_can_set_bitrate; + rtnl_link_can_set_bittiming; + rtnl_link_can_set_ctrlmode; + rtnl_link_can_set_restart_ms; + rtnl_link_can_set_sample_point; + rtnl_link_can_state; + rtnl_link_can_str2ctrlmode; + rtnl_link_can_unset_ctrlmode; + rtnl_link_carrier2str; + rtnl_link_change; + rtnl_link_delete; + rtnl_link_enslave; + rtnl_link_enslave_ifindex; + rtnl_link_fill_info; + rtnl_link_flags2str; + rtnl_link_get; + rtnl_link_get_addr; + rtnl_link_get_arptype; + rtnl_link_get_broadcast; + rtnl_link_get_by_name; + rtnl_link_get_carrier; + rtnl_link_get_family; + rtnl_link_get_flags; + rtnl_link_get_group; + rtnl_link_get_ifalias; + rtnl_link_get_ifindex; + rtnl_link_get_info_type; + rtnl_link_get_kernel; + rtnl_link_get_link; + rtnl_link_get_linkmode; + rtnl_link_get_master; + rtnl_link_get_mtu; + rtnl_link_get_name; + rtnl_link_get_ns_fd; + rtnl_link_get_ns_pid; + rtnl_link_get_num_rx_queues; + rtnl_link_get_num_tx_queues; + rtnl_link_get_num_vf; + rtnl_link_get_operstate; + rtnl_link_get_phys_port_id; + rtnl_link_get_pmtudisc; + rtnl_link_get_promiscuity; + rtnl_link_get_qdisc; + rtnl_link_get_stat; + rtnl_link_get_txqlen; + rtnl_link_get_type; + rtnl_link_get_weight; + rtnl_link_i2name; + rtnl_link_inet_devconf2str; + rtnl_link_inet_get_conf; + rtnl_link_inet_set_conf; + rtnl_link_inet_str2devconf; + rtnl_link_info_parse; + rtnl_link_ip6_tnl_add; + rtnl_link_ip6_tnl_alloc; + rtnl_link_ip6_tnl_get_encaplimit; + rtnl_link_ip6_tnl_get_flags; + rtnl_link_ip6_tnl_get_flowinfo; + rtnl_link_ip6_tnl_get_link; + rtnl_link_ip6_tnl_get_local; + rtnl_link_ip6_tnl_get_proto; + rtnl_link_ip6_tnl_get_remote; + rtnl_link_ip6_tnl_get_tos; + rtnl_link_ip6_tnl_get_ttl; + rtnl_link_ip6_tnl_set_encaplimit; + rtnl_link_ip6_tnl_set_flags; + rtnl_link_ip6_tnl_set_flowinfo; + rtnl_link_ip6_tnl_set_link; + rtnl_link_ip6_tnl_set_local; + rtnl_link_ip6_tnl_set_proto; + rtnl_link_ip6_tnl_set_remote; + rtnl_link_ip6_tnl_set_tos; + rtnl_link_ip6_tnl_set_ttl; + rtnl_link_ipgre_add; + rtnl_link_ipgre_alloc; + rtnl_link_ipgre_get_iflags; + rtnl_link_ipgre_get_ikey; + rtnl_link_ipgre_get_link; + rtnl_link_ipgre_get_local; + rtnl_link_ipgre_get_oflags; + rtnl_link_ipgre_get_okey; + rtnl_link_ipgre_get_remote; + rtnl_link_ipgre_get_tos; + rtnl_link_ipgre_get_ttl; + rtnl_link_ipgre_set_iflags; + rtnl_link_ipgre_set_ikey; + rtnl_link_ipgre_set_link; + rtnl_link_ipgre_set_local; + rtnl_link_ipgre_set_oflags; + rtnl_link_ipgre_set_okey; + rtnl_link_ipgre_set_pmtudisc; + rtnl_link_ipgre_set_remote; + rtnl_link_ipgre_set_tos; + rtnl_link_ipgre_set_ttl; + rtnl_link_ipip_add; + rtnl_link_ipip_alloc; + rtnl_link_ipip_get_link; + rtnl_link_ipip_get_local; + rtnl_link_ipip_get_pmtudisc; + rtnl_link_ipip_get_remote; + rtnl_link_ipip_get_tos; + rtnl_link_ipip_get_ttl; + rtnl_link_ipip_set_link; + rtnl_link_ipip_set_local; + rtnl_link_ipip_set_pmtudisc; + rtnl_link_ipip_set_remote; + rtnl_link_ipip_set_tos; + rtnl_link_ipip_set_ttl; + rtnl_link_ipvti_add; + rtnl_link_ipvti_alloc; + rtnl_link_ipvti_get_ikey; + rtnl_link_ipvti_get_link; + rtnl_link_ipvti_get_local; + rtnl_link_ipvti_get_okey; + rtnl_link_ipvti_get_remote; + rtnl_link_ipvti_set_ikey; + rtnl_link_ipvti_set_link; + rtnl_link_ipvti_set_local; + rtnl_link_ipvti_set_okey; + rtnl_link_ipvti_set_remote; + rtnl_link_is_bridge; + rtnl_link_is_can; + rtnl_link_is_ip6_tnl; + rtnl_link_is_ipgre; + rtnl_link_is_ipip; + rtnl_link_is_ipvti; + rtnl_link_is_macvlan; + rtnl_link_is_sit; + rtnl_link_is_veth; + rtnl_link_is_vlan; + rtnl_link_is_vxlan; + rtnl_link_macvlan_alloc; + rtnl_link_macvlan_flags2str; + rtnl_link_macvlan_get_flags; + rtnl_link_macvlan_get_mode; + rtnl_link_macvlan_mode2str; + rtnl_link_macvlan_set_flags; + rtnl_link_macvlan_set_mode; + rtnl_link_macvlan_str2flags; + rtnl_link_macvlan_str2mode; + rtnl_link_macvlan_unset_flags; + rtnl_link_mode2str; + rtnl_link_name2i; + rtnl_link_operstate2str; + rtnl_link_put; + rtnl_link_release; + rtnl_link_release_ifindex; + rtnl_link_set_addr; + rtnl_link_set_arptype; + rtnl_link_set_broadcast; + rtnl_link_set_carrier; + rtnl_link_set_family; + rtnl_link_set_flags; + rtnl_link_set_group; + rtnl_link_set_ifalias; + rtnl_link_set_ifindex; + rtnl_link_set_info_type; + rtnl_link_set_link; + rtnl_link_set_linkmode; + rtnl_link_set_master; + rtnl_link_set_mtu; + rtnl_link_set_name; + rtnl_link_set_ns_fd; + rtnl_link_set_ns_pid; + rtnl_link_set_num_rx_queues; + rtnl_link_set_num_tx_queues; + rtnl_link_set_operstate; + rtnl_link_set_promiscuity; + rtnl_link_set_qdisc; + rtnl_link_set_stat; + rtnl_link_set_txqlen; + rtnl_link_set_type; + rtnl_link_set_weight; + rtnl_link_sit_add; + rtnl_link_sit_alloc; + rtnl_link_sit_get_flags; + rtnl_link_sit_get_link; + rtnl_link_sit_get_local; + rtnl_link_sit_get_pmtudisc; + rtnl_link_sit_get_proto; + rtnl_link_sit_get_remote; + rtnl_link_sit_get_tos; + rtnl_link_sit_get_ttl; + rtnl_link_sit_set_flags; + rtnl_link_sit_set_link; + rtnl_link_sit_set_local; + rtnl_link_sit_set_pmtudisc; + rtnl_link_sit_set_proto; + rtnl_link_sit_set_remote; + rtnl_link_sit_set_tos; + rtnl_link_sit_set_ttl; + rtnl_link_stat2str; + rtnl_link_str2carrier; + rtnl_link_str2flags; + rtnl_link_str2mode; + rtnl_link_str2operstate; + rtnl_link_str2stat; + rtnl_link_unset_flags; + rtnl_link_veth_add; + rtnl_link_veth_alloc; + rtnl_link_veth_get_peer; + rtnl_link_veth_release; + rtnl_link_vlan_alloc; + rtnl_link_vlan_flags2str; + rtnl_link_vlan_get_egress_map; + rtnl_link_vlan_get_flags; + rtnl_link_vlan_get_id; + rtnl_link_vlan_get_ingress_map; + rtnl_link_vlan_get_protocol; + rtnl_link_vlan_set_egress_map; + rtnl_link_vlan_set_flags; + rtnl_link_vlan_set_id; + rtnl_link_vlan_set_ingress_map; + rtnl_link_vlan_set_protocol; + rtnl_link_vlan_str2flags; + rtnl_link_vlan_unset_flags; + rtnl_link_vxlan_alloc; + rtnl_link_vxlan_disable_l2miss; + rtnl_link_vxlan_disable_l3miss; + rtnl_link_vxlan_disable_learning; + rtnl_link_vxlan_disable_proxy; + rtnl_link_vxlan_disable_rsc; + rtnl_link_vxlan_enable_l2miss; + rtnl_link_vxlan_enable_l3miss; + rtnl_link_vxlan_enable_learning; + rtnl_link_vxlan_enable_proxy; + rtnl_link_vxlan_enable_rsc; + rtnl_link_vxlan_get_ageing; + rtnl_link_vxlan_get_group; + rtnl_link_vxlan_get_id; + rtnl_link_vxlan_get_l2miss; + rtnl_link_vxlan_get_l3miss; + rtnl_link_vxlan_get_learning; + rtnl_link_vxlan_get_limit; + rtnl_link_vxlan_get_link; + rtnl_link_vxlan_get_local; + rtnl_link_vxlan_get_port_range; + rtnl_link_vxlan_get_proxy; + rtnl_link_vxlan_get_rsc; + rtnl_link_vxlan_get_tos; + rtnl_link_vxlan_get_ttl; + rtnl_link_vxlan_set_ageing; + rtnl_link_vxlan_set_group; + rtnl_link_vxlan_set_id; + rtnl_link_vxlan_set_l2miss; + rtnl_link_vxlan_set_l3miss; + rtnl_link_vxlan_set_learning; + rtnl_link_vxlan_set_limit; + rtnl_link_vxlan_set_link; + rtnl_link_vxlan_set_local; + rtnl_link_vxlan_set_port_range; + rtnl_link_vxlan_set_proxy; + rtnl_link_vxlan_set_rsc; + rtnl_link_vxlan_set_tos; + rtnl_link_vxlan_set_ttl; + rtnl_meta_value_alloc_id; + rtnl_meta_value_alloc_int; + rtnl_meta_value_alloc_var; + rtnl_meta_value_put; + rtnl_mirred_get_action; + rtnl_mirred_get_ifindex; + rtnl_mirred_get_policy; + rtnl_mirred_set_action; + rtnl_mirred_set_ifindex; + rtnl_mirred_set_policy; + rtnl_neigh_add; + rtnl_neigh_alloc; + rtnl_neigh_alloc_cache; + rtnl_neigh_build_add_request; + rtnl_neigh_build_delete_request; + rtnl_neigh_delete; + rtnl_neigh_flags2str; + rtnl_neigh_get; + rtnl_neigh_get_dst; + rtnl_neigh_get_family; + rtnl_neigh_get_flags; + rtnl_neigh_get_ifindex; + rtnl_neigh_get_lladdr; + rtnl_neigh_get_state; + rtnl_neigh_get_type; + rtnl_neigh_parse; + rtnl_neigh_put; + rtnl_neigh_set_dst; + rtnl_neigh_set_family; + rtnl_neigh_set_flags; + rtnl_neigh_set_ifindex; + rtnl_neigh_set_lladdr; + rtnl_neigh_set_state; + rtnl_neigh_set_type; + rtnl_neigh_state2str; + rtnl_neigh_str2flag; + rtnl_neigh_str2state; + rtnl_neigh_unset_flags; + rtnl_neigh_unset_state; + rtnl_neightbl_alloc; + rtnl_neightbl_alloc_cache; + rtnl_neightbl_build_change_request; + rtnl_neightbl_change; + rtnl_neightbl_get; + rtnl_neightbl_put; + rtnl_neightbl_set_anycast_delay; + rtnl_neightbl_set_app_probes; + rtnl_neightbl_set_base_reachable_time; + rtnl_neightbl_set_delay_probe_time; + rtnl_neightbl_set_dev; + rtnl_neightbl_set_family; + rtnl_neightbl_set_gc_interval; + rtnl_neightbl_set_gc_stale_time; + rtnl_neightbl_set_gc_tresh1; + rtnl_neightbl_set_gc_tresh2; + rtnl_neightbl_set_gc_tresh3; + rtnl_neightbl_set_locktime; + rtnl_neightbl_set_mcast_probes; + rtnl_neightbl_set_name; + rtnl_neightbl_set_proxy_delay; + rtnl_neightbl_set_proxy_queue_len; + rtnl_neightbl_set_queue_len; + rtnl_neightbl_set_retrans_time; + rtnl_neightbl_set_ucast_probes; + rtnl_netem_get_corruption_correlation; + rtnl_netem_get_corruption_probability; + rtnl_netem_get_delay; + rtnl_netem_get_delay_correlation; + rtnl_netem_get_delay_distribution; + rtnl_netem_get_delay_distribution_size; + rtnl_netem_get_duplicate; + rtnl_netem_get_duplicate_correlation; + rtnl_netem_get_gap; + rtnl_netem_get_jitter; + rtnl_netem_get_limit; + rtnl_netem_get_loss; + rtnl_netem_get_loss_correlation; + rtnl_netem_get_reorder_correlation; + rtnl_netem_get_reorder_probability; + rtnl_netem_set_corruption_correlation; + rtnl_netem_set_corruption_probability; + rtnl_netem_set_delay; + rtnl_netem_set_delay_correlation; + rtnl_netem_set_delay_distribution; + rtnl_netem_set_duplicate; + rtnl_netem_set_duplicate_correlation; + rtnl_netem_set_gap; + rtnl_netem_set_jitter; + rtnl_netem_set_limit; + rtnl_netem_set_loss; + rtnl_netem_set_loss_correlation; + rtnl_netem_set_reorder_correlation; + rtnl_netem_set_reorder_probability; + rtnl_pktloc_add; + rtnl_pktloc_alloc; + rtnl_pktloc_foreach; + rtnl_pktloc_lookup; + rtnl_pktloc_put; + rtnl_prio2str; + rtnl_qdisc_add; + rtnl_qdisc_alloc; + rtnl_qdisc_alloc_cache; + rtnl_qdisc_build_add_request; + rtnl_qdisc_build_change_request; + rtnl_qdisc_build_delete_request; + rtnl_qdisc_build_update_request; + rtnl_qdisc_change; + rtnl_qdisc_delete; + rtnl_qdisc_dsmark_get_default_index; + rtnl_qdisc_dsmark_get_indices; + rtnl_qdisc_dsmark_get_set_tc_index; + rtnl_qdisc_dsmark_set_default_index; + rtnl_qdisc_dsmark_set_indices; + rtnl_qdisc_dsmark_set_set_tc_index; + rtnl_qdisc_fifo_get_limit; + rtnl_qdisc_fifo_set_limit; + rtnl_qdisc_foreach_child; + rtnl_qdisc_foreach_cls; + rtnl_qdisc_fq_codel_get_ecn; + rtnl_qdisc_fq_codel_get_flows; + rtnl_qdisc_fq_codel_get_interval; + rtnl_qdisc_fq_codel_get_limit; + rtnl_qdisc_fq_codel_get_quantum; + rtnl_qdisc_fq_codel_get_target; + rtnl_qdisc_fq_codel_set_ecn; + rtnl_qdisc_fq_codel_set_flows; + rtnl_qdisc_fq_codel_set_interval; + rtnl_qdisc_fq_codel_set_limit; + rtnl_qdisc_fq_codel_set_quantum; + rtnl_qdisc_fq_codel_set_target; + rtnl_qdisc_get; + rtnl_qdisc_get_by_parent; + rtnl_qdisc_plug_buffer; + rtnl_qdisc_plug_release_indefinite; + rtnl_qdisc_plug_release_one; + rtnl_qdisc_plug_set_limit; + rtnl_qdisc_prio_get_bands; + rtnl_qdisc_prio_get_priomap; + rtnl_qdisc_prio_set_bands; + rtnl_qdisc_prio_set_priomap; + rtnl_qdisc_put; + rtnl_qdisc_tbf_get_limit; + rtnl_qdisc_tbf_get_peakrate; + rtnl_qdisc_tbf_get_peakrate_bucket; + rtnl_qdisc_tbf_get_peakrate_cell; + rtnl_qdisc_tbf_get_rate; + rtnl_qdisc_tbf_get_rate_bucket; + rtnl_qdisc_tbf_get_rate_cell; + rtnl_qdisc_tbf_set_limit; + rtnl_qdisc_tbf_set_limit_by_latency; + rtnl_qdisc_tbf_set_peakrate; + rtnl_qdisc_tbf_set_rate; + rtnl_qdisc_update; + rtnl_realms2str; + rtnl_red_get_limit; + rtnl_red_set_limit; + rtnl_route_add; + rtnl_route_add_nexthop; + rtnl_route_alloc; + rtnl_route_alloc_cache; + rtnl_route_build_add_request; + rtnl_route_build_del_request; + rtnl_route_build_msg; + rtnl_route_delete; + rtnl_route_foreach_nexthop; + rtnl_route_get; + rtnl_route_get_dst; + rtnl_route_get_family; + rtnl_route_get_flags; + rtnl_route_get_iif; + rtnl_route_get_metric; + rtnl_route_get_nexthops; + rtnl_route_get_nnexthops; + rtnl_route_get_pref_src; + rtnl_route_get_priority; + rtnl_route_get_protocol; + rtnl_route_get_scope; + rtnl_route_get_src; + rtnl_route_get_table; + rtnl_route_get_tos; + rtnl_route_get_type; + rtnl_route_guess_scope; + rtnl_route_metric2str; + rtnl_route_nexthop_n; + rtnl_route_nh_alloc; + rtnl_route_nh_clone; + rtnl_route_nh_compare; + rtnl_route_nh_dump; + rtnl_route_nh_flags2str; + rtnl_route_nh_free; + rtnl_route_nh_get_flags; + rtnl_route_nh_get_gateway; + rtnl_route_nh_get_ifindex; + rtnl_route_nh_get_realms; + rtnl_route_nh_get_weight; + rtnl_route_nh_set_flags; + rtnl_route_nh_set_gateway; + rtnl_route_nh_set_ifindex; + rtnl_route_nh_set_realms; + rtnl_route_nh_set_weight; + rtnl_route_nh_str2flags; + rtnl_route_nh_unset_flags; + rtnl_route_parse; + rtnl_route_proto2str; + rtnl_route_put; + rtnl_route_read_protocol_names; + rtnl_route_read_table_names; + rtnl_route_remove_nexthop; + rtnl_route_set_dst; + rtnl_route_set_family; + rtnl_route_set_flags; + rtnl_route_set_iif; + rtnl_route_set_metric; + rtnl_route_set_pref_src; + rtnl_route_set_priority; + rtnl_route_set_protocol; + rtnl_route_set_scope; + rtnl_route_set_src; + rtnl_route_set_table; + rtnl_route_set_tos; + rtnl_route_set_type; + rtnl_route_str2metric; + rtnl_route_str2proto; + rtnl_route_str2table; + rtnl_route_table2str; + rtnl_route_unset_flags; + rtnl_route_unset_metric; + rtnl_rule_add; + rtnl_rule_alloc; + rtnl_rule_alloc_cache; + rtnl_rule_build_add_request; + rtnl_rule_build_delete_request; + rtnl_rule_delete; + rtnl_rule_get_action; + rtnl_rule_get_dsfield; + rtnl_rule_get_dst; + rtnl_rule_get_family; + rtnl_rule_get_goto; + rtnl_rule_get_iif; + rtnl_rule_get_mark; + rtnl_rule_get_mask; + rtnl_rule_get_oif; + rtnl_rule_get_prio; + rtnl_rule_get_realms; + rtnl_rule_get_src; + rtnl_rule_get_table; + rtnl_rule_put; + rtnl_rule_set_action; + rtnl_rule_set_dsfield; + rtnl_rule_set_dst; + rtnl_rule_set_family; + rtnl_rule_set_goto; + rtnl_rule_set_iif; + rtnl_rule_set_mark; + rtnl_rule_set_mask; + rtnl_rule_set_oif; + rtnl_rule_set_prio; + rtnl_rule_set_realms; + rtnl_rule_set_src; + rtnl_rule_set_table; + rtnl_scope2str; + rtnl_sfq_get_divisor; + rtnl_sfq_get_limit; + rtnl_sfq_get_perturb; + rtnl_sfq_get_quantum; + rtnl_sfq_set_limit; + rtnl_sfq_set_perturb; + rtnl_sfq_set_quantum; + rtnl_str2prio; + rtnl_str2scope; + rtnl_tc_calc_bufsize; + rtnl_tc_calc_cell_log; + rtnl_tc_calc_txtime; + rtnl_tc_get_handle; + rtnl_tc_get_ifindex; + rtnl_tc_get_kind; + rtnl_tc_get_link; + rtnl_tc_get_linktype; + rtnl_tc_get_mpu; + rtnl_tc_get_mtu; + rtnl_tc_get_overhead; + rtnl_tc_get_parent; + rtnl_tc_get_stat; + rtnl_tc_handle2str; + rtnl_tc_read_classid_file; + rtnl_tc_set_handle; + rtnl_tc_set_ifindex; + rtnl_tc_set_kind; + rtnl_tc_set_link; + rtnl_tc_set_linktype; + rtnl_tc_set_mpu; + rtnl_tc_set_mtu; + rtnl_tc_set_overhead; + rtnl_tc_set_parent; + rtnl_tc_str2handle; + rtnl_u32_add_action; + rtnl_u32_add_key; + rtnl_u32_add_key_in6_addr; + rtnl_u32_add_key_in_addr; + rtnl_u32_add_key_uint16; + rtnl_u32_add_key_uint32; + rtnl_u32_add_key_uint8; + rtnl_u32_del_action; + rtnl_u32_get_key; + rtnl_u32_set_classid; + rtnl_u32_set_cls_terminal; + rtnl_u32_set_divisor; + rtnl_u32_set_flags; + rtnl_u32_set_handle; + rtnl_u32_set_hashmask; + rtnl_u32_set_hashtable; + rtnl_u32_set_link; + + # The following symbols were added during the development of 3.2.26. + # Keep them in libnl_3 to avoid breaking users. + rtnl_class_hfsc_get_fsc; + rtnl_class_hfsc_get_rsc; + rtnl_class_hfsc_get_usc; + rtnl_class_hfsc_set_fsc; + rtnl_class_hfsc_set_rsc; + rtnl_class_hfsc_set_usc; + rtnl_link_inet6_addrgenmode2str; + rtnl_link_inet6_get_addr_gen_mode; + rtnl_link_inet6_get_token; + rtnl_link_inet6_set_addr_gen_mode; + rtnl_link_inet6_set_token; + rtnl_link_inet6_str2addrgenmode; + rtnl_qdisc_hfsc_get_defcls; + rtnl_qdisc_hfsc_set_defcls; + rtnl_u32_add_mark; + rtnl_u32_del_mark; + +local: + *; +}; + +libnl_3_2_26 { +global: + rtnl_neigh_get_vlan; + rtnl_neigh_set_vlan; + rtnl_skbedit_get_action; + rtnl_skbedit_get_mark; + rtnl_skbedit_get_priority; + rtnl_skbedit_get_queue_mapping; + rtnl_skbedit_set_action; + rtnl_skbedit_set_mark; + rtnl_skbedit_set_priority; + rtnl_skbedit_set_queue_mapping; + rtnl_tc_stat2str; + rtnl_tc_str2stat; + rtnl_u32_get_classid; +} libnl_3; + +libnl_3_2_27 { +global: + rtnl_link_get_link_netnsid; + rtnl_link_ipvlan_alloc; + rtnl_link_is_ipvlan; + rtnl_link_ipvlan_mode2str; + rtnl_link_ipvlan_str2mode; + rtnl_link_ipvlan_set_mode; + rtnl_link_ipvlan_get_mode; + rtnl_link_set_link_netnsid; +} libnl_3_2_26; + +libnl_3_2_28 { +global: + rtnl_link_alloc_cache_flags; + rtnl_link_bridge_get_port_vlan; + rtnl_link_bridge_has_vlan; + rtnl_link_bridge_pvid; + rtnl_link_is_macvtap; + rtnl_link_is_vrf; + rtnl_link_ipgretap_add; + rtnl_link_ipgretap_alloc; + rtnl_link_macsec_alloc; + rtnl_link_macsec_set_sci; + rtnl_link_macsec_get_sci; + rtnl_link_macsec_set_port; + rtnl_link_macsec_get_port; + rtnl_link_macsec_set_cipher_suite; + rtnl_link_macsec_get_cipher_suite; + rtnl_link_macsec_set_icv_len; + rtnl_link_macsec_get_icv_len; + rtnl_link_macsec_set_protect; + rtnl_link_macsec_get_protect; + rtnl_link_macsec_set_encrypt; + rtnl_link_macsec_get_encrypt; + rtnl_link_macsec_set_encoding_sa; + rtnl_link_macsec_get_encoding_sa; + rtnl_link_macsec_set_validation_type; + rtnl_link_macsec_get_validation_type; + rtnl_link_macsec_set_replay_protect; + rtnl_link_macsec_get_replay_protect; + rtnl_link_macsec_set_window; + rtnl_link_macsec_get_window; + rtnl_link_macsec_set_send_sci; + rtnl_link_macsec_get_send_sci; + rtnl_link_macsec_set_end_station; + rtnl_link_macsec_get_end_station; + rtnl_link_macsec_set_scb; + rtnl_link_macsec_get_scb; + rtnl_link_macvtap_alloc; + rtnl_link_macvtap_flags2str; + rtnl_link_macvtap_get_flags; + rtnl_link_macvtap_get_mode; + rtnl_link_macvtap_mode2str; + rtnl_link_macvtap_set_flags; + rtnl_link_macvtap_set_mode; + rtnl_link_macvtap_str2flags; + rtnl_link_macvtap_str2mode; + rtnl_link_macvtap_unset_flags; + rtnl_link_sit_get_ip6rd_prefix; + rtnl_link_sit_get_ip6rd_prefixlen; + rtnl_link_sit_get_ip6rd_relay_prefix; + rtnl_link_sit_get_ip6rd_relay_prefixlen; + rtnl_link_sit_set_ip6rd_prefix; + rtnl_link_sit_set_ip6rd_prefixlen; + rtnl_link_sit_set_ip6rd_relay_prefix; + rtnl_link_sit_set_ip6rd_relay_prefixlen; + rtnl_link_vrf_alloc; + rtnl_link_vrf_get_tableid; + rtnl_link_vrf_set_tableid; + rtnl_neigh_alloc_cache_flags; +} libnl_3_2_27; + +libnl_3_2_29 { +global: + rtnl_gact_set_action; + rtnl_gact_get_action; + rtnl_link_bridge_portstate2str; + rtnl_link_bridge_str2portstate; + rtnl_link_bridge_set_self; + rtnl_link_bridge_get_hwmode; + rtnl_link_bridge_set_hwmode; + rtnl_link_bridge_hwmode2str; + rtnl_link_bridge_str2hwmode; + rtnl_link_get_carrier_changes; + rtnl_link_get_gso_max_segs; + rtnl_link_get_gso_max_size; + rtnl_link_get_phys_port_name; + rtnl_link_get_phys_switch_id; + rtnl_link_ipgre_get_pmtudisc; + rtnl_link_is_ipgretap; + rtnl_link_macvlan_add_macaddr; + rtnl_link_macvlan_count_macaddr; + rtnl_link_macvlan_del_macaddr; + rtnl_link_macvlan_get_macaddr; + rtnl_link_macvlan_get_macmode; + rtnl_link_macvlan_macmode2str; + rtnl_link_macvlan_set_macmode; + rtnl_link_macvlan_str2macmode; + rtnl_link_ppp_alloc; + rtnl_link_ppp_set_fd; + rtnl_link_ppp_get_fd; + rtnl_link_vxlan_get_collect_metadata; + rtnl_link_vxlan_get_flags; + rtnl_link_vxlan_get_label; + rtnl_link_vxlan_get_port; + rtnl_link_vxlan_get_remcsum_rx; + rtnl_link_vxlan_get_remcsum_tx; + rtnl_link_vxlan_get_udp_csum; + rtnl_link_vxlan_get_udp_zero_csum6_rx; + rtnl_link_vxlan_get_udp_zero_csum6_tx; + rtnl_link_vxlan_set_collect_metadata; + rtnl_link_vxlan_set_flags; + rtnl_link_vxlan_set_label; + rtnl_link_vxlan_set_port; + rtnl_link_vxlan_set_remcsum_rx; + rtnl_link_vxlan_set_remcsum_tx; + rtnl_link_vxlan_set_udp_csum; + rtnl_link_vxlan_set_udp_zero_csum6_rx; + rtnl_link_vxlan_set_udp_zero_csum6_tx; + rtnl_link_has_vf_list; + rtnl_link_set_vf_list; + rtnl_link_unset_vf_list; + rtnl_link_vf_add; + rtnl_link_vf_alloc; + rtnl_link_vf_free; + rtnl_link_vf_get; + rtnl_link_vf_put; + rtnl_link_vf_get_addr; + rtnl_link_vf_set_addr; + rtnl_link_vf_set_ib_node_guid; + rtnl_link_vf_set_ib_port_guid; + rtnl_link_vf_get_index; + rtnl_link_vf_set_index; + rtnl_link_vf_get_linkstate; + rtnl_link_vf_set_linkstate; + rtnl_link_vf_get_rate; + rtnl_link_vf_set_rate; + rtnl_link_vf_get_rss_query_en; + rtnl_link_vf_set_rss_query_en; + rtnl_link_vf_get_spoofchk; + rtnl_link_vf_set_spoofchk; + rtnl_link_vf_get_stat; + rtnl_link_vf_get_trust; + rtnl_link_vf_set_trust; + rtnl_link_vf_get_vlans; + rtnl_link_vf_set_vlans; + rtnl_link_vf_vlan_alloc; + rtnl_link_vf_vlan_free; + rtnl_link_vf_vlan_put; + rtnl_link_vf_linkstate2str; + rtnl_link_vf_str2linkstate; + rtnl_link_vf_vlanproto2str; + rtnl_link_vf_str2vlanproto; + rtnl_link_vf_str2guid; + rtnl_u32_set_selector; +} libnl_3_2_28; + +libnl_3_4 { +global: + rtnl_act_next; + rtnl_basic_get_action; + rtnl_link_inet6_flags2str; + rtnl_link_inet6_get_flags; + rtnl_link_inet6_set_flags; + rtnl_link_inet6_str2flags; + rtnl_netconf_get_all; + rtnl_netconf_get_by_idx; + rtnl_netconf_get_default; + rtnl_netconf_get_family; + rtnl_netconf_get_forwarding; + rtnl_netconf_get_ifindex; + rtnl_netconf_get_input; + rtnl_netconf_get_mc_forwarding; + rtnl_netconf_get_rp_filter; + rtnl_netconf_put; + rtnl_rule_get_l3mdev; + rtnl_rule_set_l3mdev; + rtnl_u32_get_action; + rtnl_route_nh_set_newdst; + rtnl_route_nh_get_newdst; + rtnl_route_nh_set_via; + rtnl_route_nh_get_via; + rtnl_route_set_ttl_propagate; + rtnl_route_get_ttl_propagate; + rtnl_route_nh_encap_mpls; +} libnl_3_2_29; + +libnl_3_5 { +global: + rtnl_class_get_by_parent; + rtnl_cls_cache_set_tc_params; + rtnl_ematch_tree_clone; + rtnl_htb_get_ceil64; + rtnl_htb_get_rate64; + rtnl_htb_set_ceil64; + rtnl_htb_set_rate64; + rtnl_link_geneve_alloc; + rtnl_link_geneve_get_flags; + rtnl_link_geneve_get_id; + rtnl_link_geneve_get_label; + rtnl_link_geneve_get_port; + rtnl_link_geneve_get_remote; + rtnl_link_geneve_get_tos; + rtnl_link_geneve_get_ttl; + rtnl_link_geneve_get_udp_csum; + rtnl_link_geneve_get_udp_zero_csum6_rx; + rtnl_link_geneve_get_udp_zero_csum6_tx; + rtnl_link_geneve_set_flags; + rtnl_link_geneve_set_id; + rtnl_link_geneve_set_label; + rtnl_link_geneve_set_port; + rtnl_link_geneve_set_remote; + rtnl_link_geneve_set_tos; + rtnl_link_geneve_set_ttl; + rtnl_link_geneve_set_udp_csum; + rtnl_link_geneve_set_udp_zero_csum6_rx; + rtnl_link_geneve_set_udp_zero_csum6_tx; + rtnl_link_get_slave_type; + rtnl_link_is_geneve; + rtnl_link_is_xfrmi; + rtnl_link_set_slave_type; + rtnl_link_xfrmi_alloc; + rtnl_link_xfrmi_get_if_id; + rtnl_link_xfrmi_get_link; + rtnl_link_xfrmi_set_if_id; + rtnl_link_xfrmi_set_link; + rtnl_mall_append_action; + rtnl_mall_del_action; + rtnl_mall_get_classid; + rtnl_mall_get_first_action; + rtnl_mall_get_flags; + rtnl_mall_set_classid; + rtnl_mall_set_flags; + rtnl_neigh_get_by_vlan; + rtnl_neigh_get_master; + rtnl_neigh_set_master; + rtnl_netem_set_delay_distribution_data; + rtnl_qdisc_mqprio_get_hw_offload; + rtnl_qdisc_mqprio_get_max_rate; + rtnl_qdisc_mqprio_get_min_rate; + rtnl_qdisc_mqprio_get_mode; + rtnl_qdisc_mqprio_get_num_tc; + rtnl_qdisc_mqprio_get_priomap; + rtnl_qdisc_mqprio_get_queue; + rtnl_qdisc_mqprio_get_shaper; + rtnl_qdisc_mqprio_hw_offload; + rtnl_qdisc_mqprio_set_max_rate; + rtnl_qdisc_mqprio_set_min_rate; + rtnl_qdisc_mqprio_set_mode; + rtnl_qdisc_mqprio_set_num_tc; + rtnl_qdisc_mqprio_set_priomap; + rtnl_qdisc_mqprio_set_queue; + rtnl_qdisc_mqprio_set_shaper; + rtnl_rule_get_dport; + rtnl_rule_get_ipproto; + rtnl_rule_get_protocol; + rtnl_rule_get_sport; + rtnl_rule_set_dport; + rtnl_rule_set_dport_range; + rtnl_rule_set_ipproto; + rtnl_rule_set_protocol; + rtnl_rule_set_sport; + rtnl_rule_set_sport_range; + rtnl_tc_get_chain; + rtnl_tc_set_chain; + rtnl_vlan_get_action; + rtnl_vlan_get_mode; + rtnl_vlan_get_protocol; + rtnl_vlan_get_vlan_id; + rtnl_vlan_get_vlan_prio; + rtnl_vlan_set_action; + rtnl_vlan_set_mode; + rtnl_vlan_set_protocol; + rtnl_vlan_set_vlan_id; + rtnl_vlan_set_vlan_prio; +} libnl_3_4; + +libnl_3_6 { +global: + rtnl_cls_find_by_handle; + rtnl_cls_find_by_prio; + rtnl_flower_append_action; + rtnl_flower_del_action; + rtnl_flower_get_action; + rtnl_flower_get_dst_mac; + rtnl_flower_get_ip_dscp; + rtnl_flower_get_proto; + rtnl_flower_get_src_mac; + rtnl_flower_get_vlan_id; + rtnl_flower_get_vlan_prio; + rtnl_flower_set_dst_mac; + rtnl_flower_set_flags; + rtnl_flower_set_ip_dscp; + rtnl_flower_set_proto; + rtnl_flower_set_src_mac; + rtnl_flower_set_vlan_ethtype; + rtnl_flower_set_vlan_id; + rtnl_flower_set_vlan_prio; + rtnl_link_can_get_data_bittiming; + rtnl_link_can_get_data_bittiming_const; + rtnl_link_can_set_data_bittiming; + rtnl_link_can_set_data_bittiming_const; + rtnl_link_ip6_tnl_get_fwmark; + rtnl_link_ip6_tnl_set_fwmark; + rtnl_link_ip6gre_add; + rtnl_link_ip6gre_alloc; + rtnl_link_ip6gre_get_encaplimit; + rtnl_link_ip6gre_get_flags; + rtnl_link_ip6gre_get_flowinfo; + rtnl_link_ip6gre_get_fwmark; + rtnl_link_ip6gre_get_iflags; + rtnl_link_ip6gre_get_ikey; + rtnl_link_ip6gre_get_link; + rtnl_link_ip6gre_get_local; + rtnl_link_ip6gre_get_oflags; + rtnl_link_ip6gre_get_okey; + rtnl_link_ip6gre_get_remote; + rtnl_link_ip6gre_get_ttl; + rtnl_link_ip6gre_set_encaplimit; + rtnl_link_ip6gre_set_flags; + rtnl_link_ip6gre_set_flowinfo; + rtnl_link_ip6gre_set_fwmark; + rtnl_link_ip6gre_set_iflags; + rtnl_link_ip6gre_set_ikey; + rtnl_link_ip6gre_set_link; + rtnl_link_ip6gre_set_local; + rtnl_link_ip6gre_set_oflags; + rtnl_link_ip6gre_set_okey; + rtnl_link_ip6gre_set_remote; + rtnl_link_ip6gre_set_ttl; + rtnl_link_ip6vti_add; + rtnl_link_ip6vti_alloc; + rtnl_link_ip6vti_get_fwmark; + rtnl_link_ip6vti_get_ikey; + rtnl_link_ip6vti_get_link; + rtnl_link_ip6vti_get_local; + rtnl_link_ip6vti_get_okey; + rtnl_link_ip6vti_get_remote; + rtnl_link_ip6vti_set_fwmark; + rtnl_link_ip6vti_set_ikey; + rtnl_link_ip6vti_set_link; + rtnl_link_ip6vti_set_local; + rtnl_link_ip6vti_set_okey; + rtnl_link_ip6vti_set_remote; + rtnl_link_ipgre_get_fwmark; + rtnl_link_ipgre_set_fwmark; + rtnl_link_ipip_get_fwmark; + rtnl_link_ipip_set_fwmark; + rtnl_link_ipvti_get_fwmark; + rtnl_link_ipvti_set_fwmark; + rtnl_link_is_ip6gre; + rtnl_link_is_ip6vti; + rtnl_link_macsec_get_offload; + rtnl_link_macsec_set_offload; + rtnl_link_sit_get_fwmark; + rtnl_link_sit_set_fwmark; + rtnl_link_team_add; + rtnl_link_team_alloc; + rtnl_mdb_add_entry; + rtnl_mdb_alloc_cache; + rtnl_mdb_alloc_cache_flags; + rtnl_mdb_entry_get_addr; + rtnl_mdb_entry_get_ifindex; + rtnl_mdb_entry_get_proto; + rtnl_mdb_entry_get_state; + rtnl_mdb_entry_get_vid; + rtnl_mdb_foreach_entry; + rtnl_mdb_get_ifindex; + rtnl_netconf_alloc_cache; + rtnl_netconf_get_ignore_routes_linkdown; + rtnl_netconf_get_proxy_neigh; + rtnl_qdisc_get_by_kind; + rtnl_route_lookup; + rtnl_route_nh_get_encap_mpls_dst; + rtnl_route_nh_get_encap_mpls_ttl; +} libnl_3_5; + +libnl_3_7 { +global: + rtnl_flower_get_ipv4_dst; + rtnl_flower_get_ipv4_src; + rtnl_flower_set_ipv4_dst; + rtnl_flower_set_ipv4_src; + rtnl_nat_get_action; + rtnl_nat_get_flags; + rtnl_nat_get_mask; + rtnl_nat_get_new_addr; + rtnl_nat_get_old_addr; + rtnl_nat_set_action; + rtnl_nat_set_flags; + rtnl_nat_set_mask; + rtnl_nat_set_new_addr; + rtnl_nat_set_old_addr; +} libnl_3_6; + +libnl_3_8 { +global: + rtnl_link_bond_set_activeslave; + rtnl_link_bond_set_mode; + rtnl_link_bridge_get_vlan_filtering; + rtnl_link_bridge_get_vlan_protocol; + rtnl_link_bridge_get_vlan_stats_enabled; + rtnl_link_bridge_set_vlan_filtering; + rtnl_link_bridge_set_vlan_protocol; + rtnl_link_bridge_set_vlan_stats_enabled; + rtnl_link_inet6_get_conf; + rtnl_link_info_ops_get; + rtnl_neigh_get_nhid; + rtnl_neigh_set_nhid; + rtnl_nh_alloc; + rtnl_nh_alloc_cache; + rtnl_nh_get; + rtnl_nh_get_fdb; + rtnl_nh_get_gateway; + rtnl_nh_get_group_entry; + rtnl_nh_get_group_size; + rtnl_nh_get_id; + rtnl_nh_put; + rtnl_nh_set_fdb; + rtnl_nh_set_gateway; +} libnl_3_7; + +libnl_3_9 { +global: + rtnl_link_bond_set_hashing_type; + rtnl_link_bond_set_miimon; + rtnl_link_bond_set_min_links; + rtnl_link_can_get_device_stats; +} libnl_3_8; + +libnl_3_10 { +global: + rtnl_link_bridge_enable_vlan; + rtnl_link_bridge_get_ageing_time; + rtnl_link_bridge_get_vlan_default_pvid; + rtnl_link_bridge_set_ageing_time; + rtnl_link_bridge_set_master; + rtnl_link_bridge_set_nf_call_arptables; + rtnl_link_bridge_set_nf_call_ip6tables; + rtnl_link_bridge_set_nf_call_iptables; + rtnl_link_bridge_set_port_vlan_map_range; + rtnl_link_bridge_set_port_vlan_pvid; + rtnl_link_bridge_unset_port_vlan_map_range; + rtnl_route_get_nhid; + rtnl_route_nh_identical; + rtnl_route_set_nhid; +} libnl_3_9; + +libnl_3_11 { +global: + rtnl_link_bond_get_activeslave; + rtnl_link_bond_get_hashing_type; + rtnl_link_bond_get_miimon; + rtnl_link_bond_get_min_links; + rtnl_link_bond_get_mode; + rtnl_link_bridge_clear_mst_port_state_info; + rtnl_link_bridge_del_mst_port_state; + rtnl_link_bridge_foreach_mst_entry; + rtnl_link_bridge_get_boolopt; + rtnl_link_bridge_get_mcast_router; + rtnl_link_bridge_get_mcast_snooping; + rtnl_link_bridge_get_mst_port_state; + rtnl_link_bridge_get_nf_call_arptables; + rtnl_link_bridge_get_nf_call_ip6tables; + rtnl_link_bridge_get_nf_call_iptables; + rtnl_link_bridge_get_stp_state; + rtnl_link_bridge_set_boolopt; + rtnl_link_bridge_set_mcast_router; + rtnl_link_bridge_set_mcast_snooping; + rtnl_link_bridge_set_mst_port_state; + rtnl_link_bridge_set_stp_state; + rtnl_link_bridge_set_vlan_default_pvid; + rtnl_link_get_perm_addr; + rtnl_neigh_extflags2str; + rtnl_neigh_get_ext_flags; + rtnl_neigh_set_ext_flags; + rtnl_neigh_str2extflag; + rtnl_neigh_unset_ext_flags; +} libnl_3_10; diff --git a/libnl/libnl-xfrm-3.0.pc.in b/libnl/libnl-xfrm-3.0.pc.in new file mode 100644 index 0000000..48ffb70 --- /dev/null +++ b/libnl/libnl-xfrm-3.0.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libnl-xfrm +Description: Netlink Routing Family Library +Version: @PACKAGE_VERSION@ +Requires: libnl-3.0 +Libs: -L${libdir} -lnl-xfrm-@MAJ_VERSION@ +Cflags: -I${includedir}/libnl@MAJ_VERSION@ diff --git a/libnl/libnl-xfrm-3.sym b/libnl/libnl-xfrm-3.sym new file mode 100644 index 0000000..ed7c7ad --- /dev/null +++ b/libnl/libnl-xfrm-3.sym @@ -0,0 +1,257 @@ +# The linker version script controls which symbols are exported. +# Version tags that correspond to past releases group the symbols +# that were part of that release. Hence, such a version tag is sealed +# and MUST not be extended. +# Any new symbols MUST only be added to the bottom of the file, in +# the version tag for the next upcoming release. +libnl_3 { +global: + xfrmnl_ae_alloc; + xfrmnl_ae_build_get_request; + xfrmnl_ae_flags2str; + xfrmnl_ae_get_curlifetime; + xfrmnl_ae_get_daddr; + xfrmnl_ae_get_family; + xfrmnl_ae_get_flags; + xfrmnl_ae_get_kernel; + xfrmnl_ae_get_mark; + xfrmnl_ae_get_proto; + xfrmnl_ae_get_replay_maxage; + xfrmnl_ae_get_replay_maxdiff; + xfrmnl_ae_get_replay_state; + xfrmnl_ae_get_replay_state_esn; + xfrmnl_ae_get_reqid; + xfrmnl_ae_get_saddr; + xfrmnl_ae_get_spi; + xfrmnl_ae_parse; + xfrmnl_ae_put; + xfrmnl_ae_set; + xfrmnl_ae_set_curlifetime; + xfrmnl_ae_set_daddr; + xfrmnl_ae_set_family; + xfrmnl_ae_set_flags; + xfrmnl_ae_set_mark; + xfrmnl_ae_set_proto; + xfrmnl_ae_set_replay_maxage; + xfrmnl_ae_set_replay_maxdiff; + xfrmnl_ae_set_replay_state; + xfrmnl_ae_set_replay_state_esn; + xfrmnl_ae_set_reqid; + xfrmnl_ae_set_saddr; + xfrmnl_ae_set_spi; + xfrmnl_ae_str2flag; + xfrmnl_ltime_cfg_alloc; + xfrmnl_ltime_cfg_clone; + xfrmnl_ltime_cfg_cmp; + xfrmnl_ltime_cfg_get; + xfrmnl_ltime_cfg_get_hard_addexpires; + xfrmnl_ltime_cfg_get_hard_bytelimit; + xfrmnl_ltime_cfg_get_hard_packetlimit; + xfrmnl_ltime_cfg_get_hard_useexpires; + xfrmnl_ltime_cfg_get_soft_addexpires; + xfrmnl_ltime_cfg_get_soft_bytelimit; + xfrmnl_ltime_cfg_get_soft_packetlimit; + xfrmnl_ltime_cfg_get_soft_useexpires; + xfrmnl_ltime_cfg_put; + xfrmnl_ltime_cfg_set_hard_addexpires; + xfrmnl_ltime_cfg_set_hard_bytelimit; + xfrmnl_ltime_cfg_set_hard_packetlimit; + xfrmnl_ltime_cfg_set_hard_useexpires; + xfrmnl_ltime_cfg_set_soft_addexpires; + xfrmnl_ltime_cfg_set_soft_bytelimit; + xfrmnl_ltime_cfg_set_soft_packetlimit; + xfrmnl_ltime_cfg_set_soft_useexpires; + xfrmnl_ltime_cfg_shared; + xfrmnl_sa_add; + xfrmnl_sa_alloc; + xfrmnl_sa_alloc_cache; + xfrmnl_sa_build_add_request; + xfrmnl_sa_build_delete_request; + xfrmnl_sa_build_get_request; + xfrmnl_sa_build_update_request; + xfrmnl_sa_delete; + xfrmnl_sa_flags2str; + xfrmnl_sa_get; + xfrmnl_sa_get_aead_params; + xfrmnl_sa_get_auth_params; + xfrmnl_sa_get_coaddr; + xfrmnl_sa_get_comp_params; + xfrmnl_sa_get_crypto_params; + xfrmnl_sa_get_curlifetime; + xfrmnl_sa_get_daddr; + xfrmnl_sa_get_encap_tmpl; + xfrmnl_sa_get_family; + xfrmnl_sa_get_flags; + xfrmnl_sa_get_kernel; + xfrmnl_sa_get_lifetime_cfg; + xfrmnl_sa_get_mark; + xfrmnl_sa_get_mode; + xfrmnl_sa_get_proto; + xfrmnl_sa_get_replay_maxage; + xfrmnl_sa_get_replay_maxdiff; + xfrmnl_sa_get_replay_state; + xfrmnl_sa_get_replay_state_esn; + xfrmnl_sa_get_replay_window; + xfrmnl_sa_get_reqid; + xfrmnl_sa_get_saddr; + xfrmnl_sa_get_sec_ctx; + xfrmnl_sa_get_sel; + xfrmnl_sa_get_seq; + xfrmnl_sa_get_spi; + xfrmnl_sa_get_stats; + xfrmnl_sa_get_tfcpad; + xfrmnl_sa_is_expiry_reached; + xfrmnl_sa_is_hardexpiry_reached; + xfrmnl_sa_mode2str; + xfrmnl_sa_parse; + xfrmnl_sa_put; + xfrmnl_sa_set_aead_params; + xfrmnl_sa_set_auth_params; + xfrmnl_sa_set_coaddr; + xfrmnl_sa_set_comp_params; + xfrmnl_sa_set_crypto_params; + xfrmnl_sa_set_daddr; + xfrmnl_sa_set_encap_tmpl; + xfrmnl_sa_set_family; + xfrmnl_sa_set_flags; + xfrmnl_sa_set_lifetime_cfg; + xfrmnl_sa_set_mark; + xfrmnl_sa_set_mode; + xfrmnl_sa_set_proto; + xfrmnl_sa_set_replay_maxage; + xfrmnl_sa_set_replay_maxdiff; + xfrmnl_sa_set_replay_state; + xfrmnl_sa_set_replay_state_esn; + xfrmnl_sa_set_replay_window; + xfrmnl_sa_set_reqid; + xfrmnl_sa_set_saddr; + xfrmnl_sa_set_sec_ctx; + xfrmnl_sa_set_sel; + xfrmnl_sa_set_spi; + xfrmnl_sa_set_tfcpad; + xfrmnl_sa_str2flag; + xfrmnl_sa_str2mode; + xfrmnl_sa_update; + xfrmnl_sel_alloc; + xfrmnl_sel_clone; + xfrmnl_sel_cmp; + xfrmnl_sel_dump; + xfrmnl_sel_get; + xfrmnl_sel_get_daddr; + xfrmnl_sel_get_dport; + xfrmnl_sel_get_dportmask; + xfrmnl_sel_get_family; + xfrmnl_sel_get_ifindex; + xfrmnl_sel_get_prefixlen_d; + xfrmnl_sel_get_prefixlen_s; + xfrmnl_sel_get_proto; + xfrmnl_sel_get_saddr; + xfrmnl_sel_get_sport; + xfrmnl_sel_get_sportmask; + xfrmnl_sel_get_userid; + xfrmnl_sel_put; + xfrmnl_sel_set_daddr; + xfrmnl_sel_set_dport; + xfrmnl_sel_set_dportmask; + xfrmnl_sel_set_family; + xfrmnl_sel_set_ifindex; + xfrmnl_sel_set_prefixlen_d; + xfrmnl_sel_set_prefixlen_s; + xfrmnl_sel_set_proto; + xfrmnl_sel_set_saddr; + xfrmnl_sel_set_sport; + xfrmnl_sel_set_sportmask; + xfrmnl_sel_set_userid; + xfrmnl_sel_shared; + xfrmnl_sp_action2str; + xfrmnl_sp_add; + xfrmnl_sp_add_usertemplate; + xfrmnl_sp_alloc; + xfrmnl_sp_alloc_cache; + xfrmnl_sp_build_add_request; + xfrmnl_sp_build_delete_request; + xfrmnl_sp_build_get_request; + xfrmnl_sp_build_update_request; + xfrmnl_sp_delete; + xfrmnl_sp_dir2str; + xfrmnl_sp_flags2str; + xfrmnl_sp_foreach_usertemplate; + xfrmnl_sp_get; + xfrmnl_sp_get_action; + xfrmnl_sp_get_curlifetime; + xfrmnl_sp_get_dir; + xfrmnl_sp_get_flags; + xfrmnl_sp_get_index; + xfrmnl_sp_get_kernel; + xfrmnl_sp_get_lifetime_cfg; + xfrmnl_sp_get_mark; + xfrmnl_sp_get_nusertemplates; + xfrmnl_sp_get_priority; + xfrmnl_sp_get_sec_ctx; + xfrmnl_sp_get_sel; + xfrmnl_sp_get_share; + xfrmnl_sp_get_userpolicy_type; + xfrmnl_sp_get_usertemplates; + xfrmnl_sp_index2dir; + xfrmnl_sp_parse; + xfrmnl_sp_put; + xfrmnl_sp_remove_usertemplate; + xfrmnl_sp_set_action; + xfrmnl_sp_set_dir; + xfrmnl_sp_set_flags; + xfrmnl_sp_set_index; + xfrmnl_sp_set_lifetime_cfg; + xfrmnl_sp_set_mark; + xfrmnl_sp_set_priority; + xfrmnl_sp_set_sec_ctx; + xfrmnl_sp_set_sel; + xfrmnl_sp_set_share; + xfrmnl_sp_set_userpolicy_type; + xfrmnl_sp_share2str; + xfrmnl_sp_str2action; + xfrmnl_sp_str2dir; + xfrmnl_sp_str2flag; + xfrmnl_sp_str2share; + xfrmnl_sp_str2type; + xfrmnl_sp_type2str; + xfrmnl_sp_update; + xfrmnl_sp_usertemplate_n; + xfrmnl_user_tmpl_alloc; + xfrmnl_user_tmpl_clone; + xfrmnl_user_tmpl_cmp; + xfrmnl_user_tmpl_dump; + xfrmnl_user_tmpl_free; + xfrmnl_user_tmpl_get_aalgos; + xfrmnl_user_tmpl_get_calgos; + xfrmnl_user_tmpl_get_daddr; + xfrmnl_user_tmpl_get_ealgos; + xfrmnl_user_tmpl_get_family; + xfrmnl_user_tmpl_get_mode; + xfrmnl_user_tmpl_get_optional; + xfrmnl_user_tmpl_get_proto; + xfrmnl_user_tmpl_get_reqid; + xfrmnl_user_tmpl_get_saddr; + xfrmnl_user_tmpl_get_share; + xfrmnl_user_tmpl_get_spi; + xfrmnl_user_tmpl_mode2str; + xfrmnl_user_tmpl_set_aalgos; + xfrmnl_user_tmpl_set_calgos; + xfrmnl_user_tmpl_set_daddr; + xfrmnl_user_tmpl_set_ealgos; + xfrmnl_user_tmpl_set_family; + xfrmnl_user_tmpl_set_mode; + xfrmnl_user_tmpl_set_optional; + xfrmnl_user_tmpl_set_proto; + xfrmnl_user_tmpl_set_reqid; + xfrmnl_user_tmpl_set_saddr; + xfrmnl_user_tmpl_set_share; + xfrmnl_user_tmpl_set_spi; + xfrmnl_user_tmpl_str2mode; +local: + *; +}; + +libnl_3_6 { + xfrmnl_sa_get_user_offload; + xfrmnl_sa_set_user_offload; +} libnl_3; diff --git a/libnl/m4/.gitignore b/libnl/m4/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/libnl/man/genl-ctrl-list.8 b/libnl/man/genl-ctrl-list.8 new file mode 100644 index 0000000..6132475 --- /dev/null +++ b/libnl/man/genl-ctrl-list.8 @@ -0,0 +1,104 @@ +.TH genl\-ctrl-list 8 "20 April 2012" "libnl" +.SH NAME +genl\-ctrl\-list \- List available kernel-side Generic Netlink families +.SH SYNOPSIS +.B genl\-ctrl\-list [-d] + +.SH DESCRIPTION +.PP +Queries the Generic Netlink controller in kernel and prints a list of all +registered Generic Netlink families including the version of the interface +that has been registered. + +.SH OPTIONS +.TP +.BR \-\^h " or " \-\-help +Print help text to console and exit. +.TP +.BR \-\^v " or " \-\-version +Print versioning information to console and exit. +.TP +.BR \-\^d " or " \-\-details +Include additional detailed information for each Generic Netlink +family that is printed. + +The information includes: + +.RS +.TP +.B hdrsize N +The size of the user specific header. + +.TP +.B maxattr N +The maximum Netlink attribute identifier expected by the interface. + +.TP +.B op NAME (ID) +A list of available operations including their name, numeric identifier +and the flags indicating the capabilities of the opertion. + +Available flags: +.RS +.TP +.I admin-perm +Requires administrative privileges + +.TP +.I has-doit +Command can handle request + +.TP +.I has-dump +Command can handle a dump request + +.TP +.I has-policy +Command enforces attribute validation policy +.RE + +.TP +.B grp NAME (ID) +A list of registered multicast groups including name (if available) +and identifier. +.RE + +.RS +.B Example: +.RS +0x0010 nlctrl version 2 +.RS 0 + hdrsize 0 maxattr 7 +.RS 0 + op GETFAMILY (0x03) +.RS 0 + grp notify (0x10) +.RE + + +.SH EXAMPLE +.RS 0 +$ genl-ctrl-list +.RS 0 +0x0010 nlctrl version 2 +.RS 0 +0x0011 NLBL_MGMT version 3 +.RS 0 +0x0012 NLBL_CIPSOv4 version 3 +.RS 0 +0x0013 NLBL_UNLBL version 3 +.RS 0 +0x0014 acpi_event version 1 +.RS 0 +0x0015 thermal_event version 1 +.RS 0 +0x0016 VFS_DQUOT version 1 +.RS 0 +0x0017 TASKSTATS version 1 +.RS 0 +0x0018 NET_DM version 2 + +.SH AUTHOR +.PP +Thomas Graf is the original author and current maintainer of libnl and +libnl tools. Many people have contributed to it since. diff --git a/libnl/man/nl-classid-lookup.8 b/libnl/man/nl-classid-lookup.8 new file mode 100644 index 0000000..3cf13de --- /dev/null +++ b/libnl/man/nl-classid-lookup.8 @@ -0,0 +1,51 @@ +.TH nl\-classid\-lookup 8 "19 October 2010" "libnl" +.SH NAME +nl\-classid\-lookup - Lookup classid definitions +.SH SYNOPSIS +.B nl\-classid\-lookup +.RB [ \-hv ] +.RB [ \-r ] +.RB [ \-\-raw ] +.I name + +.SH DESCRIPTION +.PP +nl\-classid\-lookup searches the classid database for a matching entry. It is used +to resolve qdisc/class names to classid values and vice versa. + +.SH OPTIONS +.TP +.BR \-\^h " or " \-\-help +Print help text to console and exit. +.TP +.BR \-\^v " or " \-\-version +Print versioning information to console and exit. +.TP +.BR \-\^r " or " \-\-reverse +Do a reverse lookup. Lookup a classid and print its name. +.TP +.B \-\-raw +Print the raw classid in hexadecimal format, do not pretty print it. + +.SH USAGE +.PP +Resolve the qdisc/class name "interactive": +.PP +.RS +# nl\-classid\-lookup interactive +.RE +.PP +Lookup the name of classid 1:2: +.PP +.RS +# nl\-classid\-lookup -r 1:2 +.RE + +.SH FILES +.PP +/etc/libnl/classid + +.SH AUTHOR +.PP +Thomas Graf is the original author and current maintainer of libnl and +libnl tools. Many people have contributed to it since. diff --git a/libnl/man/nl-pktloc-lookup.8 b/libnl/man/nl-pktloc-lookup.8 new file mode 100644 index 0000000..7a1daa4 --- /dev/null +++ b/libnl/man/nl-pktloc-lookup.8 @@ -0,0 +1,48 @@ +.TH nl\-pktloc-lookup 8 "27 October 2010" "libnl" +.SH NAME +nl\-pktloc\-lookup - Lookup packet location definitions +.SH SYNOPSIS +.B nl\-pktloc\-lookup +.I name +.br +.B nl\-pktloc\-lookup \-\-list + +.SH DESCRIPTION +.PP +nl\-pktloc\-lookup searches the packet location database for a matching +entry. It is used to resolve packet location aliases to their definition, +i.e. alignment, layer, offset, and mask. + +.SH OPTIONS +.TP +.BR \-\^h " or " \-\-help +Print help text to console and exit. +.TP +.BR \-\^v " or " \-\-version +Print versioning information to console and exit. +.TP +.BR \-\^l " or " \-\-list +List all packet location definitions. +.TP +.BR \-\-u32=VALUE +Prints the packet location definition in a special format that is +understood by iproute2's u32 selector parser. It will output a +u32 selector which will compare the provided value with the value +specified by the packet location. + +Please note that due to the limitation of u32, it is not possible +to use packet locations based on the link layer. nl-pktloc-lookup +will print an error message in this case. + +Example: + selector=$(nl-pktloc-lookup --u32 22 tcp.sport) + tc filter add [...] u32 match $(selector) flowid 1:2 + +.SH FILES +.PP +/etc/libnl/pktloc + +.SH AUTHOR +.PP +Thomas Graf is the original author and current maintainer of libnl and +libnl tools. Many people have contributed to it since. diff --git a/libnl/man/nl-qdisc-add.8 b/libnl/man/nl-qdisc-add.8 new file mode 100644 index 0000000..bc3fb17 --- /dev/null +++ b/libnl/man/nl-qdisc-add.8 @@ -0,0 +1,118 @@ +.TH nl\-qdisc 8 "21 October 2010" "libnl" +.SH NAME +nl\-qdisc\-{add|list|delete} - Manage queueing disciplines +.SH SYNOPSIS +.B nl\-qdisc\-add \-\-dev +.I dev +.B \-\-parent +.I id +.B [OPTIONS] +.I qdisc-type +.B [QDISC] +.sp +.B nl\-qdisc\-delete [ \-\-interactive ] [OPTIONS] +.sp +.B nl\-qdisc\-list [OPTIONS] + +.SH DESCRIPTION +.PP +The nl\-qdisc tools allow to manage and configure queueing disciplines +(qdiscs) in the kernel. + +.SH OPTIONS +.TP +.BR \-\^h " or " \-\-help +Print help text to console and exit. +.TP +.BR \-\^v " or " \-\-version +Print versioning information to console and exit. +.TP +.BR \-\^q " or " \-\-quiet +Do not print informal notifications about actions taken to the console. +By default a short description of each qdisc added/update/deleted will +be printed to the console. This option disables this behaviour. +.TP +.BR \-\^d " or " \-\-dev "=DEV" +Network device the qdisc is attached to. +.TP +.BR \-\^p " or " \-\-parent "=ID" +Identifier of the parent qdisc/class this qdisc is attached to. The +identifier can be specified as classid, name or one of the special +values "root" or "ingress". +.TP +.BR \-\^i " or " \-\-id "=ID" +Identifier of qdisc. It can be specified as classid or name. + +.SS nl\-qdisc\-add Options +.TP +.B \-\-update +Update qdisc if it already exists, otherwise attempting to add a qdisc which already +exists will result in an error. This does not include changing the type of the qdisc, +use \-\-replace if you wish to do so. +.TP +.B \-\-replace +Replace or update qdisc if it already exists. Same behaviour as \-\-update but will +completely replace the qdisc if it exists already. +.TP +.B \-\-update\-only +Update an existing qdisc but do not create it if it does not exist. +.TP +.B \-\-replace\-only +Update or replace an existing qdisc but do not create it if it does exist. + +.SS nl\-qdisc\-delete Options +.TP +.B \-\-interactive +The interactive mode requires confirmation by the user for each qdisc deleted. It +will print a prompt for each qdisc matching the provided filter and requires the +user to answer 'y'es or 'n'o. +.TP +.B \-\-yes +Make the default answer for interactive prompts be 'y'es. This option is also +required to delete all qdiscs on all network devices. +.TP +.BR \-\^k " or " \-\-kind "=TYPE" +Only delete qdiscs of this type. + +.SS nl\-qdisc\-list Options +.TP +.B \-\-details +Show detailed information for each qdisc listed. +.TP +.B \-\-stats +Show statistics information for each qdisc listed. This option will also turn +on detailed information automatically. +.TP +.BR \-\^r " or " \-\-recursive +List all TC objects recurisvely attached to all qdiscs matching the filter. +.TP +.BR \-\^k " or " \-\-kind "=TYPE" +Only list qdiscs of this type. + +.SH USAGE +.PP +Add a HTB root qdisc with id "5:": +.PP +.RS +nl\-qdisc\-add \-\-dev eth0 \-\-parent root \-\-id 5: htb +.RE +.PP +List all qdiscs on eth0 and print statistical data: +.PP +.RS +nl\-qdisc\-list \-\-stats \-\-dev eth0 +.RE +.PP +Delete the qdisc "5:": +.RS +nl\-qdisc\-delete \-\-id 5: +.RE + +.SH "SEE ALSO" +.PP +.B nl\-classid\-lookup(8) + +.SH AUTHOR +.PP +Thomas Graf is the original author and current maintainer of libnl and +libnl tools. Many people have contributed to it since. diff --git a/libnl/man/nl-qdisc-delete.8 b/libnl/man/nl-qdisc-delete.8 new file mode 100644 index 0000000..864a4e0 --- /dev/null +++ b/libnl/man/nl-qdisc-delete.8 @@ -0,0 +1 @@ +.so man8/nl-qdisc-add.8 diff --git a/libnl/man/nl-qdisc-list.8 b/libnl/man/nl-qdisc-list.8 new file mode 100644 index 0000000..864a4e0 --- /dev/null +++ b/libnl/man/nl-qdisc-list.8 @@ -0,0 +1 @@ +.so man8/nl-qdisc-add.8 diff --git a/libnl/pyproject.toml b/libnl/pyproject.toml new file mode 100644 index 0000000..e221cb7 --- /dev/null +++ b/libnl/pyproject.toml @@ -0,0 +1,8 @@ +[tool.black] +target-version = ["py37", "py38", "py39", "py310"] +include = ''' + ( + \.py$ + | \.py\.in$ + ) + ''' diff --git a/libnl/python/README b/libnl/python/README new file mode 100644 index 0000000..4ccc337 --- /dev/null +++ b/libnl/python/README @@ -0,0 +1,12 @@ + +*************************************************************************** + +NOTE: The python wrapper is experimental and may or may not work. + +*************************************************************************** + +For the brave: + + (requires an installed libnl) + - $ python ./setup.py build + - $ sudo python ./setup.py install diff --git a/libnl/python/doc/conf.py b/libnl/python/doc/conf.py new file mode 100644 index 0000000..dfa53ab --- /dev/null +++ b/libnl/python/doc/conf.py @@ -0,0 +1,224 @@ +# -*- coding: utf-8 -*- +# +# libnl-python documentation build configuration file, created by +# sphinx-quickstart on Mon May 9 10:58:58 2011. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ["sphinx.ext.todo", "sphinx.ext.ifconfig", "sphinx.ext.viewcode"] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# The suffix of source filenames. +source_suffix = ".rst" + +# The encoding of source files. +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = "index" + +# General information about the project. +project = "libnl-python" +copyright = "2011, Thomas Graf " + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = "1.0" +# The full version, including alpha/beta/rc tags. +release = "1.0" + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# today = '' +# Else, today_fmt is used as the format for a strftime call. +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ["_build"] + +# The reST default role (used for this markup: `text`) to use for all documents. +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = "sphinx" + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = "default" + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +# html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +# html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# html_additional_pages = {} + +# If false, no module index is generated. +# html_domain_indices = True + +# If false, no index is generated. +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = "libnl-pythondoc" + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +# latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +# latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ( + "index", + "libnl-python.tex", + "libnl-python Documentation", + "Thomas Graf \\textless{}tgraf@suug.ch\\textgreater{}", + "manual", + ), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# latex_use_parts = False + +# If true, show page references after internal links. +# latex_show_pagerefs = False + +# If true, show URL addresses after external links. +# latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +# latex_preamble = '' + +# Documents to append as an appendix to all manuals. +# latex_appendices = [] + +# If false, no module index is generated. +# latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ( + "index", + "libnl-python", + "libnl-python Documentation", + ["Thomas Graf "], + 1, + ) +] diff --git a/libnl/python/doc/core.rst b/libnl/python/doc/core.rst new file mode 100644 index 0000000..012e14c --- /dev/null +++ b/libnl/python/doc/core.rst @@ -0,0 +1,215 @@ +******************* +Netlink Core Module +******************* + +.. py:module:: netlink.core + +Examples:: + + import netlink.core as netlink + +=============== +Object +=============== + +.. py:class:: Object + + Base class for all classes representing a cacheable object + + Example:: + obj = netlink.Object("route/link", "link") + + .. py:method:: clone + + Clone the object and return a duplicate (used for COW) + + .. py:method:: dump([params=None]) + + Call the libnl internal dump mechanism to dump the object + according to the parameters specified. + + .. py:method:: apply(attr, val) + + Applies a attribute=value pair and modifies the object accordingly. + Example:: + obj.apply("mtu", 1200) # Sets attribute mtu to 1200 (link obj) + + :raises: KeyError if attribute is unknown + :raises: ImmutableError if attribute is not mutable + + .. py:attribute:: mark + + True if the object is marked, otherwise False. + + .. py:attribute:: shared + + True if the object is used by multiple parties, otherwise False. + + .. py:attribute:: refcnt + + Number of users sharing a reference to the object + :rtype: int + + .. py:attribute:: attrs + + List of attributes + + :rtype: list of strings + +=============== +Cache +=============== + +.. py:class:: Cache + + Base class for all cache implementations. + + A cache is a collection of cacheable objects which is typically used + by netlink protocols which handle any kind of object, e.g. network + links, network addresses, neighbours, ... + + .. py:method:: subset(filter) + + Returns a new cache containing the subset which matches the + provided filter. + + :raises: ValueError if no filter is specified + :rtype: :py:class:`Cache` + + .. py:method:: dump([params=None, filter=None]) + + Calls the libnl internal dump mechanism to dump the cache according + to the parameters and filter specified. + + .. py:method:: clear() + + Remove and possibly destroy all objects in the cache + + .. py:method:: refill([socket=None]) -> :py:class:`Cache` + + Clears and refills the cache with the content which is provided by + the kernel, e.g. for a link cache this would mean refilling the + cache with all configured network links. + + .. py:method:: provide() + + Caches which have been "provided" are made available to other users + (of the same application context) which "require" it. F.e. a link + cache is generally provided to allow others to translate interface + indexes to link names + + + .. py:method:: unprovide() + + No longer make the cache available to others. If the cache has been + handed out already, that reference will still be valid. + +=============== +AbstractAddress +=============== + +.. py:class:: AbstractAddress + + Abstract representation of an address. This class is not to be mistaken + with :py:class:`route.Address` which represents a configured network + address. This class represents the actual address in a family independent + way:: + + addr = netlink.AbstractAddress('127.0.0.1/8') + print addr # => '127.0.0.1/8' + print addr.prefixlen # => '8' + print addr.family # => 'inet' + print len(addr) # => '4' (32bit ipv4 address) + + a = netlink.AbstractAddress('10.0.0.1/24') + b = netlink.AbstractAddress('10.0.0.2/24') + print a == b # => False + + .. py:attribute:: prefixlen + + Length of prefix in number of bits. + + :rtype: int + + .. py:attribute:: family + + The family type of the address. Setting the address family can be + done with a string or a :py:class:`AddressFamily` object. + + :rtype: :py:class:`AddressFamily` + + .. py:attribute:: shared + + True if address is in use by multiple callers, otherwise False + + :rtype: bool + +=============== +AddressFamily +=============== + +.. py:class:: AddressFamily + + Address family representation:: + + af = netlink.AddressFamily('inet6') + # raises: + # - ValueError if family name is not known + # - TypeError if invalid type is specified for family + + print af # => 'inet6' (string representation) + print int(af) # => 10 (numeric representation) + print repr(af) # => AddressFamily('inet6') + +=============== +Exceptions +=============== + +.. py:exception:: NetlinkError + + Generic exception raised by netlink modules. + +.. py:exception:: KernelError + + Raised if an error occured while communicating with the kernel. Contains + the error code returning which is automatically included in the error + message. + +.. py:exception:: ImmutableError + + Raised if an attribute is modified which is marked immutable. + +=============== +Socket +=============== + +.. py:class:: Socket + + Netlink socket. + + Note: It is not required to manually create and connect netlink sockets + when using caches. The caches will automatically lookup or create a + socket as needed. + + .. py:attribute:: local_port + + Local port (address) of netlink socket + + .. py:attribute:: peer_port + + Peer port (remote address) of netlink socket. If set, all messages + will be sent to that peer. + + .. py:method:: connect(proto) + + Connect the netlink socket using the specified netlink protocol:: + sock.connect(netlink.NETLINK_ROUTE) + + .. py:method:: disconnect() + + Disconnect the socket + + .. py:method:: set_bufsize(rx, tx) + + Sets the size of the socket buffer + diff --git a/libnl/python/doc/index.rst b/libnl/python/doc/index.rst new file mode 100644 index 0000000..8de8ae3 --- /dev/null +++ b/libnl/python/doc/index.rst @@ -0,0 +1,24 @@ +.. libnl-python documentation master file, created by + sphinx-quickstart on Mon May 9 10:58:58 2011. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to libnl-python's documentation! +======================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + core + route + route_addr + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/libnl/python/doc/route.rst b/libnl/python/doc/route.rst new file mode 100644 index 0000000..0b8f3f9 --- /dev/null +++ b/libnl/python/doc/route.rst @@ -0,0 +1,3 @@ +********************** +Routing +********************** diff --git a/libnl/python/doc/route_addr.rst b/libnl/python/doc/route_addr.rst new file mode 100644 index 0000000..2cfe139 --- /dev/null +++ b/libnl/python/doc/route_addr.rst @@ -0,0 +1,47 @@ +================= +Network Addresses +================= + +The **Address** module provides access to the network address configuration +of the kernel. It provides an interface to fetch all configured addresses, +add new addresses and to delete existing addresses. + +Fetching the list of network addresses is achieved by creating a new +address cache:: + + import netlink.route.address as Address + + addr_cache = Address.AddressCache() + addr_cache.refill() + + for addr in addr_cache: + print addr + +.. py:module:: netlink.route.addr + + +AddressCache +------------ + +.. py:class:: AddressCache + + Represents a cache containing all or a subset of network addresses. + + .. py:method:: lookup(ifindex, local) + + Lookup the address which matches ifindex and local address + + :raises: KeyError if address is not found. + +Address +------- + +.. py:class:: Address + + Representation of a configured network address. + + .. py:attribute:: ifindex + + Interface index + + :rtype: int diff --git a/libnl/python/examples/iface.py b/libnl/python/examples/iface.py new file mode 100644 index 0000000..afeafff --- /dev/null +++ b/libnl/python/examples/iface.py @@ -0,0 +1,112 @@ +from __future__ import print_function +import netlink.capi as nl +import netlink.genl.capi as genl +import nl80211 +import sys +import traceback + + +class test_class: + def __init__(self): + self.done = 1 + + +def msg_handler(m, a): + try: + e, attr = genl.py_genlmsg_parse( + nl.nlmsg_hdr(m), 0, nl80211.NL80211_ATTR_MAX, None + ) + if nl80211.NL80211_ATTR_WIPHY in attr: + thiswiphy = nl.nla_get_u32(attr[nl80211.NL80211_ATTR_WIPHY]) + print("phy#%d" % thiswiphy) + if nl80211.NL80211_ATTR_IFNAME in attr: + print( + "\tinterface %s" % nl.nla_get_string(attr[nl80211.NL80211_ATTR_IFNAME]) + ) + if nl80211.NL80211_ATTR_IFINDEX in attr: + print("\tifindex %d" % nl.nla_get_u32(attr[nl80211.NL80211_ATTR_IFINDEX])) + if nl80211.NL80211_ATTR_WDEV in attr: + print("\twdev 0x%lx" % nl.nla_get_u64(attr[nl80211.NL80211_ATTR_WDEV])) + if nl80211.NL80211_ATTR_MAC in attr: + print( + "\tmac %02x:%02x:%02x:%02x:%02x:%02x" + % tuple(nl.nla_data(attr[nl80211.NL80211_ATTR_MAC])) + ) + if nl80211.NL80211_ATTR_SSID in attr: + print("\tssid ", nl.nla_data(attr[nl80211.NL80211_ATTR_SSID])) + if nl80211.NL80211_ATTR_IFTYPE in attr: + iftype = nl.nla_get_u32(attr[nl80211.NL80211_ATTR_IFTYPE]) + print("\ttype %s" % nl80211.nl80211_iftype2str[iftype]) + if nl80211.NL80211_ATTR_WIPHY_FREQ in attr: + freq = nl.nla_get_u32(attr[nl80211.NL80211_ATTR_WIPHY_FREQ]) + + sys.stdout.write("\tfreq %d MHz" % freq) + + if nl80211.NL80211_ATTR_CHANNEL_WIDTH in attr: + chanw = nl.nla_get_u32(attr[nl80211.NL80211_ATTR_CHANNEL_WIDTH]) + sys.stdout.write(", width: %s" % nl80211.nl80211_chan_width2str[chanw]) + if nl80211.NL80211_ATTR_CENTER_FREQ1 in attr: + sys.stdout.write( + ", center1: %d MHz" + % nl.nla_get_u32(attr[nl80211.NL80211_ATTR_CENTER_FREQ1]) + ) + if nl80211.NL80211_ATTR_CENTER_FREQ2 in attr: + sys.stdout.write( + ", center2: %d MHz" + % nl.nla_get_u32(attr[nl80211.NL80211_ATTR_CENTER_FREQ2]) + ) + elif nl80211.NL80211_ATTR_WIPHY_CHANNEL_TYPE in attr: + channel_type = nl.nla_get_u32( + attr[nl80211.NL80211_ATTR_WIPHY_CHANNEL_TYPE] + ) + sys.stdout.write(" %s" % nl80211.nl80211_channel_type2str(channel_type)) + + sys.stdout.write("\n") + return nl.NL_SKIP + except Exception: + (t, v, tb) = sys.exc_info() + print(v.message) + traceback.print_tb(tb) + + +def error_handler(err, a): + a.done = err.error + return nl.NL_STOP + + +def finish_handler(m, a): + return nl.NL_SKIP + + +def ack_handler(m, a): + a.done = 0 + return nl.NL_STOP + + +try: + cbd = test_class() + tx_cb = nl.nl_cb_alloc(nl.NL_CB_DEFAULT) + rx_cb = nl.nl_cb_clone(tx_cb) + s = nl.nl_socket_alloc_cb(tx_cb) + nl.py_nl_cb_err(rx_cb, nl.NL_CB_CUSTOM, error_handler, cbd) + nl.py_nl_cb_set(rx_cb, nl.NL_CB_FINISH, nl.NL_CB_CUSTOM, finish_handler, cbd) + nl.py_nl_cb_set(rx_cb, nl.NL_CB_ACK, nl.NL_CB_CUSTOM, ack_handler, cbd) + nl.py_nl_cb_set(rx_cb, nl.NL_CB_VALID, nl.NL_CB_CUSTOM, msg_handler, cbd) + + genl.genl_connect(s) + family = genl.genl_ctrl_resolve(s, "nl80211") + m = nl.nlmsg_alloc() + genl.genlmsg_put(m, 0, 0, family, 0, 0, nl80211.NL80211_CMD_GET_INTERFACE, 0) + nl.nla_put_u32(m, nl80211.NL80211_ATTR_IFINDEX, nl.if_nametoindex("wlan0")) + + err = nl.nl_send_auto_complete(s, m) + if err < 0: + nl.nlmsg_free(m) + + while cbd.done > 0 and not err < 0: + err = nl.nl_recvmsgs(s, rx_cb) + +except Exception: + (t, v, tb) = sys.exc_info() + print(v.message) + traceback.print_tb(tb) diff --git a/libnl/python/examples/nl80211.py b/libnl/python/examples/nl80211.py new file mode 100644 index 0000000..88ea535 --- /dev/null +++ b/libnl/python/examples/nl80211.py @@ -0,0 +1,1575 @@ +########################################################### +# file: nl80211.py +# --------------------------------------------------------- +# This file is generated using extract.py using pycparser +########################################################### +NL80211_GENL_FAMILY = "nl80211" + +NL80211_CMD_UNSPEC = 0 +NL80211_CMD_GET_WIPHY = 1 +NL80211_CMD_SET_WIPHY = 2 +NL80211_CMD_NEW_WIPHY = 3 +NL80211_CMD_DEL_WIPHY = 4 +NL80211_CMD_GET_INTERFACE = 5 +NL80211_CMD_SET_INTERFACE = 6 +NL80211_CMD_NEW_INTERFACE = 7 +NL80211_CMD_DEL_INTERFACE = 8 +NL80211_CMD_GET_KEY = 9 +NL80211_CMD_SET_KEY = 10 +NL80211_CMD_NEW_KEY = 11 +NL80211_CMD_DEL_KEY = 12 +NL80211_CMD_GET_BEACON = 13 +NL80211_CMD_SET_BEACON = 14 +NL80211_CMD_START_AP = 15 +NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP +NL80211_CMD_STOP_AP = 16 +NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP +NL80211_CMD_GET_STATION = 17 +NL80211_CMD_SET_STATION = 18 +NL80211_CMD_NEW_STATION = 19 +NL80211_CMD_DEL_STATION = 20 +NL80211_CMD_GET_MPATH = 21 +NL80211_CMD_SET_MPATH = 22 +NL80211_CMD_NEW_MPATH = 23 +NL80211_CMD_DEL_MPATH = 24 +NL80211_CMD_SET_BSS = 25 +NL80211_CMD_SET_REG = 26 +NL80211_CMD_REQ_SET_REG = 27 +NL80211_CMD_GET_MESH_CONFIG = 28 +NL80211_CMD_SET_MESH_CONFIG = 29 +NL80211_CMD_SET_MGMT_EXTRA_IE = 30 +NL80211_CMD_GET_REG = 31 +NL80211_CMD_GET_SCAN = 32 +NL80211_CMD_TRIGGER_SCAN = 33 +NL80211_CMD_NEW_SCAN_RESULTS = 34 +NL80211_CMD_SCAN_ABORTED = 35 +NL80211_CMD_REG_CHANGE = 36 +NL80211_CMD_AUTHENTICATE = 37 +NL80211_CMD_ASSOCIATE = 38 +NL80211_CMD_DEAUTHENTICATE = 39 +NL80211_CMD_DISASSOCIATE = 40 +NL80211_CMD_MICHAEL_MIC_FAILURE = 41 +NL80211_CMD_REG_BEACON_HINT = 42 +NL80211_CMD_JOIN_IBSS = 43 +NL80211_CMD_LEAVE_IBSS = 44 +NL80211_CMD_TESTMODE = 45 +NL80211_CMD_CONNECT = 46 +NL80211_CMD_ROAM = 47 +NL80211_CMD_DISCONNECT = 48 +NL80211_CMD_SET_WIPHY_NETNS = 49 +NL80211_CMD_GET_SURVEY = 50 +NL80211_CMD_NEW_SURVEY_RESULTS = 51 +NL80211_CMD_SET_PMKSA = 52 +NL80211_CMD_DEL_PMKSA = 53 +NL80211_CMD_FLUSH_PMKSA = 54 +NL80211_CMD_REMAIN_ON_CHANNEL = 55 +NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL = 56 +NL80211_CMD_SET_TX_BITRATE_MASK = 57 +NL80211_CMD_REGISTER_FRAME = 58 +NL80211_CMD_REGISTER_ACTION = NL80211_CMD_REGISTER_FRAME +NL80211_CMD_FRAME = 59 +NL80211_CMD_ACTION = NL80211_CMD_FRAME +NL80211_CMD_FRAME_TX_STATUS = 60 +NL80211_CMD_ACTION_TX_STATUS = NL80211_CMD_FRAME_TX_STATUS +NL80211_CMD_SET_POWER_SAVE = 61 +NL80211_CMD_GET_POWER_SAVE = 62 +NL80211_CMD_SET_CQM = 63 +NL80211_CMD_NOTIFY_CQM = 64 +NL80211_CMD_SET_CHANNEL = 65 +NL80211_CMD_SET_WDS_PEER = 66 +NL80211_CMD_FRAME_WAIT_CANCEL = 67 +NL80211_CMD_JOIN_MESH = 68 +NL80211_CMD_LEAVE_MESH = 69 +NL80211_CMD_UNPROT_DEAUTHENTICATE = 70 +NL80211_CMD_UNPROT_DISASSOCIATE = 71 +NL80211_CMD_NEW_PEER_CANDIDATE = 72 +NL80211_CMD_GET_WOWLAN = 73 +NL80211_CMD_SET_WOWLAN = 74 +NL80211_CMD_START_SCHED_SCAN = 75 +NL80211_CMD_STOP_SCHED_SCAN = 76 +NL80211_CMD_SCHED_SCAN_RESULTS = 77 +NL80211_CMD_SCHED_SCAN_STOPPED = 78 +NL80211_CMD_SET_REKEY_OFFLOAD = 79 +NL80211_CMD_PMKSA_CANDIDATE = 80 +NL80211_CMD_TDLS_OPER = 81 +NL80211_CMD_TDLS_MGMT = 82 +NL80211_CMD_UNEXPECTED_FRAME = 83 +NL80211_CMD_PROBE_CLIENT = 84 +NL80211_CMD_REGISTER_BEACONS = 85 +NL80211_CMD_UNEXPECTED_4ADDR_FRAME = 86 +NL80211_CMD_SET_NOACK_MAP = 87 +NL80211_CMD_CH_SWITCH_NOTIFY = 88 +NL80211_CMD_START_P2P_DEVICE = 89 +NL80211_CMD_STOP_P2P_DEVICE = 90 +NL80211_CMD_CONN_FAILED = 91 +NL80211_CMD_SET_MCAST_RATE = 92 +NL80211_CMD_SET_MAC_ACL = 93 +NL80211_CMD_RADAR_DETECT = 94 +NL80211_CMD_GET_PROTOCOL_FEATURES = 95 +NL80211_CMD_UPDATE_FT_IES = 96 +NL80211_CMD_FT_EVENT = 97 +NL80211_CMD_CRIT_PROTOCOL_START = 98 +NL80211_CMD_CRIT_PROTOCOL_STOP = 99 +__NL80211_CMD_AFTER_LAST = 100 +NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1 +NL80211_ATTR_UNSPEC = 0 +NL80211_ATTR_WIPHY = 1 +NL80211_ATTR_WIPHY_NAME = 2 +NL80211_ATTR_IFINDEX = 3 +NL80211_ATTR_IFNAME = 4 +NL80211_ATTR_IFTYPE = 5 +NL80211_ATTR_MAC = 6 +NL80211_ATTR_KEY_DATA = 7 +NL80211_ATTR_KEY_IDX = 8 +NL80211_ATTR_KEY_CIPHER = 9 +NL80211_ATTR_KEY_SEQ = 10 +NL80211_ATTR_KEY_DEFAULT = 11 +NL80211_ATTR_BEACON_INTERVAL = 12 +NL80211_ATTR_DTIM_PERIOD = 13 +NL80211_ATTR_BEACON_HEAD = 14 +NL80211_ATTR_BEACON_TAIL = 15 +NL80211_ATTR_STA_AID = 16 +NL80211_ATTR_STA_FLAGS = 17 +NL80211_ATTR_STA_LISTEN_INTERVAL = 18 +NL80211_ATTR_STA_SUPPORTED_RATES = 19 +NL80211_ATTR_STA_VLAN = 20 +NL80211_ATTR_STA_INFO = 21 +NL80211_ATTR_WIPHY_BANDS = 22 +NL80211_ATTR_MNTR_FLAGS = 23 +NL80211_ATTR_MESH_ID = 24 +NL80211_ATTR_STA_PLINK_ACTION = 25 +NL80211_ATTR_MPATH_NEXT_HOP = 26 +NL80211_ATTR_MPATH_INFO = 27 +NL80211_ATTR_BSS_CTS_PROT = 28 +NL80211_ATTR_BSS_SHORT_PREAMBLE = 29 +NL80211_ATTR_BSS_SHORT_SLOT_TIME = 30 +NL80211_ATTR_HT_CAPABILITY = 31 +NL80211_ATTR_SUPPORTED_IFTYPES = 32 +NL80211_ATTR_REG_ALPHA2 = 33 +NL80211_ATTR_REG_RULES = 34 +NL80211_ATTR_MESH_CONFIG = 35 +NL80211_ATTR_BSS_BASIC_RATES = 36 +NL80211_ATTR_WIPHY_TXQ_PARAMS = 37 +NL80211_ATTR_WIPHY_FREQ = 38 +NL80211_ATTR_WIPHY_CHANNEL_TYPE = 39 +NL80211_ATTR_KEY_DEFAULT_MGMT = 40 +NL80211_ATTR_MGMT_SUBTYPE = 41 +NL80211_ATTR_IE = 42 +NL80211_ATTR_MAX_NUM_SCAN_SSIDS = 43 +NL80211_ATTR_SCAN_FREQUENCIES = 44 +NL80211_ATTR_SCAN_SSIDS = 45 +NL80211_ATTR_GENERATION = 46 +NL80211_ATTR_BSS = 47 +NL80211_ATTR_REG_INITIATOR = 48 +NL80211_ATTR_REG_TYPE = 49 +NL80211_ATTR_SUPPORTED_COMMANDS = 50 +NL80211_ATTR_FRAME = 51 +NL80211_ATTR_SSID = 52 +NL80211_ATTR_AUTH_TYPE = 53 +NL80211_ATTR_REASON_CODE = 54 +NL80211_ATTR_KEY_TYPE = 55 +NL80211_ATTR_MAX_SCAN_IE_LEN = 56 +NL80211_ATTR_CIPHER_SUITES = 57 +NL80211_ATTR_FREQ_BEFORE = 58 +NL80211_ATTR_FREQ_AFTER = 59 +NL80211_ATTR_FREQ_FIXED = 60 +NL80211_ATTR_WIPHY_RETRY_SHORT = 61 +NL80211_ATTR_WIPHY_RETRY_LONG = 62 +NL80211_ATTR_WIPHY_FRAG_THRESHOLD = 63 +NL80211_ATTR_WIPHY_RTS_THRESHOLD = 64 +NL80211_ATTR_TIMED_OUT = 65 +NL80211_ATTR_USE_MFP = 66 +NL80211_ATTR_STA_FLAGS2 = 67 +NL80211_ATTR_CONTROL_PORT = 68 +NL80211_ATTR_TESTDATA = 69 +NL80211_ATTR_PRIVACY = 70 +NL80211_ATTR_DISCONNECTED_BY_AP = 71 +NL80211_ATTR_STATUS_CODE = 72 +NL80211_ATTR_CIPHER_SUITES_PAIRWISE = 73 +NL80211_ATTR_CIPHER_SUITE_GROUP = 74 +NL80211_ATTR_WPA_VERSIONS = 75 +NL80211_ATTR_AKM_SUITES = 76 +NL80211_ATTR_REQ_IE = 77 +NL80211_ATTR_RESP_IE = 78 +NL80211_ATTR_PREV_BSSID = 79 +NL80211_ATTR_KEY = 80 +NL80211_ATTR_KEYS = 81 +NL80211_ATTR_PID = 82 +NL80211_ATTR_4ADDR = 83 +NL80211_ATTR_SURVEY_INFO = 84 +NL80211_ATTR_PMKID = 85 +NL80211_ATTR_MAX_NUM_PMKIDS = 86 +NL80211_ATTR_DURATION = 87 +NL80211_ATTR_COOKIE = 88 +NL80211_ATTR_WIPHY_COVERAGE_CLASS = 89 +NL80211_ATTR_TX_RATES = 90 +NL80211_ATTR_FRAME_MATCH = 91 +NL80211_ATTR_ACK = 92 +NL80211_ATTR_PS_STATE = 93 +NL80211_ATTR_CQM = 94 +NL80211_ATTR_LOCAL_STATE_CHANGE = 95 +NL80211_ATTR_AP_ISOLATE = 96 +NL80211_ATTR_WIPHY_TX_POWER_SETTING = 97 +NL80211_ATTR_WIPHY_TX_POWER_LEVEL = 98 +NL80211_ATTR_TX_FRAME_TYPES = 99 +NL80211_ATTR_RX_FRAME_TYPES = 100 +NL80211_ATTR_FRAME_TYPE = 101 +NL80211_ATTR_CONTROL_PORT_ETHERTYPE = 102 +NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT = 103 +NL80211_ATTR_SUPPORT_IBSS_RSN = 104 +NL80211_ATTR_WIPHY_ANTENNA_TX = 105 +NL80211_ATTR_WIPHY_ANTENNA_RX = 106 +NL80211_ATTR_MCAST_RATE = 107 +NL80211_ATTR_OFFCHANNEL_TX_OK = 108 +NL80211_ATTR_BSS_HT_OPMODE = 109 +NL80211_ATTR_KEY_DEFAULT_TYPES = 110 +NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION = 111 +NL80211_ATTR_MESH_SETUP = 112 +NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX = 113 +NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX = 114 +NL80211_ATTR_SUPPORT_MESH_AUTH = 115 +NL80211_ATTR_STA_PLINK_STATE = 116 +NL80211_ATTR_WOWLAN_TRIGGERS = 117 +NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED = 118 +NL80211_ATTR_SCHED_SCAN_INTERVAL = 119 +NL80211_ATTR_INTERFACE_COMBINATIONS = 120 +NL80211_ATTR_SOFTWARE_IFTYPES = 121 +NL80211_ATTR_REKEY_DATA = 122 +NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS = 123 +NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN = 124 +NL80211_ATTR_SCAN_SUPP_RATES = 125 +NL80211_ATTR_HIDDEN_SSID = 126 +NL80211_ATTR_IE_PROBE_RESP = 127 +NL80211_ATTR_IE_ASSOC_RESP = 128 +NL80211_ATTR_STA_WME = 129 +NL80211_ATTR_SUPPORT_AP_UAPSD = 130 +NL80211_ATTR_ROAM_SUPPORT = 131 +NL80211_ATTR_SCHED_SCAN_MATCH = 132 +NL80211_ATTR_MAX_MATCH_SETS = 133 +NL80211_ATTR_PMKSA_CANDIDATE = 134 +NL80211_ATTR_TX_NO_CCK_RATE = 135 +NL80211_ATTR_TDLS_ACTION = 136 +NL80211_ATTR_TDLS_DIALOG_TOKEN = 137 +NL80211_ATTR_TDLS_OPERATION = 138 +NL80211_ATTR_TDLS_SUPPORT = 139 +NL80211_ATTR_TDLS_EXTERNAL_SETUP = 140 +NL80211_ATTR_DEVICE_AP_SME = 141 +NL80211_ATTR_DONT_WAIT_FOR_ACK = 142 +NL80211_ATTR_FEATURE_FLAGS = 143 +NL80211_ATTR_PROBE_RESP_OFFLOAD = 144 +NL80211_ATTR_PROBE_RESP = 145 +NL80211_ATTR_DFS_REGION = 146 +NL80211_ATTR_DISABLE_HT = 147 +NL80211_ATTR_HT_CAPABILITY_MASK = 148 +NL80211_ATTR_NOACK_MAP = 149 +NL80211_ATTR_INACTIVITY_TIMEOUT = 150 +NL80211_ATTR_RX_SIGNAL_DBM = 151 +NL80211_ATTR_BG_SCAN_PERIOD = 152 +NL80211_ATTR_WDEV = 153 +NL80211_ATTR_USER_REG_HINT_TYPE = 154 +NL80211_ATTR_CONN_FAILED_REASON = 155 +NL80211_ATTR_SAE_DATA = 156 +NL80211_ATTR_VHT_CAPABILITY = 157 +NL80211_ATTR_SCAN_FLAGS = 158 +NL80211_ATTR_CHANNEL_WIDTH = 159 +NL80211_ATTR_CENTER_FREQ1 = 160 +NL80211_ATTR_CENTER_FREQ2 = 161 +NL80211_ATTR_P2P_CTWINDOW = 162 +NL80211_ATTR_P2P_OPPPS = 163 +NL80211_ATTR_LOCAL_MESH_POWER_MODE = 164 +NL80211_ATTR_ACL_POLICY = 165 +NL80211_ATTR_MAC_ADDRS = 166 +NL80211_ATTR_MAC_ACL_MAX = 167 +NL80211_ATTR_RADAR_EVENT = 168 +NL80211_ATTR_EXT_CAPA = 169 +NL80211_ATTR_EXT_CAPA_MASK = 170 +NL80211_ATTR_STA_CAPABILITY = 171 +NL80211_ATTR_STA_EXT_CAPABILITY = 172 +NL80211_ATTR_PROTOCOL_FEATURES = 173 +NL80211_ATTR_SPLIT_WIPHY_DUMP = 174 +NL80211_ATTR_DISABLE_VHT = 175 +NL80211_ATTR_VHT_CAPABILITY_MASK = 176 +NL80211_ATTR_MDID = 177 +NL80211_ATTR_IE_RIC = 178 +NL80211_ATTR_CRIT_PROT_ID = 179 +NL80211_ATTR_MAX_CRIT_PROT_DURATION = 180 +NL80211_ATTR_PEER_AID = 181 +__NL80211_ATTR_AFTER_LAST = 182 +NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 +NL80211_IFTYPE_UNSPECIFIED = 0 +NL80211_IFTYPE_ADHOC = 1 +NL80211_IFTYPE_STATION = 2 +NL80211_IFTYPE_AP = 3 +NL80211_IFTYPE_AP_VLAN = 4 +NL80211_IFTYPE_WDS = 5 +NL80211_IFTYPE_MONITOR = 6 +NL80211_IFTYPE_MESH_POINT = 7 +NL80211_IFTYPE_P2P_CLIENT = 8 +NL80211_IFTYPE_P2P_GO = 9 +NL80211_IFTYPE_P2P_DEVICE = 10 +NUM_NL80211_IFTYPES = 11 +NL80211_IFTYPE_MAX = NUM_NL80211_IFTYPES - 1 +__NL80211_STA_FLAG_INVALID = 0 +NL80211_STA_FLAG_AUTHORIZED = 1 +NL80211_STA_FLAG_SHORT_PREAMBLE = 2 +NL80211_STA_FLAG_WME = 3 +NL80211_STA_FLAG_MFP = 4 +NL80211_STA_FLAG_AUTHENTICATED = 5 +NL80211_STA_FLAG_TDLS_PEER = 6 +NL80211_STA_FLAG_ASSOCIATED = 7 +__NL80211_STA_FLAG_AFTER_LAST = 8 +NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1 +__NL80211_RATE_INFO_INVALID = 0 +NL80211_RATE_INFO_BITRATE = 1 +NL80211_RATE_INFO_MCS = 2 +NL80211_RATE_INFO_40_MHZ_WIDTH = 3 +NL80211_RATE_INFO_SHORT_GI = 4 +NL80211_RATE_INFO_BITRATE32 = 5 +NL80211_RATE_INFO_VHT_MCS = 6 +NL80211_RATE_INFO_VHT_NSS = 7 +NL80211_RATE_INFO_80_MHZ_WIDTH = 8 +NL80211_RATE_INFO_80P80_MHZ_WIDTH = 9 +NL80211_RATE_INFO_160_MHZ_WIDTH = 10 +__NL80211_RATE_INFO_AFTER_LAST = 11 +NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1 +__NL80211_STA_BSS_PARAM_INVALID = 0 +NL80211_STA_BSS_PARAM_CTS_PROT = 1 +NL80211_STA_BSS_PARAM_SHORT_PREAMBLE = 2 +NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME = 3 +NL80211_STA_BSS_PARAM_DTIM_PERIOD = 4 +NL80211_STA_BSS_PARAM_BEACON_INTERVAL = 5 +__NL80211_STA_BSS_PARAM_AFTER_LAST = 6 +NL80211_STA_BSS_PARAM_MAX = __NL80211_STA_BSS_PARAM_AFTER_LAST - 1 +__NL80211_STA_INFO_INVALID = 0 +NL80211_STA_INFO_INACTIVE_TIME = 1 +NL80211_STA_INFO_RX_BYTES = 2 +NL80211_STA_INFO_TX_BYTES = 3 +NL80211_STA_INFO_LLID = 4 +NL80211_STA_INFO_PLID = 5 +NL80211_STA_INFO_PLINK_STATE = 6 +NL80211_STA_INFO_SIGNAL = 7 +NL80211_STA_INFO_TX_BITRATE = 8 +NL80211_STA_INFO_RX_PACKETS = 9 +NL80211_STA_INFO_TX_PACKETS = 10 +NL80211_STA_INFO_TX_RETRIES = 11 +NL80211_STA_INFO_TX_FAILED = 12 +NL80211_STA_INFO_SIGNAL_AVG = 13 +NL80211_STA_INFO_RX_BITRATE = 14 +NL80211_STA_INFO_BSS_PARAM = 15 +NL80211_STA_INFO_CONNECTED_TIME = 16 +NL80211_STA_INFO_STA_FLAGS = 17 +NL80211_STA_INFO_BEACON_LOSS = 18 +NL80211_STA_INFO_T_OFFSET = 19 +NL80211_STA_INFO_LOCAL_PM = 20 +NL80211_STA_INFO_PEER_PM = 21 +NL80211_STA_INFO_NONPEER_PM = 22 +NL80211_STA_INFO_RX_BYTES64 = 23 +NL80211_STA_INFO_TX_BYTES64 = 24 +NL80211_STA_INFO_CHAIN_SIGNAL = 25 +NL80211_STA_INFO_CHAIN_SIGNAL_AVG = 26 +__NL80211_STA_INFO_AFTER_LAST = 27 +NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1 +NL80211_MPATH_FLAG_ACTIVE = 1 << 0 +NL80211_MPATH_FLAG_RESOLVING = 1 << 1 +NL80211_MPATH_FLAG_SN_VALID = 1 << 2 +NL80211_MPATH_FLAG_FIXED = 1 << 3 +NL80211_MPATH_FLAG_RESOLVED = 1 << 4 +__NL80211_MPATH_INFO_INVALID = 0 +NL80211_MPATH_INFO_FRAME_QLEN = 1 +NL80211_MPATH_INFO_SN = 2 +NL80211_MPATH_INFO_METRIC = 3 +NL80211_MPATH_INFO_EXPTIME = 4 +NL80211_MPATH_INFO_FLAGS = 5 +NL80211_MPATH_INFO_DISCOVERY_TIMEOUT = 6 +NL80211_MPATH_INFO_DISCOVERY_RETRIES = 7 +__NL80211_MPATH_INFO_AFTER_LAST = 8 +NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1 +__NL80211_BAND_ATTR_INVALID = 0 +NL80211_BAND_ATTR_FREQS = 1 +NL80211_BAND_ATTR_RATES = 2 +NL80211_BAND_ATTR_HT_MCS_SET = 3 +NL80211_BAND_ATTR_HT_CAPA = 4 +NL80211_BAND_ATTR_HT_AMPDU_FACTOR = 5 +NL80211_BAND_ATTR_HT_AMPDU_DENSITY = 6 +NL80211_BAND_ATTR_VHT_MCS_SET = 7 +NL80211_BAND_ATTR_VHT_CAPA = 8 +__NL80211_BAND_ATTR_AFTER_LAST = 9 +NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1 +__NL80211_FREQUENCY_ATTR_INVALID = 0 +NL80211_FREQUENCY_ATTR_FREQ = 1 +NL80211_FREQUENCY_ATTR_DISABLED = 2 +NL80211_FREQUENCY_ATTR_PASSIVE_SCAN = 3 +NL80211_FREQUENCY_ATTR_NO_IBSS = 4 +NL80211_FREQUENCY_ATTR_RADAR = 5 +NL80211_FREQUENCY_ATTR_MAX_TX_POWER = 6 +NL80211_FREQUENCY_ATTR_DFS_STATE = 7 +NL80211_FREQUENCY_ATTR_DFS_TIME = 8 +NL80211_FREQUENCY_ATTR_NO_HT40_MINUS = 9 +NL80211_FREQUENCY_ATTR_NO_HT40_PLUS = 10 +NL80211_FREQUENCY_ATTR_NO_80MHZ = 11 +NL80211_FREQUENCY_ATTR_NO_160MHZ = 12 +__NL80211_FREQUENCY_ATTR_AFTER_LAST = 13 +NL80211_FREQUENCY_ATTR_MAX = __NL80211_FREQUENCY_ATTR_AFTER_LAST - 1 +__NL80211_BITRATE_ATTR_INVALID = 0 +NL80211_BITRATE_ATTR_RATE = 1 +NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE = 2 +__NL80211_BITRATE_ATTR_AFTER_LAST = 3 +NL80211_BITRATE_ATTR_MAX = __NL80211_BITRATE_ATTR_AFTER_LAST - 1 +NL80211_REGDOM_SET_BY_CORE = 0 +NL80211_REGDOM_SET_BY_USER = 1 +NL80211_REGDOM_SET_BY_DRIVER = 2 +NL80211_REGDOM_SET_BY_COUNTRY_IE = 3 +NL80211_REGDOM_TYPE_COUNTRY = 0 +NL80211_REGDOM_TYPE_WORLD = 1 +NL80211_REGDOM_TYPE_CUSTOM_WORLD = 2 +NL80211_REGDOM_TYPE_INTERSECTION = 3 +__NL80211_REG_RULE_ATTR_INVALID = 0 +NL80211_ATTR_REG_RULE_FLAGS = 1 +NL80211_ATTR_FREQ_RANGE_START = 2 +NL80211_ATTR_FREQ_RANGE_END = 3 +NL80211_ATTR_FREQ_RANGE_MAX_BW = 4 +NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN = 5 +NL80211_ATTR_POWER_RULE_MAX_EIRP = 6 +__NL80211_REG_RULE_ATTR_AFTER_LAST = 7 +NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1 +__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID = 0 +NL80211_SCHED_SCAN_MATCH_ATTR_SSID = 1 +NL80211_SCHED_SCAN_MATCH_ATTR_RSSI = 2 +__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST = 3 +NL80211_SCHED_SCAN_MATCH_ATTR_MAX = __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1 +NL80211_RRF_NO_OFDM = 1 << 0 +NL80211_RRF_NO_CCK = 1 << 1 +NL80211_RRF_NO_INDOOR = 1 << 2 +NL80211_RRF_NO_OUTDOOR = 1 << 3 +NL80211_RRF_DFS = 1 << 4 +NL80211_RRF_PTP_ONLY = 1 << 5 +NL80211_RRF_PTMP_ONLY = 1 << 6 +NL80211_RRF_PASSIVE_SCAN = 1 << 7 +NL80211_RRF_NO_IBSS = 1 << 8 +NL80211_DFS_UNSET = 0 +NL80211_DFS_FCC = 1 +NL80211_DFS_ETSI = 2 +NL80211_DFS_JP = 3 +NL80211_USER_REG_HINT_USER = 0 +NL80211_USER_REG_HINT_CELL_BASE = 1 +__NL80211_SURVEY_INFO_INVALID = 0 +NL80211_SURVEY_INFO_FREQUENCY = 1 +NL80211_SURVEY_INFO_NOISE = 2 +NL80211_SURVEY_INFO_IN_USE = 3 +NL80211_SURVEY_INFO_CHANNEL_TIME = 4 +NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY = 5 +NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY = 6 +NL80211_SURVEY_INFO_CHANNEL_TIME_RX = 7 +NL80211_SURVEY_INFO_CHANNEL_TIME_TX = 8 +__NL80211_SURVEY_INFO_AFTER_LAST = 9 +NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1 +__NL80211_MNTR_FLAG_INVALID = 0 +NL80211_MNTR_FLAG_FCSFAIL = 1 +NL80211_MNTR_FLAG_PLCPFAIL = 2 +NL80211_MNTR_FLAG_CONTROL = 3 +NL80211_MNTR_FLAG_OTHER_BSS = 4 +NL80211_MNTR_FLAG_COOK_FRAMES = 5 +NL80211_MNTR_FLAG_ACTIVE = 6 +__NL80211_MNTR_FLAG_AFTER_LAST = 7 +NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1 +NL80211_MESH_POWER_UNKNOWN = 0 +NL80211_MESH_POWER_ACTIVE = 1 +NL80211_MESH_POWER_LIGHT_SLEEP = 2 +NL80211_MESH_POWER_DEEP_SLEEP = 3 +__NL80211_MESH_POWER_AFTER_LAST = 4 +NL80211_MESH_POWER_MAX = __NL80211_MESH_POWER_AFTER_LAST - 1 +__NL80211_MESHCONF_INVALID = 0 +NL80211_MESHCONF_RETRY_TIMEOUT = 1 +NL80211_MESHCONF_CONFIRM_TIMEOUT = 2 +NL80211_MESHCONF_HOLDING_TIMEOUT = 3 +NL80211_MESHCONF_MAX_PEER_LINKS = 4 +NL80211_MESHCONF_MAX_RETRIES = 5 +NL80211_MESHCONF_TTL = 6 +NL80211_MESHCONF_AUTO_OPEN_PLINKS = 7 +NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES = 8 +NL80211_MESHCONF_PATH_REFRESH_TIME = 9 +NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT = 10 +NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT = 11 +NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL = 12 +NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME = 13 +NL80211_MESHCONF_HWMP_ROOTMODE = 14 +NL80211_MESHCONF_ELEMENT_TTL = 15 +NL80211_MESHCONF_HWMP_RANN_INTERVAL = 16 +NL80211_MESHCONF_GATE_ANNOUNCEMENTS = 17 +NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL = 18 +NL80211_MESHCONF_FORWARDING = 19 +NL80211_MESHCONF_RSSI_THRESHOLD = 20 +NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR = 21 +NL80211_MESHCONF_HT_OPMODE = 22 +NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT = 23 +NL80211_MESHCONF_HWMP_ROOT_INTERVAL = 24 +NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL = 25 +NL80211_MESHCONF_POWER_MODE = 26 +NL80211_MESHCONF_AWAKE_WINDOW = 27 +NL80211_MESHCONF_PLINK_TIMEOUT = 28 +__NL80211_MESHCONF_ATTR_AFTER_LAST = 29 +NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1 +__NL80211_MESH_SETUP_INVALID = 0 +NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL = 1 +NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC = 2 +NL80211_MESH_SETUP_IE = 3 +NL80211_MESH_SETUP_USERSPACE_AUTH = 4 +NL80211_MESH_SETUP_USERSPACE_AMPE = 5 +NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC = 6 +NL80211_MESH_SETUP_USERSPACE_MPM = 7 +NL80211_MESH_SETUP_AUTH_PROTOCOL = 8 +__NL80211_MESH_SETUP_ATTR_AFTER_LAST = 9 +NL80211_MESH_SETUP_ATTR_MAX = __NL80211_MESH_SETUP_ATTR_AFTER_LAST - 1 +__NL80211_TXQ_ATTR_INVALID = 0 +NL80211_TXQ_ATTR_AC = 1 +NL80211_TXQ_ATTR_TXOP = 2 +NL80211_TXQ_ATTR_CWMIN = 3 +NL80211_TXQ_ATTR_CWMAX = 4 +NL80211_TXQ_ATTR_AIFS = 5 +__NL80211_TXQ_ATTR_AFTER_LAST = 6 +NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1 +NL80211_AC_VO = 0 +NL80211_AC_VI = 1 +NL80211_AC_BE = 2 +NL80211_AC_BK = 3 +NL80211_NUM_ACS = 4 +NL80211_CHAN_NO_HT = 0 +NL80211_CHAN_HT20 = 1 +NL80211_CHAN_HT40MINUS = 2 +NL80211_CHAN_HT40PLUS = 3 +NL80211_CHAN_WIDTH_20_NOHT = 0 +NL80211_CHAN_WIDTH_20 = 1 +NL80211_CHAN_WIDTH_40 = 2 +NL80211_CHAN_WIDTH_80 = 3 +NL80211_CHAN_WIDTH_80P80 = 4 +NL80211_CHAN_WIDTH_160 = 5 +__NL80211_BSS_INVALID = 0 +NL80211_BSS_BSSID = 1 +NL80211_BSS_FREQUENCY = 2 +NL80211_BSS_TSF = 3 +NL80211_BSS_BEACON_INTERVAL = 4 +NL80211_BSS_CAPABILITY = 5 +NL80211_BSS_INFORMATION_ELEMENTS = 6 +NL80211_BSS_SIGNAL_MBM = 7 +NL80211_BSS_SIGNAL_UNSPEC = 8 +NL80211_BSS_STATUS = 9 +NL80211_BSS_SEEN_MS_AGO = 10 +NL80211_BSS_BEACON_IES = 11 +__NL80211_BSS_AFTER_LAST = 12 +NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1 +NL80211_BSS_STATUS_AUTHENTICATED = 0 +NL80211_BSS_STATUS_ASSOCIATED = 1 +NL80211_BSS_STATUS_IBSS_JOINED = 2 +NL80211_AUTHTYPE_OPEN_SYSTEM = 0 +NL80211_AUTHTYPE_SHARED_KEY = 1 +NL80211_AUTHTYPE_FT = 2 +NL80211_AUTHTYPE_NETWORK_EAP = 3 +NL80211_AUTHTYPE_SAE = 4 +__NL80211_AUTHTYPE_NUM = 5 +NL80211_AUTHTYPE_MAX = __NL80211_AUTHTYPE_NUM - 1 +NL80211_AUTHTYPE_AUTOMATIC = 6 +NL80211_KEYTYPE_GROUP = 0 +NL80211_KEYTYPE_PAIRWISE = 1 +NL80211_KEYTYPE_PEERKEY = 2 +NUM_NL80211_KEYTYPES = 3 +NL80211_MFP_NO = 0 +NL80211_MFP_REQUIRED = 1 +NL80211_WPA_VERSION_1 = 1 << 0 +NL80211_WPA_VERSION_2 = 1 << 1 +__NL80211_KEY_DEFAULT_TYPE_INVALID = 0 +NL80211_KEY_DEFAULT_TYPE_UNICAST = 1 +NL80211_KEY_DEFAULT_TYPE_MULTICAST = 2 +NUM_NL80211_KEY_DEFAULT_TYPES = 3 +__NL80211_KEY_INVALID = 0 +NL80211_KEY_DATA = 1 +NL80211_KEY_IDX = 2 +NL80211_KEY_CIPHER = 3 +NL80211_KEY_SEQ = 4 +NL80211_KEY_DEFAULT = 5 +NL80211_KEY_DEFAULT_MGMT = 6 +NL80211_KEY_TYPE = 7 +NL80211_KEY_DEFAULT_TYPES = 8 +__NL80211_KEY_AFTER_LAST = 9 +NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1 +__NL80211_TXRATE_INVALID = 0 +NL80211_TXRATE_LEGACY = 1 +NL80211_TXRATE_MCS = 2 +__NL80211_TXRATE_AFTER_LAST = 3 +NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1 +NL80211_BAND_2GHZ = 0 +NL80211_BAND_5GHZ = 1 +NL80211_BAND_60GHZ = 2 +NL80211_PS_DISABLED = 0 +NL80211_PS_ENABLED = 1 +__NL80211_ATTR_CQM_INVALID = 0 +NL80211_ATTR_CQM_RSSI_THOLD = 1 +NL80211_ATTR_CQM_RSSI_HYST = 2 +NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT = 3 +NL80211_ATTR_CQM_PKT_LOSS_EVENT = 4 +NL80211_ATTR_CQM_TXE_RATE = 5 +NL80211_ATTR_CQM_TXE_PKTS = 6 +NL80211_ATTR_CQM_TXE_INTVL = 7 +__NL80211_ATTR_CQM_AFTER_LAST = 8 +NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1 +NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW = 0 +NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH = 1 +NL80211_CQM_RSSI_BEACON_LOSS_EVENT = 2 +NL80211_TX_POWER_AUTOMATIC = 0 +NL80211_TX_POWER_LIMITED = 1 +NL80211_TX_POWER_FIXED = 2 +__NL80211_WOWLAN_PKTPAT_INVALID = 0 +NL80211_WOWLAN_PKTPAT_MASK = 1 +NL80211_WOWLAN_PKTPAT_PATTERN = 2 +NL80211_WOWLAN_PKTPAT_OFFSET = 3 +NUM_NL80211_WOWLAN_PKTPAT = 4 +MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1 +__NL80211_WOWLAN_TRIG_INVALID = 0 +NL80211_WOWLAN_TRIG_ANY = 1 +NL80211_WOWLAN_TRIG_DISCONNECT = 2 +NL80211_WOWLAN_TRIG_MAGIC_PKT = 3 +NL80211_WOWLAN_TRIG_PKT_PATTERN = 4 +NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED = 5 +NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE = 6 +NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST = 7 +NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE = 8 +NL80211_WOWLAN_TRIG_RFKILL_RELEASE = 9 +NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211 = 10 +NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN = 11 +NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023 = 12 +NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN = 13 +NL80211_WOWLAN_TRIG_TCP_CONNECTION = 14 +NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH = 15 +NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST = 16 +NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS = 17 +NUM_NL80211_WOWLAN_TRIG = 18 +MAX_NL80211_WOWLAN_TRIG = NUM_NL80211_WOWLAN_TRIG - 1 +__NL80211_WOWLAN_TCP_INVALID = 0 +NL80211_WOWLAN_TCP_SRC_IPV4 = 1 +NL80211_WOWLAN_TCP_DST_IPV4 = 2 +NL80211_WOWLAN_TCP_DST_MAC = 3 +NL80211_WOWLAN_TCP_SRC_PORT = 4 +NL80211_WOWLAN_TCP_DST_PORT = 5 +NL80211_WOWLAN_TCP_DATA_PAYLOAD = 6 +NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ = 7 +NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN = 8 +NL80211_WOWLAN_TCP_DATA_INTERVAL = 9 +NL80211_WOWLAN_TCP_WAKE_PAYLOAD = 10 +NL80211_WOWLAN_TCP_WAKE_MASK = 11 +NUM_NL80211_WOWLAN_TCP = 12 +MAX_NL80211_WOWLAN_TCP = NUM_NL80211_WOWLAN_TCP - 1 +NL80211_IFACE_LIMIT_UNSPEC = 0 +NL80211_IFACE_LIMIT_MAX = 1 +NL80211_IFACE_LIMIT_TYPES = 2 +NUM_NL80211_IFACE_LIMIT = 3 +MAX_NL80211_IFACE_LIMIT = NUM_NL80211_IFACE_LIMIT - 1 +NL80211_IFACE_COMB_UNSPEC = 0 +NL80211_IFACE_COMB_LIMITS = 1 +NL80211_IFACE_COMB_MAXNUM = 2 +NL80211_IFACE_COMB_STA_AP_BI_MATCH = 3 +NL80211_IFACE_COMB_NUM_CHANNELS = 4 +NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS = 5 +NUM_NL80211_IFACE_COMB = 6 +MAX_NL80211_IFACE_COMB = NUM_NL80211_IFACE_COMB - 1 +NL80211_PLINK_LISTEN = 0 +NL80211_PLINK_OPN_SNT = 1 +NL80211_PLINK_OPN_RCVD = 2 +NL80211_PLINK_CNF_RCVD = 3 +NL80211_PLINK_ESTAB = 4 +NL80211_PLINK_HOLDING = 5 +NL80211_PLINK_BLOCKED = 6 +NUM_NL80211_PLINK_STATES = 7 +MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1 +NL80211_PLINK_ACTION_NO_ACTION = 0 +NL80211_PLINK_ACTION_OPEN = 1 +NL80211_PLINK_ACTION_BLOCK = 2 +NUM_NL80211_PLINK_ACTIONS = 3 +__NL80211_REKEY_DATA_INVALID = 0 +NL80211_REKEY_DATA_KEK = 1 +NL80211_REKEY_DATA_KCK = 2 +NL80211_REKEY_DATA_REPLAY_CTR = 3 +NUM_NL80211_REKEY_DATA = 4 +MAX_NL80211_REKEY_DATA = NUM_NL80211_REKEY_DATA - 1 +NL80211_HIDDEN_SSID_NOT_IN_USE = 0 +NL80211_HIDDEN_SSID_ZERO_LEN = 1 +NL80211_HIDDEN_SSID_ZERO_CONTENTS = 2 +__NL80211_STA_WME_INVALID = 0 +NL80211_STA_WME_UAPSD_QUEUES = 1 +NL80211_STA_WME_MAX_SP = 2 +__NL80211_STA_WME_AFTER_LAST = 3 +NL80211_STA_WME_MAX = __NL80211_STA_WME_AFTER_LAST - 1 +__NL80211_PMKSA_CANDIDATE_INVALID = 0 +NL80211_PMKSA_CANDIDATE_INDEX = 1 +NL80211_PMKSA_CANDIDATE_BSSID = 2 +NL80211_PMKSA_CANDIDATE_PREAUTH = 3 +NUM_NL80211_PMKSA_CANDIDATE = 4 +MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1 +NL80211_TDLS_DISCOVERY_REQ = 0 +NL80211_TDLS_SETUP = 1 +NL80211_TDLS_TEARDOWN = 2 +NL80211_TDLS_ENABLE_LINK = 3 +NL80211_TDLS_DISABLE_LINK = 4 +NL80211_FEATURE_SK_TX_STATUS = 1 << 0 +NL80211_FEATURE_HT_IBSS = 1 << 1 +NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2 +NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3 +NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL = 1 << 4 +NL80211_FEATURE_SAE = 1 << 5 +NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6 +NL80211_FEATURE_SCAN_FLUSH = 1 << 7 +NL80211_FEATURE_AP_SCAN = 1 << 8 +NL80211_FEATURE_VIF_TXPOWER = 1 << 9 +NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10 +NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11 +NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12 +NL80211_FEATURE_ADVERTISE_CHAN_LIMITS = 1 << 14 +NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 15 +NL80211_FEATURE_USERSPACE_MPM = 1 << 16 +NL80211_FEATURE_ACTIVE_MONITOR = 1 << 17 +NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS = 1 << 0 +NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 = 1 << 1 +NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P = 1 << 2 +NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U = 1 << 3 +NL80211_CONN_FAIL_MAX_CLIENTS = 0 +NL80211_CONN_FAIL_BLOCKED_CLIENT = 1 +NL80211_SCAN_FLAG_LOW_PRIORITY = 1 << 0 +NL80211_SCAN_FLAG_FLUSH = 1 << 1 +NL80211_SCAN_FLAG_AP = 1 << 2 +NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED = 0 +NL80211_ACL_POLICY_DENY_UNLESS_LISTED = 1 +NL80211_RADAR_DETECTED = 0 +NL80211_RADAR_CAC_FINISHED = 1 +NL80211_RADAR_CAC_ABORTED = 2 +NL80211_RADAR_NOP_FINISHED = 3 +NL80211_DFS_USABLE = 0 +NL80211_DFS_UNAVAILABLE = 1 +NL80211_DFS_AVAILABLE = 2 +NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP = 1 << 0 +NL80211_CRIT_PROTO_UNSPEC = 0 +NL80211_CRIT_PROTO_DHCP = 1 +NL80211_CRIT_PROTO_EAPOL = 2 +NL80211_CRIT_PROTO_APIPA = 3 +NUM_NL80211_CRIT_PROTO = 4 +nl80211_commands2str = { + NL80211_CMD_UNSPEC: "NL80211_CMD_UNSPEC", + NL80211_CMD_GET_WIPHY: "NL80211_CMD_GET_WIPHY", + NL80211_CMD_SET_WIPHY: "NL80211_CMD_SET_WIPHY", + NL80211_CMD_NEW_WIPHY: "NL80211_CMD_NEW_WIPHY", + NL80211_CMD_DEL_WIPHY: "NL80211_CMD_DEL_WIPHY", + NL80211_CMD_GET_INTERFACE: "NL80211_CMD_GET_INTERFACE", + NL80211_CMD_SET_INTERFACE: "NL80211_CMD_SET_INTERFACE", + NL80211_CMD_NEW_INTERFACE: "NL80211_CMD_NEW_INTERFACE", + NL80211_CMD_DEL_INTERFACE: "NL80211_CMD_DEL_INTERFACE", + NL80211_CMD_GET_KEY: "NL80211_CMD_GET_KEY", + NL80211_CMD_SET_KEY: "NL80211_CMD_SET_KEY", + NL80211_CMD_NEW_KEY: "NL80211_CMD_NEW_KEY", + NL80211_CMD_DEL_KEY: "NL80211_CMD_DEL_KEY", + NL80211_CMD_GET_BEACON: "NL80211_CMD_GET_BEACON", + NL80211_CMD_SET_BEACON: "NL80211_CMD_SET_BEACON", + NL80211_CMD_START_AP: "NL80211_CMD_START_AP", + NL80211_CMD_STOP_AP: "NL80211_CMD_STOP_AP", + NL80211_CMD_GET_STATION: "NL80211_CMD_GET_STATION", + NL80211_CMD_SET_STATION: "NL80211_CMD_SET_STATION", + NL80211_CMD_NEW_STATION: "NL80211_CMD_NEW_STATION", + NL80211_CMD_DEL_STATION: "NL80211_CMD_DEL_STATION", + NL80211_CMD_GET_MPATH: "NL80211_CMD_GET_MPATH", + NL80211_CMD_SET_MPATH: "NL80211_CMD_SET_MPATH", + NL80211_CMD_NEW_MPATH: "NL80211_CMD_NEW_MPATH", + NL80211_CMD_DEL_MPATH: "NL80211_CMD_DEL_MPATH", + NL80211_CMD_SET_BSS: "NL80211_CMD_SET_BSS", + NL80211_CMD_SET_REG: "NL80211_CMD_SET_REG", + NL80211_CMD_REQ_SET_REG: "NL80211_CMD_REQ_SET_REG", + NL80211_CMD_GET_MESH_CONFIG: "NL80211_CMD_GET_MESH_CONFIG", + NL80211_CMD_SET_MESH_CONFIG: "NL80211_CMD_SET_MESH_CONFIG", + NL80211_CMD_SET_MGMT_EXTRA_IE: "NL80211_CMD_SET_MGMT_EXTRA_IE", + NL80211_CMD_GET_REG: "NL80211_CMD_GET_REG", + NL80211_CMD_GET_SCAN: "NL80211_CMD_GET_SCAN", + NL80211_CMD_TRIGGER_SCAN: "NL80211_CMD_TRIGGER_SCAN", + NL80211_CMD_NEW_SCAN_RESULTS: "NL80211_CMD_NEW_SCAN_RESULTS", + NL80211_CMD_SCAN_ABORTED: "NL80211_CMD_SCAN_ABORTED", + NL80211_CMD_REG_CHANGE: "NL80211_CMD_REG_CHANGE", + NL80211_CMD_AUTHENTICATE: "NL80211_CMD_AUTHENTICATE", + NL80211_CMD_ASSOCIATE: "NL80211_CMD_ASSOCIATE", + NL80211_CMD_DEAUTHENTICATE: "NL80211_CMD_DEAUTHENTICATE", + NL80211_CMD_DISASSOCIATE: "NL80211_CMD_DISASSOCIATE", + NL80211_CMD_MICHAEL_MIC_FAILURE: "NL80211_CMD_MICHAEL_MIC_FAILURE", + NL80211_CMD_REG_BEACON_HINT: "NL80211_CMD_REG_BEACON_HINT", + NL80211_CMD_JOIN_IBSS: "NL80211_CMD_JOIN_IBSS", + NL80211_CMD_LEAVE_IBSS: "NL80211_CMD_LEAVE_IBSS", + NL80211_CMD_TESTMODE: "NL80211_CMD_TESTMODE", + NL80211_CMD_CONNECT: "NL80211_CMD_CONNECT", + NL80211_CMD_ROAM: "NL80211_CMD_ROAM", + NL80211_CMD_DISCONNECT: "NL80211_CMD_DISCONNECT", + NL80211_CMD_SET_WIPHY_NETNS: "NL80211_CMD_SET_WIPHY_NETNS", + NL80211_CMD_GET_SURVEY: "NL80211_CMD_GET_SURVEY", + NL80211_CMD_NEW_SURVEY_RESULTS: "NL80211_CMD_NEW_SURVEY_RESULTS", + NL80211_CMD_SET_PMKSA: "NL80211_CMD_SET_PMKSA", + NL80211_CMD_DEL_PMKSA: "NL80211_CMD_DEL_PMKSA", + NL80211_CMD_FLUSH_PMKSA: "NL80211_CMD_FLUSH_PMKSA", + NL80211_CMD_REMAIN_ON_CHANNEL: "NL80211_CMD_REMAIN_ON_CHANNEL", + NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: "NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL", + NL80211_CMD_SET_TX_BITRATE_MASK: "NL80211_CMD_SET_TX_BITRATE_MASK", + NL80211_CMD_REGISTER_FRAME: "NL80211_CMD_REGISTER_FRAME", + NL80211_CMD_FRAME: "NL80211_CMD_FRAME", + NL80211_CMD_FRAME_TX_STATUS: "NL80211_CMD_FRAME_TX_STATUS", + NL80211_CMD_SET_POWER_SAVE: "NL80211_CMD_SET_POWER_SAVE", + NL80211_CMD_GET_POWER_SAVE: "NL80211_CMD_GET_POWER_SAVE", + NL80211_CMD_SET_CQM: "NL80211_CMD_SET_CQM", + NL80211_CMD_NOTIFY_CQM: "NL80211_CMD_NOTIFY_CQM", + NL80211_CMD_SET_CHANNEL: "NL80211_CMD_SET_CHANNEL", + NL80211_CMD_SET_WDS_PEER: "NL80211_CMD_SET_WDS_PEER", + NL80211_CMD_FRAME_WAIT_CANCEL: "NL80211_CMD_FRAME_WAIT_CANCEL", + NL80211_CMD_JOIN_MESH: "NL80211_CMD_JOIN_MESH", + NL80211_CMD_LEAVE_MESH: "NL80211_CMD_LEAVE_MESH", + NL80211_CMD_UNPROT_DEAUTHENTICATE: "NL80211_CMD_UNPROT_DEAUTHENTICATE", + NL80211_CMD_UNPROT_DISASSOCIATE: "NL80211_CMD_UNPROT_DISASSOCIATE", + NL80211_CMD_NEW_PEER_CANDIDATE: "NL80211_CMD_NEW_PEER_CANDIDATE", + NL80211_CMD_GET_WOWLAN: "NL80211_CMD_GET_WOWLAN", + NL80211_CMD_SET_WOWLAN: "NL80211_CMD_SET_WOWLAN", + NL80211_CMD_START_SCHED_SCAN: "NL80211_CMD_START_SCHED_SCAN", + NL80211_CMD_STOP_SCHED_SCAN: "NL80211_CMD_STOP_SCHED_SCAN", + NL80211_CMD_SCHED_SCAN_RESULTS: "NL80211_CMD_SCHED_SCAN_RESULTS", + NL80211_CMD_SCHED_SCAN_STOPPED: "NL80211_CMD_SCHED_SCAN_STOPPED", + NL80211_CMD_SET_REKEY_OFFLOAD: "NL80211_CMD_SET_REKEY_OFFLOAD", + NL80211_CMD_PMKSA_CANDIDATE: "NL80211_CMD_PMKSA_CANDIDATE", + NL80211_CMD_TDLS_OPER: "NL80211_CMD_TDLS_OPER", + NL80211_CMD_TDLS_MGMT: "NL80211_CMD_TDLS_MGMT", + NL80211_CMD_UNEXPECTED_FRAME: "NL80211_CMD_UNEXPECTED_FRAME", + NL80211_CMD_PROBE_CLIENT: "NL80211_CMD_PROBE_CLIENT", + NL80211_CMD_REGISTER_BEACONS: "NL80211_CMD_REGISTER_BEACONS", + NL80211_CMD_UNEXPECTED_4ADDR_FRAME: "NL80211_CMD_UNEXPECTED_4ADDR_FRAME", + NL80211_CMD_SET_NOACK_MAP: "NL80211_CMD_SET_NOACK_MAP", + NL80211_CMD_CH_SWITCH_NOTIFY: "NL80211_CMD_CH_SWITCH_NOTIFY", + NL80211_CMD_START_P2P_DEVICE: "NL80211_CMD_START_P2P_DEVICE", + NL80211_CMD_STOP_P2P_DEVICE: "NL80211_CMD_STOP_P2P_DEVICE", + NL80211_CMD_CONN_FAILED: "NL80211_CMD_CONN_FAILED", + NL80211_CMD_SET_MCAST_RATE: "NL80211_CMD_SET_MCAST_RATE", + NL80211_CMD_SET_MAC_ACL: "NL80211_CMD_SET_MAC_ACL", + NL80211_CMD_RADAR_DETECT: "NL80211_CMD_RADAR_DETECT", + NL80211_CMD_GET_PROTOCOL_FEATURES: "NL80211_CMD_GET_PROTOCOL_FEATURES", + NL80211_CMD_UPDATE_FT_IES: "NL80211_CMD_UPDATE_FT_IES", + NL80211_CMD_FT_EVENT: "NL80211_CMD_FT_EVENT", + NL80211_CMD_CRIT_PROTOCOL_START: "NL80211_CMD_CRIT_PROTOCOL_START", + NL80211_CMD_CRIT_PROTOCOL_STOP: "NL80211_CMD_CRIT_PROTOCOL_STOP", + __NL80211_CMD_AFTER_LAST: "__NL80211_CMD_AFTER_LAST", +} +nl80211_attrs2str = { + NL80211_ATTR_UNSPEC: "NL80211_ATTR_UNSPEC", + NL80211_ATTR_WIPHY: "NL80211_ATTR_WIPHY", + NL80211_ATTR_WIPHY_NAME: "NL80211_ATTR_WIPHY_NAME", + NL80211_ATTR_IFINDEX: "NL80211_ATTR_IFINDEX", + NL80211_ATTR_IFNAME: "NL80211_ATTR_IFNAME", + NL80211_ATTR_IFTYPE: "NL80211_ATTR_IFTYPE", + NL80211_ATTR_MAC: "NL80211_ATTR_MAC", + NL80211_ATTR_KEY_DATA: "NL80211_ATTR_KEY_DATA", + NL80211_ATTR_KEY_IDX: "NL80211_ATTR_KEY_IDX", + NL80211_ATTR_KEY_CIPHER: "NL80211_ATTR_KEY_CIPHER", + NL80211_ATTR_KEY_SEQ: "NL80211_ATTR_KEY_SEQ", + NL80211_ATTR_KEY_DEFAULT: "NL80211_ATTR_KEY_DEFAULT", + NL80211_ATTR_BEACON_INTERVAL: "NL80211_ATTR_BEACON_INTERVAL", + NL80211_ATTR_DTIM_PERIOD: "NL80211_ATTR_DTIM_PERIOD", + NL80211_ATTR_BEACON_HEAD: "NL80211_ATTR_BEACON_HEAD", + NL80211_ATTR_BEACON_TAIL: "NL80211_ATTR_BEACON_TAIL", + NL80211_ATTR_STA_AID: "NL80211_ATTR_STA_AID", + NL80211_ATTR_STA_FLAGS: "NL80211_ATTR_STA_FLAGS", + NL80211_ATTR_STA_LISTEN_INTERVAL: "NL80211_ATTR_STA_LISTEN_INTERVAL", + NL80211_ATTR_STA_SUPPORTED_RATES: "NL80211_ATTR_STA_SUPPORTED_RATES", + NL80211_ATTR_STA_VLAN: "NL80211_ATTR_STA_VLAN", + NL80211_ATTR_STA_INFO: "NL80211_ATTR_STA_INFO", + NL80211_ATTR_WIPHY_BANDS: "NL80211_ATTR_WIPHY_BANDS", + NL80211_ATTR_MNTR_FLAGS: "NL80211_ATTR_MNTR_FLAGS", + NL80211_ATTR_MESH_ID: "NL80211_ATTR_MESH_ID", + NL80211_ATTR_STA_PLINK_ACTION: "NL80211_ATTR_STA_PLINK_ACTION", + NL80211_ATTR_MPATH_NEXT_HOP: "NL80211_ATTR_MPATH_NEXT_HOP", + NL80211_ATTR_MPATH_INFO: "NL80211_ATTR_MPATH_INFO", + NL80211_ATTR_BSS_CTS_PROT: "NL80211_ATTR_BSS_CTS_PROT", + NL80211_ATTR_BSS_SHORT_PREAMBLE: "NL80211_ATTR_BSS_SHORT_PREAMBLE", + NL80211_ATTR_BSS_SHORT_SLOT_TIME: "NL80211_ATTR_BSS_SHORT_SLOT_TIME", + NL80211_ATTR_HT_CAPABILITY: "NL80211_ATTR_HT_CAPABILITY", + NL80211_ATTR_SUPPORTED_IFTYPES: "NL80211_ATTR_SUPPORTED_IFTYPES", + NL80211_ATTR_REG_ALPHA2: "NL80211_ATTR_REG_ALPHA2", + NL80211_ATTR_REG_RULES: "NL80211_ATTR_REG_RULES", + NL80211_ATTR_MESH_CONFIG: "NL80211_ATTR_MESH_CONFIG", + NL80211_ATTR_BSS_BASIC_RATES: "NL80211_ATTR_BSS_BASIC_RATES", + NL80211_ATTR_WIPHY_TXQ_PARAMS: "NL80211_ATTR_WIPHY_TXQ_PARAMS", + NL80211_ATTR_WIPHY_FREQ: "NL80211_ATTR_WIPHY_FREQ", + NL80211_ATTR_WIPHY_CHANNEL_TYPE: "NL80211_ATTR_WIPHY_CHANNEL_TYPE", + NL80211_ATTR_KEY_DEFAULT_MGMT: "NL80211_ATTR_KEY_DEFAULT_MGMT", + NL80211_ATTR_MGMT_SUBTYPE: "NL80211_ATTR_MGMT_SUBTYPE", + NL80211_ATTR_IE: "NL80211_ATTR_IE", + NL80211_ATTR_MAX_NUM_SCAN_SSIDS: "NL80211_ATTR_MAX_NUM_SCAN_SSIDS", + NL80211_ATTR_SCAN_FREQUENCIES: "NL80211_ATTR_SCAN_FREQUENCIES", + NL80211_ATTR_SCAN_SSIDS: "NL80211_ATTR_SCAN_SSIDS", + NL80211_ATTR_GENERATION: "NL80211_ATTR_GENERATION", + NL80211_ATTR_BSS: "NL80211_ATTR_BSS", + NL80211_ATTR_REG_INITIATOR: "NL80211_ATTR_REG_INITIATOR", + NL80211_ATTR_REG_TYPE: "NL80211_ATTR_REG_TYPE", + NL80211_ATTR_SUPPORTED_COMMANDS: "NL80211_ATTR_SUPPORTED_COMMANDS", + NL80211_ATTR_FRAME: "NL80211_ATTR_FRAME", + NL80211_ATTR_SSID: "NL80211_ATTR_SSID", + NL80211_ATTR_AUTH_TYPE: "NL80211_ATTR_AUTH_TYPE", + NL80211_ATTR_REASON_CODE: "NL80211_ATTR_REASON_CODE", + NL80211_ATTR_KEY_TYPE: "NL80211_ATTR_KEY_TYPE", + NL80211_ATTR_MAX_SCAN_IE_LEN: "NL80211_ATTR_MAX_SCAN_IE_LEN", + NL80211_ATTR_CIPHER_SUITES: "NL80211_ATTR_CIPHER_SUITES", + NL80211_ATTR_FREQ_BEFORE: "NL80211_ATTR_FREQ_BEFORE", + NL80211_ATTR_FREQ_AFTER: "NL80211_ATTR_FREQ_AFTER", + NL80211_ATTR_FREQ_FIXED: "NL80211_ATTR_FREQ_FIXED", + NL80211_ATTR_WIPHY_RETRY_SHORT: "NL80211_ATTR_WIPHY_RETRY_SHORT", + NL80211_ATTR_WIPHY_RETRY_LONG: "NL80211_ATTR_WIPHY_RETRY_LONG", + NL80211_ATTR_WIPHY_FRAG_THRESHOLD: "NL80211_ATTR_WIPHY_FRAG_THRESHOLD", + NL80211_ATTR_WIPHY_RTS_THRESHOLD: "NL80211_ATTR_WIPHY_RTS_THRESHOLD", + NL80211_ATTR_TIMED_OUT: "NL80211_ATTR_TIMED_OUT", + NL80211_ATTR_USE_MFP: "NL80211_ATTR_USE_MFP", + NL80211_ATTR_STA_FLAGS2: "NL80211_ATTR_STA_FLAGS2", + NL80211_ATTR_CONTROL_PORT: "NL80211_ATTR_CONTROL_PORT", + NL80211_ATTR_TESTDATA: "NL80211_ATTR_TESTDATA", + NL80211_ATTR_PRIVACY: "NL80211_ATTR_PRIVACY", + NL80211_ATTR_DISCONNECTED_BY_AP: "NL80211_ATTR_DISCONNECTED_BY_AP", + NL80211_ATTR_STATUS_CODE: "NL80211_ATTR_STATUS_CODE", + NL80211_ATTR_CIPHER_SUITES_PAIRWISE: "NL80211_ATTR_CIPHER_SUITES_PAIRWISE", + NL80211_ATTR_CIPHER_SUITE_GROUP: "NL80211_ATTR_CIPHER_SUITE_GROUP", + NL80211_ATTR_WPA_VERSIONS: "NL80211_ATTR_WPA_VERSIONS", + NL80211_ATTR_AKM_SUITES: "NL80211_ATTR_AKM_SUITES", + NL80211_ATTR_REQ_IE: "NL80211_ATTR_REQ_IE", + NL80211_ATTR_RESP_IE: "NL80211_ATTR_RESP_IE", + NL80211_ATTR_PREV_BSSID: "NL80211_ATTR_PREV_BSSID", + NL80211_ATTR_KEY: "NL80211_ATTR_KEY", + NL80211_ATTR_KEYS: "NL80211_ATTR_KEYS", + NL80211_ATTR_PID: "NL80211_ATTR_PID", + NL80211_ATTR_4ADDR: "NL80211_ATTR_4ADDR", + NL80211_ATTR_SURVEY_INFO: "NL80211_ATTR_SURVEY_INFO", + NL80211_ATTR_PMKID: "NL80211_ATTR_PMKID", + NL80211_ATTR_MAX_NUM_PMKIDS: "NL80211_ATTR_MAX_NUM_PMKIDS", + NL80211_ATTR_DURATION: "NL80211_ATTR_DURATION", + NL80211_ATTR_COOKIE: "NL80211_ATTR_COOKIE", + NL80211_ATTR_WIPHY_COVERAGE_CLASS: "NL80211_ATTR_WIPHY_COVERAGE_CLASS", + NL80211_ATTR_TX_RATES: "NL80211_ATTR_TX_RATES", + NL80211_ATTR_FRAME_MATCH: "NL80211_ATTR_FRAME_MATCH", + NL80211_ATTR_ACK: "NL80211_ATTR_ACK", + NL80211_ATTR_PS_STATE: "NL80211_ATTR_PS_STATE", + NL80211_ATTR_CQM: "NL80211_ATTR_CQM", + NL80211_ATTR_LOCAL_STATE_CHANGE: "NL80211_ATTR_LOCAL_STATE_CHANGE", + NL80211_ATTR_AP_ISOLATE: "NL80211_ATTR_AP_ISOLATE", + NL80211_ATTR_WIPHY_TX_POWER_SETTING: "NL80211_ATTR_WIPHY_TX_POWER_SETTING", + NL80211_ATTR_WIPHY_TX_POWER_LEVEL: "NL80211_ATTR_WIPHY_TX_POWER_LEVEL", + NL80211_ATTR_TX_FRAME_TYPES: "NL80211_ATTR_TX_FRAME_TYPES", + NL80211_ATTR_RX_FRAME_TYPES: "NL80211_ATTR_RX_FRAME_TYPES", + NL80211_ATTR_FRAME_TYPE: "NL80211_ATTR_FRAME_TYPE", + NL80211_ATTR_CONTROL_PORT_ETHERTYPE: "NL80211_ATTR_CONTROL_PORT_ETHERTYPE", + NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: "NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT", + NL80211_ATTR_SUPPORT_IBSS_RSN: "NL80211_ATTR_SUPPORT_IBSS_RSN", + NL80211_ATTR_WIPHY_ANTENNA_TX: "NL80211_ATTR_WIPHY_ANTENNA_TX", + NL80211_ATTR_WIPHY_ANTENNA_RX: "NL80211_ATTR_WIPHY_ANTENNA_RX", + NL80211_ATTR_MCAST_RATE: "NL80211_ATTR_MCAST_RATE", + NL80211_ATTR_OFFCHANNEL_TX_OK: "NL80211_ATTR_OFFCHANNEL_TX_OK", + NL80211_ATTR_BSS_HT_OPMODE: "NL80211_ATTR_BSS_HT_OPMODE", + NL80211_ATTR_KEY_DEFAULT_TYPES: "NL80211_ATTR_KEY_DEFAULT_TYPES", + NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION: "NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION", + NL80211_ATTR_MESH_SETUP: "NL80211_ATTR_MESH_SETUP", + NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX: "NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX", + NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX: "NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX", + NL80211_ATTR_SUPPORT_MESH_AUTH: "NL80211_ATTR_SUPPORT_MESH_AUTH", + NL80211_ATTR_STA_PLINK_STATE: "NL80211_ATTR_STA_PLINK_STATE", + NL80211_ATTR_WOWLAN_TRIGGERS: "NL80211_ATTR_WOWLAN_TRIGGERS", + NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: "NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED", + NL80211_ATTR_SCHED_SCAN_INTERVAL: "NL80211_ATTR_SCHED_SCAN_INTERVAL", + NL80211_ATTR_INTERFACE_COMBINATIONS: "NL80211_ATTR_INTERFACE_COMBINATIONS", + NL80211_ATTR_SOFTWARE_IFTYPES: "NL80211_ATTR_SOFTWARE_IFTYPES", + NL80211_ATTR_REKEY_DATA: "NL80211_ATTR_REKEY_DATA", + NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS: "NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS", + NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN: "NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN", + NL80211_ATTR_SCAN_SUPP_RATES: "NL80211_ATTR_SCAN_SUPP_RATES", + NL80211_ATTR_HIDDEN_SSID: "NL80211_ATTR_HIDDEN_SSID", + NL80211_ATTR_IE_PROBE_RESP: "NL80211_ATTR_IE_PROBE_RESP", + NL80211_ATTR_IE_ASSOC_RESP: "NL80211_ATTR_IE_ASSOC_RESP", + NL80211_ATTR_STA_WME: "NL80211_ATTR_STA_WME", + NL80211_ATTR_SUPPORT_AP_UAPSD: "NL80211_ATTR_SUPPORT_AP_UAPSD", + NL80211_ATTR_ROAM_SUPPORT: "NL80211_ATTR_ROAM_SUPPORT", + NL80211_ATTR_SCHED_SCAN_MATCH: "NL80211_ATTR_SCHED_SCAN_MATCH", + NL80211_ATTR_MAX_MATCH_SETS: "NL80211_ATTR_MAX_MATCH_SETS", + NL80211_ATTR_PMKSA_CANDIDATE: "NL80211_ATTR_PMKSA_CANDIDATE", + NL80211_ATTR_TX_NO_CCK_RATE: "NL80211_ATTR_TX_NO_CCK_RATE", + NL80211_ATTR_TDLS_ACTION: "NL80211_ATTR_TDLS_ACTION", + NL80211_ATTR_TDLS_DIALOG_TOKEN: "NL80211_ATTR_TDLS_DIALOG_TOKEN", + NL80211_ATTR_TDLS_OPERATION: "NL80211_ATTR_TDLS_OPERATION", + NL80211_ATTR_TDLS_SUPPORT: "NL80211_ATTR_TDLS_SUPPORT", + NL80211_ATTR_TDLS_EXTERNAL_SETUP: "NL80211_ATTR_TDLS_EXTERNAL_SETUP", + NL80211_ATTR_DEVICE_AP_SME: "NL80211_ATTR_DEVICE_AP_SME", + NL80211_ATTR_DONT_WAIT_FOR_ACK: "NL80211_ATTR_DONT_WAIT_FOR_ACK", + NL80211_ATTR_FEATURE_FLAGS: "NL80211_ATTR_FEATURE_FLAGS", + NL80211_ATTR_PROBE_RESP_OFFLOAD: "NL80211_ATTR_PROBE_RESP_OFFLOAD", + NL80211_ATTR_PROBE_RESP: "NL80211_ATTR_PROBE_RESP", + NL80211_ATTR_DFS_REGION: "NL80211_ATTR_DFS_REGION", + NL80211_ATTR_DISABLE_HT: "NL80211_ATTR_DISABLE_HT", + NL80211_ATTR_HT_CAPABILITY_MASK: "NL80211_ATTR_HT_CAPABILITY_MASK", + NL80211_ATTR_NOACK_MAP: "NL80211_ATTR_NOACK_MAP", + NL80211_ATTR_INACTIVITY_TIMEOUT: "NL80211_ATTR_INACTIVITY_TIMEOUT", + NL80211_ATTR_RX_SIGNAL_DBM: "NL80211_ATTR_RX_SIGNAL_DBM", + NL80211_ATTR_BG_SCAN_PERIOD: "NL80211_ATTR_BG_SCAN_PERIOD", + NL80211_ATTR_WDEV: "NL80211_ATTR_WDEV", + NL80211_ATTR_USER_REG_HINT_TYPE: "NL80211_ATTR_USER_REG_HINT_TYPE", + NL80211_ATTR_CONN_FAILED_REASON: "NL80211_ATTR_CONN_FAILED_REASON", + NL80211_ATTR_SAE_DATA: "NL80211_ATTR_SAE_DATA", + NL80211_ATTR_VHT_CAPABILITY: "NL80211_ATTR_VHT_CAPABILITY", + NL80211_ATTR_SCAN_FLAGS: "NL80211_ATTR_SCAN_FLAGS", + NL80211_ATTR_CHANNEL_WIDTH: "NL80211_ATTR_CHANNEL_WIDTH", + NL80211_ATTR_CENTER_FREQ1: "NL80211_ATTR_CENTER_FREQ1", + NL80211_ATTR_CENTER_FREQ2: "NL80211_ATTR_CENTER_FREQ2", + NL80211_ATTR_P2P_CTWINDOW: "NL80211_ATTR_P2P_CTWINDOW", + NL80211_ATTR_P2P_OPPPS: "NL80211_ATTR_P2P_OPPPS", + NL80211_ATTR_LOCAL_MESH_POWER_MODE: "NL80211_ATTR_LOCAL_MESH_POWER_MODE", + NL80211_ATTR_ACL_POLICY: "NL80211_ATTR_ACL_POLICY", + NL80211_ATTR_MAC_ADDRS: "NL80211_ATTR_MAC_ADDRS", + NL80211_ATTR_MAC_ACL_MAX: "NL80211_ATTR_MAC_ACL_MAX", + NL80211_ATTR_RADAR_EVENT: "NL80211_ATTR_RADAR_EVENT", + NL80211_ATTR_EXT_CAPA: "NL80211_ATTR_EXT_CAPA", + NL80211_ATTR_EXT_CAPA_MASK: "NL80211_ATTR_EXT_CAPA_MASK", + NL80211_ATTR_STA_CAPABILITY: "NL80211_ATTR_STA_CAPABILITY", + NL80211_ATTR_STA_EXT_CAPABILITY: "NL80211_ATTR_STA_EXT_CAPABILITY", + NL80211_ATTR_PROTOCOL_FEATURES: "NL80211_ATTR_PROTOCOL_FEATURES", + NL80211_ATTR_SPLIT_WIPHY_DUMP: "NL80211_ATTR_SPLIT_WIPHY_DUMP", + NL80211_ATTR_DISABLE_VHT: "NL80211_ATTR_DISABLE_VHT", + NL80211_ATTR_VHT_CAPABILITY_MASK: "NL80211_ATTR_VHT_CAPABILITY_MASK", + NL80211_ATTR_MDID: "NL80211_ATTR_MDID", + NL80211_ATTR_IE_RIC: "NL80211_ATTR_IE_RIC", + NL80211_ATTR_CRIT_PROT_ID: "NL80211_ATTR_CRIT_PROT_ID", + NL80211_ATTR_MAX_CRIT_PROT_DURATION: "NL80211_ATTR_MAX_CRIT_PROT_DURATION", + NL80211_ATTR_PEER_AID: "NL80211_ATTR_PEER_AID", + __NL80211_ATTR_AFTER_LAST: "__NL80211_ATTR_AFTER_LAST", +} +nl80211_iftype2str = { + NL80211_IFTYPE_UNSPECIFIED: "NL80211_IFTYPE_UNSPECIFIED", + NL80211_IFTYPE_ADHOC: "NL80211_IFTYPE_ADHOC", + NL80211_IFTYPE_STATION: "NL80211_IFTYPE_STATION", + NL80211_IFTYPE_AP: "NL80211_IFTYPE_AP", + NL80211_IFTYPE_AP_VLAN: "NL80211_IFTYPE_AP_VLAN", + NL80211_IFTYPE_WDS: "NL80211_IFTYPE_WDS", + NL80211_IFTYPE_MONITOR: "NL80211_IFTYPE_MONITOR", + NL80211_IFTYPE_MESH_POINT: "NL80211_IFTYPE_MESH_POINT", + NL80211_IFTYPE_P2P_CLIENT: "NL80211_IFTYPE_P2P_CLIENT", + NL80211_IFTYPE_P2P_GO: "NL80211_IFTYPE_P2P_GO", + NL80211_IFTYPE_P2P_DEVICE: "NL80211_IFTYPE_P2P_DEVICE", + NUM_NL80211_IFTYPES: "NUM_NL80211_IFTYPES", +} +nl80211_sta_flags2str = { + __NL80211_STA_FLAG_INVALID: "__NL80211_STA_FLAG_INVALID", + NL80211_STA_FLAG_AUTHORIZED: "NL80211_STA_FLAG_AUTHORIZED", + NL80211_STA_FLAG_SHORT_PREAMBLE: "NL80211_STA_FLAG_SHORT_PREAMBLE", + NL80211_STA_FLAG_WME: "NL80211_STA_FLAG_WME", + NL80211_STA_FLAG_MFP: "NL80211_STA_FLAG_MFP", + NL80211_STA_FLAG_AUTHENTICATED: "NL80211_STA_FLAG_AUTHENTICATED", + NL80211_STA_FLAG_TDLS_PEER: "NL80211_STA_FLAG_TDLS_PEER", + NL80211_STA_FLAG_ASSOCIATED: "NL80211_STA_FLAG_ASSOCIATED", + __NL80211_STA_FLAG_AFTER_LAST: "__NL80211_STA_FLAG_AFTER_LAST", +} +nl80211_rate_info2str = { + __NL80211_RATE_INFO_INVALID: "__NL80211_RATE_INFO_INVALID", + NL80211_RATE_INFO_BITRATE: "NL80211_RATE_INFO_BITRATE", + NL80211_RATE_INFO_MCS: "NL80211_RATE_INFO_MCS", + NL80211_RATE_INFO_40_MHZ_WIDTH: "NL80211_RATE_INFO_40_MHZ_WIDTH", + NL80211_RATE_INFO_SHORT_GI: "NL80211_RATE_INFO_SHORT_GI", + NL80211_RATE_INFO_BITRATE32: "NL80211_RATE_INFO_BITRATE32", + NL80211_RATE_INFO_VHT_MCS: "NL80211_RATE_INFO_VHT_MCS", + NL80211_RATE_INFO_VHT_NSS: "NL80211_RATE_INFO_VHT_NSS", + NL80211_RATE_INFO_80_MHZ_WIDTH: "NL80211_RATE_INFO_80_MHZ_WIDTH", + NL80211_RATE_INFO_80P80_MHZ_WIDTH: "NL80211_RATE_INFO_80P80_MHZ_WIDTH", + NL80211_RATE_INFO_160_MHZ_WIDTH: "NL80211_RATE_INFO_160_MHZ_WIDTH", + __NL80211_RATE_INFO_AFTER_LAST: "__NL80211_RATE_INFO_AFTER_LAST", +} +nl80211_sta_bss_param2str = { + __NL80211_STA_BSS_PARAM_INVALID: "__NL80211_STA_BSS_PARAM_INVALID", + NL80211_STA_BSS_PARAM_CTS_PROT: "NL80211_STA_BSS_PARAM_CTS_PROT", + NL80211_STA_BSS_PARAM_SHORT_PREAMBLE: "NL80211_STA_BSS_PARAM_SHORT_PREAMBLE", + NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME: "NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME", + NL80211_STA_BSS_PARAM_DTIM_PERIOD: "NL80211_STA_BSS_PARAM_DTIM_PERIOD", + NL80211_STA_BSS_PARAM_BEACON_INTERVAL: "NL80211_STA_BSS_PARAM_BEACON_INTERVAL", + __NL80211_STA_BSS_PARAM_AFTER_LAST: "__NL80211_STA_BSS_PARAM_AFTER_LAST", +} +nl80211_sta_info2str = { + __NL80211_STA_INFO_INVALID: "__NL80211_STA_INFO_INVALID", + NL80211_STA_INFO_INACTIVE_TIME: "NL80211_STA_INFO_INACTIVE_TIME", + NL80211_STA_INFO_RX_BYTES: "NL80211_STA_INFO_RX_BYTES", + NL80211_STA_INFO_TX_BYTES: "NL80211_STA_INFO_TX_BYTES", + NL80211_STA_INFO_LLID: "NL80211_STA_INFO_LLID", + NL80211_STA_INFO_PLID: "NL80211_STA_INFO_PLID", + NL80211_STA_INFO_PLINK_STATE: "NL80211_STA_INFO_PLINK_STATE", + NL80211_STA_INFO_SIGNAL: "NL80211_STA_INFO_SIGNAL", + NL80211_STA_INFO_TX_BITRATE: "NL80211_STA_INFO_TX_BITRATE", + NL80211_STA_INFO_RX_PACKETS: "NL80211_STA_INFO_RX_PACKETS", + NL80211_STA_INFO_TX_PACKETS: "NL80211_STA_INFO_TX_PACKETS", + NL80211_STA_INFO_TX_RETRIES: "NL80211_STA_INFO_TX_RETRIES", + NL80211_STA_INFO_TX_FAILED: "NL80211_STA_INFO_TX_FAILED", + NL80211_STA_INFO_SIGNAL_AVG: "NL80211_STA_INFO_SIGNAL_AVG", + NL80211_STA_INFO_RX_BITRATE: "NL80211_STA_INFO_RX_BITRATE", + NL80211_STA_INFO_BSS_PARAM: "NL80211_STA_INFO_BSS_PARAM", + NL80211_STA_INFO_CONNECTED_TIME: "NL80211_STA_INFO_CONNECTED_TIME", + NL80211_STA_INFO_STA_FLAGS: "NL80211_STA_INFO_STA_FLAGS", + NL80211_STA_INFO_BEACON_LOSS: "NL80211_STA_INFO_BEACON_LOSS", + NL80211_STA_INFO_T_OFFSET: "NL80211_STA_INFO_T_OFFSET", + NL80211_STA_INFO_LOCAL_PM: "NL80211_STA_INFO_LOCAL_PM", + NL80211_STA_INFO_PEER_PM: "NL80211_STA_INFO_PEER_PM", + NL80211_STA_INFO_NONPEER_PM: "NL80211_STA_INFO_NONPEER_PM", + NL80211_STA_INFO_RX_BYTES64: "NL80211_STA_INFO_RX_BYTES64", + NL80211_STA_INFO_TX_BYTES64: "NL80211_STA_INFO_TX_BYTES64", + NL80211_STA_INFO_CHAIN_SIGNAL: "NL80211_STA_INFO_CHAIN_SIGNAL", + NL80211_STA_INFO_CHAIN_SIGNAL_AVG: "NL80211_STA_INFO_CHAIN_SIGNAL_AVG", + __NL80211_STA_INFO_AFTER_LAST: "__NL80211_STA_INFO_AFTER_LAST", +} +nl80211_mpath_flags2str = { + NL80211_MPATH_FLAG_ACTIVE: "NL80211_MPATH_FLAG_ACTIVE", + NL80211_MPATH_FLAG_RESOLVING: "NL80211_MPATH_FLAG_RESOLVING", + NL80211_MPATH_FLAG_SN_VALID: "NL80211_MPATH_FLAG_SN_VALID", + NL80211_MPATH_FLAG_FIXED: "NL80211_MPATH_FLAG_FIXED", + NL80211_MPATH_FLAG_RESOLVED: "NL80211_MPATH_FLAG_RESOLVED", +} +nl80211_mpath_info2str = { + __NL80211_MPATH_INFO_INVALID: "__NL80211_MPATH_INFO_INVALID", + NL80211_MPATH_INFO_FRAME_QLEN: "NL80211_MPATH_INFO_FRAME_QLEN", + NL80211_MPATH_INFO_SN: "NL80211_MPATH_INFO_SN", + NL80211_MPATH_INFO_METRIC: "NL80211_MPATH_INFO_METRIC", + NL80211_MPATH_INFO_EXPTIME: "NL80211_MPATH_INFO_EXPTIME", + NL80211_MPATH_INFO_FLAGS: "NL80211_MPATH_INFO_FLAGS", + NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: "NL80211_MPATH_INFO_DISCOVERY_TIMEOUT", + NL80211_MPATH_INFO_DISCOVERY_RETRIES: "NL80211_MPATH_INFO_DISCOVERY_RETRIES", + __NL80211_MPATH_INFO_AFTER_LAST: "__NL80211_MPATH_INFO_AFTER_LAST", +} +nl80211_band_attr2str = { + __NL80211_BAND_ATTR_INVALID: "__NL80211_BAND_ATTR_INVALID", + NL80211_BAND_ATTR_FREQS: "NL80211_BAND_ATTR_FREQS", + NL80211_BAND_ATTR_RATES: "NL80211_BAND_ATTR_RATES", + NL80211_BAND_ATTR_HT_MCS_SET: "NL80211_BAND_ATTR_HT_MCS_SET", + NL80211_BAND_ATTR_HT_CAPA: "NL80211_BAND_ATTR_HT_CAPA", + NL80211_BAND_ATTR_HT_AMPDU_FACTOR: "NL80211_BAND_ATTR_HT_AMPDU_FACTOR", + NL80211_BAND_ATTR_HT_AMPDU_DENSITY: "NL80211_BAND_ATTR_HT_AMPDU_DENSITY", + NL80211_BAND_ATTR_VHT_MCS_SET: "NL80211_BAND_ATTR_VHT_MCS_SET", + NL80211_BAND_ATTR_VHT_CAPA: "NL80211_BAND_ATTR_VHT_CAPA", + __NL80211_BAND_ATTR_AFTER_LAST: "__NL80211_BAND_ATTR_AFTER_LAST", +} +nl80211_frequency_attr2str = { + __NL80211_FREQUENCY_ATTR_INVALID: "__NL80211_FREQUENCY_ATTR_INVALID", + NL80211_FREQUENCY_ATTR_FREQ: "NL80211_FREQUENCY_ATTR_FREQ", + NL80211_FREQUENCY_ATTR_DISABLED: "NL80211_FREQUENCY_ATTR_DISABLED", + NL80211_FREQUENCY_ATTR_PASSIVE_SCAN: "NL80211_FREQUENCY_ATTR_PASSIVE_SCAN", + NL80211_FREQUENCY_ATTR_NO_IBSS: "NL80211_FREQUENCY_ATTR_NO_IBSS", + NL80211_FREQUENCY_ATTR_RADAR: "NL80211_FREQUENCY_ATTR_RADAR", + NL80211_FREQUENCY_ATTR_MAX_TX_POWER: "NL80211_FREQUENCY_ATTR_MAX_TX_POWER", + NL80211_FREQUENCY_ATTR_DFS_STATE: "NL80211_FREQUENCY_ATTR_DFS_STATE", + NL80211_FREQUENCY_ATTR_DFS_TIME: "NL80211_FREQUENCY_ATTR_DFS_TIME", + NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: "NL80211_FREQUENCY_ATTR_NO_HT40_MINUS", + NL80211_FREQUENCY_ATTR_NO_HT40_PLUS: "NL80211_FREQUENCY_ATTR_NO_HT40_PLUS", + NL80211_FREQUENCY_ATTR_NO_80MHZ: "NL80211_FREQUENCY_ATTR_NO_80MHZ", + NL80211_FREQUENCY_ATTR_NO_160MHZ: "NL80211_FREQUENCY_ATTR_NO_160MHZ", + __NL80211_FREQUENCY_ATTR_AFTER_LAST: "__NL80211_FREQUENCY_ATTR_AFTER_LAST", +} +nl80211_bitrate_attr2str = { + __NL80211_BITRATE_ATTR_INVALID: "__NL80211_BITRATE_ATTR_INVALID", + NL80211_BITRATE_ATTR_RATE: "NL80211_BITRATE_ATTR_RATE", + NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: "NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE", + __NL80211_BITRATE_ATTR_AFTER_LAST: "__NL80211_BITRATE_ATTR_AFTER_LAST", +} +nl80211_reg_initiator2str = { + NL80211_REGDOM_SET_BY_CORE: "NL80211_REGDOM_SET_BY_CORE", + NL80211_REGDOM_SET_BY_USER: "NL80211_REGDOM_SET_BY_USER", + NL80211_REGDOM_SET_BY_DRIVER: "NL80211_REGDOM_SET_BY_DRIVER", + NL80211_REGDOM_SET_BY_COUNTRY_IE: "NL80211_REGDOM_SET_BY_COUNTRY_IE", +} +nl80211_reg_type2str = { + NL80211_REGDOM_TYPE_COUNTRY: "NL80211_REGDOM_TYPE_COUNTRY", + NL80211_REGDOM_TYPE_WORLD: "NL80211_REGDOM_TYPE_WORLD", + NL80211_REGDOM_TYPE_CUSTOM_WORLD: "NL80211_REGDOM_TYPE_CUSTOM_WORLD", + NL80211_REGDOM_TYPE_INTERSECTION: "NL80211_REGDOM_TYPE_INTERSECTION", +} +nl80211_reg_rule_attr2str = { + __NL80211_REG_RULE_ATTR_INVALID: "__NL80211_REG_RULE_ATTR_INVALID", + NL80211_ATTR_REG_RULE_FLAGS: "NL80211_ATTR_REG_RULE_FLAGS", + NL80211_ATTR_FREQ_RANGE_START: "NL80211_ATTR_FREQ_RANGE_START", + NL80211_ATTR_FREQ_RANGE_END: "NL80211_ATTR_FREQ_RANGE_END", + NL80211_ATTR_FREQ_RANGE_MAX_BW: "NL80211_ATTR_FREQ_RANGE_MAX_BW", + NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: "NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN", + NL80211_ATTR_POWER_RULE_MAX_EIRP: "NL80211_ATTR_POWER_RULE_MAX_EIRP", + __NL80211_REG_RULE_ATTR_AFTER_LAST: "__NL80211_REG_RULE_ATTR_AFTER_LAST", +} +nl80211_sched_scan_match_attr2str = { + __NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: "__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID", + NL80211_SCHED_SCAN_MATCH_ATTR_SSID: "NL80211_SCHED_SCAN_MATCH_ATTR_SSID", + NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: "NL80211_SCHED_SCAN_MATCH_ATTR_RSSI", + __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: "__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST", +} +nl80211_reg_rule_flags2str = { + NL80211_RRF_NO_OFDM: "NL80211_RRF_NO_OFDM", + NL80211_RRF_NO_CCK: "NL80211_RRF_NO_CCK", + NL80211_RRF_NO_INDOOR: "NL80211_RRF_NO_INDOOR", + NL80211_RRF_NO_OUTDOOR: "NL80211_RRF_NO_OUTDOOR", + NL80211_RRF_DFS: "NL80211_RRF_DFS", + NL80211_RRF_PTP_ONLY: "NL80211_RRF_PTP_ONLY", + NL80211_RRF_PTMP_ONLY: "NL80211_RRF_PTMP_ONLY", + NL80211_RRF_PASSIVE_SCAN: "NL80211_RRF_PASSIVE_SCAN", + NL80211_RRF_NO_IBSS: "NL80211_RRF_NO_IBSS", +} +nl80211_dfs_regions2str = {} +nl80211_user_reg_hint_type2str = {} +nl80211_survey_info2str = { + __NL80211_SURVEY_INFO_INVALID: "__NL80211_SURVEY_INFO_INVALID", + NL80211_SURVEY_INFO_FREQUENCY: "NL80211_SURVEY_INFO_FREQUENCY", + NL80211_SURVEY_INFO_NOISE: "NL80211_SURVEY_INFO_NOISE", + NL80211_SURVEY_INFO_IN_USE: "NL80211_SURVEY_INFO_IN_USE", + NL80211_SURVEY_INFO_CHANNEL_TIME: "NL80211_SURVEY_INFO_CHANNEL_TIME", + NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: "NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY", + NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: "NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY", + NL80211_SURVEY_INFO_CHANNEL_TIME_RX: "NL80211_SURVEY_INFO_CHANNEL_TIME_RX", + NL80211_SURVEY_INFO_CHANNEL_TIME_TX: "NL80211_SURVEY_INFO_CHANNEL_TIME_TX", + __NL80211_SURVEY_INFO_AFTER_LAST: "__NL80211_SURVEY_INFO_AFTER_LAST", +} +nl80211_mntr_flags2str = { + __NL80211_MNTR_FLAG_INVALID: "__NL80211_MNTR_FLAG_INVALID", + NL80211_MNTR_FLAG_FCSFAIL: "NL80211_MNTR_FLAG_FCSFAIL", + NL80211_MNTR_FLAG_PLCPFAIL: "NL80211_MNTR_FLAG_PLCPFAIL", + NL80211_MNTR_FLAG_CONTROL: "NL80211_MNTR_FLAG_CONTROL", + NL80211_MNTR_FLAG_OTHER_BSS: "NL80211_MNTR_FLAG_OTHER_BSS", + NL80211_MNTR_FLAG_COOK_FRAMES: "NL80211_MNTR_FLAG_COOK_FRAMES", + NL80211_MNTR_FLAG_ACTIVE: "NL80211_MNTR_FLAG_ACTIVE", + __NL80211_MNTR_FLAG_AFTER_LAST: "__NL80211_MNTR_FLAG_AFTER_LAST", +} +nl80211_mesh_power_mode2str = { + NL80211_MESH_POWER_UNKNOWN: "NL80211_MESH_POWER_UNKNOWN", + NL80211_MESH_POWER_ACTIVE: "NL80211_MESH_POWER_ACTIVE", + NL80211_MESH_POWER_LIGHT_SLEEP: "NL80211_MESH_POWER_LIGHT_SLEEP", + NL80211_MESH_POWER_DEEP_SLEEP: "NL80211_MESH_POWER_DEEP_SLEEP", + __NL80211_MESH_POWER_AFTER_LAST: "__NL80211_MESH_POWER_AFTER_LAST", +} +nl80211_meshconf_params2str = { + __NL80211_MESHCONF_INVALID: "__NL80211_MESHCONF_INVALID", + NL80211_MESHCONF_RETRY_TIMEOUT: "NL80211_MESHCONF_RETRY_TIMEOUT", + NL80211_MESHCONF_CONFIRM_TIMEOUT: "NL80211_MESHCONF_CONFIRM_TIMEOUT", + NL80211_MESHCONF_HOLDING_TIMEOUT: "NL80211_MESHCONF_HOLDING_TIMEOUT", + NL80211_MESHCONF_MAX_PEER_LINKS: "NL80211_MESHCONF_MAX_PEER_LINKS", + NL80211_MESHCONF_MAX_RETRIES: "NL80211_MESHCONF_MAX_RETRIES", + NL80211_MESHCONF_TTL: "NL80211_MESHCONF_TTL", + NL80211_MESHCONF_AUTO_OPEN_PLINKS: "NL80211_MESHCONF_AUTO_OPEN_PLINKS", + NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: "NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES", + NL80211_MESHCONF_PATH_REFRESH_TIME: "NL80211_MESHCONF_PATH_REFRESH_TIME", + NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: "NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT", + NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: "NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT", + NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: "NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL", + NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: "NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME", + NL80211_MESHCONF_HWMP_ROOTMODE: "NL80211_MESHCONF_HWMP_ROOTMODE", + NL80211_MESHCONF_ELEMENT_TTL: "NL80211_MESHCONF_ELEMENT_TTL", + NL80211_MESHCONF_HWMP_RANN_INTERVAL: "NL80211_MESHCONF_HWMP_RANN_INTERVAL", + NL80211_MESHCONF_GATE_ANNOUNCEMENTS: "NL80211_MESHCONF_GATE_ANNOUNCEMENTS", + NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL: "NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL", + NL80211_MESHCONF_FORWARDING: "NL80211_MESHCONF_FORWARDING", + NL80211_MESHCONF_RSSI_THRESHOLD: "NL80211_MESHCONF_RSSI_THRESHOLD", + NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR: "NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR", + NL80211_MESHCONF_HT_OPMODE: "NL80211_MESHCONF_HT_OPMODE", + NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT: "NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT", + NL80211_MESHCONF_HWMP_ROOT_INTERVAL: "NL80211_MESHCONF_HWMP_ROOT_INTERVAL", + NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL: "NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL", + NL80211_MESHCONF_POWER_MODE: "NL80211_MESHCONF_POWER_MODE", + NL80211_MESHCONF_AWAKE_WINDOW: "NL80211_MESHCONF_AWAKE_WINDOW", + NL80211_MESHCONF_PLINK_TIMEOUT: "NL80211_MESHCONF_PLINK_TIMEOUT", + __NL80211_MESHCONF_ATTR_AFTER_LAST: "__NL80211_MESHCONF_ATTR_AFTER_LAST", +} +nl80211_mesh_setup_params2str = { + __NL80211_MESH_SETUP_INVALID: "__NL80211_MESH_SETUP_INVALID", + NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: "NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL", + NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: "NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC", + NL80211_MESH_SETUP_IE: "NL80211_MESH_SETUP_IE", + NL80211_MESH_SETUP_USERSPACE_AUTH: "NL80211_MESH_SETUP_USERSPACE_AUTH", + NL80211_MESH_SETUP_USERSPACE_AMPE: "NL80211_MESH_SETUP_USERSPACE_AMPE", + NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC: "NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC", + NL80211_MESH_SETUP_USERSPACE_MPM: "NL80211_MESH_SETUP_USERSPACE_MPM", + NL80211_MESH_SETUP_AUTH_PROTOCOL: "NL80211_MESH_SETUP_AUTH_PROTOCOL", + __NL80211_MESH_SETUP_ATTR_AFTER_LAST: "__NL80211_MESH_SETUP_ATTR_AFTER_LAST", +} +nl80211_txq_attr2str = { + __NL80211_TXQ_ATTR_INVALID: "__NL80211_TXQ_ATTR_INVALID", + NL80211_TXQ_ATTR_AC: "NL80211_TXQ_ATTR_AC", + NL80211_TXQ_ATTR_TXOP: "NL80211_TXQ_ATTR_TXOP", + NL80211_TXQ_ATTR_CWMIN: "NL80211_TXQ_ATTR_CWMIN", + NL80211_TXQ_ATTR_CWMAX: "NL80211_TXQ_ATTR_CWMAX", + NL80211_TXQ_ATTR_AIFS: "NL80211_TXQ_ATTR_AIFS", + __NL80211_TXQ_ATTR_AFTER_LAST: "__NL80211_TXQ_ATTR_AFTER_LAST", +} +nl80211_ac2str = { + NL80211_AC_VO: "NL80211_AC_VO", + NL80211_AC_VI: "NL80211_AC_VI", + NL80211_AC_BE: "NL80211_AC_BE", + NL80211_AC_BK: "NL80211_AC_BK", + NL80211_NUM_ACS: "NL80211_NUM_ACS", +} +nl80211_channel_type2str = { + NL80211_CHAN_NO_HT: "NL80211_CHAN_NO_HT", + NL80211_CHAN_HT20: "NL80211_CHAN_HT20", + NL80211_CHAN_HT40MINUS: "NL80211_CHAN_HT40MINUS", + NL80211_CHAN_HT40PLUS: "NL80211_CHAN_HT40PLUS", +} +nl80211_chan_width2str = { + NL80211_CHAN_WIDTH_20_NOHT: "NL80211_CHAN_WIDTH_20_NOHT", + NL80211_CHAN_WIDTH_20: "NL80211_CHAN_WIDTH_20", + NL80211_CHAN_WIDTH_40: "NL80211_CHAN_WIDTH_40", + NL80211_CHAN_WIDTH_80: "NL80211_CHAN_WIDTH_80", + NL80211_CHAN_WIDTH_80P80: "NL80211_CHAN_WIDTH_80P80", + NL80211_CHAN_WIDTH_160: "NL80211_CHAN_WIDTH_160", +} +nl80211_bss2str = { + __NL80211_BSS_INVALID: "__NL80211_BSS_INVALID", + NL80211_BSS_BSSID: "NL80211_BSS_BSSID", + NL80211_BSS_FREQUENCY: "NL80211_BSS_FREQUENCY", + NL80211_BSS_TSF: "NL80211_BSS_TSF", + NL80211_BSS_BEACON_INTERVAL: "NL80211_BSS_BEACON_INTERVAL", + NL80211_BSS_CAPABILITY: "NL80211_BSS_CAPABILITY", + NL80211_BSS_INFORMATION_ELEMENTS: "NL80211_BSS_INFORMATION_ELEMENTS", + NL80211_BSS_SIGNAL_MBM: "NL80211_BSS_SIGNAL_MBM", + NL80211_BSS_SIGNAL_UNSPEC: "NL80211_BSS_SIGNAL_UNSPEC", + NL80211_BSS_STATUS: "NL80211_BSS_STATUS", + NL80211_BSS_SEEN_MS_AGO: "NL80211_BSS_SEEN_MS_AGO", + NL80211_BSS_BEACON_IES: "NL80211_BSS_BEACON_IES", + __NL80211_BSS_AFTER_LAST: "__NL80211_BSS_AFTER_LAST", +} +nl80211_bss_status2str = { + NL80211_BSS_STATUS_AUTHENTICATED: "NL80211_BSS_STATUS_AUTHENTICATED", + NL80211_BSS_STATUS_ASSOCIATED: "NL80211_BSS_STATUS_ASSOCIATED", + NL80211_BSS_STATUS_IBSS_JOINED: "NL80211_BSS_STATUS_IBSS_JOINED", +} +nl80211_auth_type2str = { + NL80211_AUTHTYPE_OPEN_SYSTEM: "NL80211_AUTHTYPE_OPEN_SYSTEM", + NL80211_AUTHTYPE_SHARED_KEY: "NL80211_AUTHTYPE_SHARED_KEY", + NL80211_AUTHTYPE_FT: "NL80211_AUTHTYPE_FT", + NL80211_AUTHTYPE_NETWORK_EAP: "NL80211_AUTHTYPE_NETWORK_EAP", + NL80211_AUTHTYPE_SAE: "NL80211_AUTHTYPE_SAE", + __NL80211_AUTHTYPE_NUM: "__NL80211_AUTHTYPE_NUM", + NL80211_AUTHTYPE_AUTOMATIC: "NL80211_AUTHTYPE_AUTOMATIC", +} +nl80211_key_type2str = { + NL80211_KEYTYPE_GROUP: "NL80211_KEYTYPE_GROUP", + NL80211_KEYTYPE_PAIRWISE: "NL80211_KEYTYPE_PAIRWISE", + NL80211_KEYTYPE_PEERKEY: "NL80211_KEYTYPE_PEERKEY", + NUM_NL80211_KEYTYPES: "NUM_NL80211_KEYTYPES", +} +nl80211_mfp2str = { + NL80211_MFP_NO: "NL80211_MFP_NO", + NL80211_MFP_REQUIRED: "NL80211_MFP_REQUIRED", +} +nl80211_wpa_versions2str = { + NL80211_WPA_VERSION_1: "NL80211_WPA_VERSION_1", + NL80211_WPA_VERSION_2: "NL80211_WPA_VERSION_2", +} +nl80211_key_default_types2str = { + __NL80211_KEY_DEFAULT_TYPE_INVALID: "__NL80211_KEY_DEFAULT_TYPE_INVALID", + NL80211_KEY_DEFAULT_TYPE_UNICAST: "NL80211_KEY_DEFAULT_TYPE_UNICAST", + NL80211_KEY_DEFAULT_TYPE_MULTICAST: "NL80211_KEY_DEFAULT_TYPE_MULTICAST", + NUM_NL80211_KEY_DEFAULT_TYPES: "NUM_NL80211_KEY_DEFAULT_TYPES", +} +nl80211_key_attributes2str = { + __NL80211_KEY_INVALID: "__NL80211_KEY_INVALID", + NL80211_KEY_DATA: "NL80211_KEY_DATA", + NL80211_KEY_IDX: "NL80211_KEY_IDX", + NL80211_KEY_CIPHER: "NL80211_KEY_CIPHER", + NL80211_KEY_SEQ: "NL80211_KEY_SEQ", + NL80211_KEY_DEFAULT: "NL80211_KEY_DEFAULT", + NL80211_KEY_DEFAULT_MGMT: "NL80211_KEY_DEFAULT_MGMT", + NL80211_KEY_TYPE: "NL80211_KEY_TYPE", + NL80211_KEY_DEFAULT_TYPES: "NL80211_KEY_DEFAULT_TYPES", + __NL80211_KEY_AFTER_LAST: "__NL80211_KEY_AFTER_LAST", +} +nl80211_tx_rate_attributes2str = { + __NL80211_TXRATE_INVALID: "__NL80211_TXRATE_INVALID", + NL80211_TXRATE_LEGACY: "NL80211_TXRATE_LEGACY", + NL80211_TXRATE_MCS: "NL80211_TXRATE_MCS", + __NL80211_TXRATE_AFTER_LAST: "__NL80211_TXRATE_AFTER_LAST", +} +nl80211_band2str = { + NL80211_BAND_2GHZ: "NL80211_BAND_2GHZ", + NL80211_BAND_5GHZ: "NL80211_BAND_5GHZ", + NL80211_BAND_60GHZ: "NL80211_BAND_60GHZ", +} +nl80211_ps_state2str = { + NL80211_PS_DISABLED: "NL80211_PS_DISABLED", + NL80211_PS_ENABLED: "NL80211_PS_ENABLED", +} +nl80211_attr_cqm2str = { + __NL80211_ATTR_CQM_INVALID: "__NL80211_ATTR_CQM_INVALID", + NL80211_ATTR_CQM_RSSI_THOLD: "NL80211_ATTR_CQM_RSSI_THOLD", + NL80211_ATTR_CQM_RSSI_HYST: "NL80211_ATTR_CQM_RSSI_HYST", + NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: "NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT", + NL80211_ATTR_CQM_PKT_LOSS_EVENT: "NL80211_ATTR_CQM_PKT_LOSS_EVENT", + NL80211_ATTR_CQM_TXE_RATE: "NL80211_ATTR_CQM_TXE_RATE", + NL80211_ATTR_CQM_TXE_PKTS: "NL80211_ATTR_CQM_TXE_PKTS", + NL80211_ATTR_CQM_TXE_INTVL: "NL80211_ATTR_CQM_TXE_INTVL", + __NL80211_ATTR_CQM_AFTER_LAST: "__NL80211_ATTR_CQM_AFTER_LAST", +} +nl80211_cqm_rssi_threshold_event2str = { + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW: "NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW", + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: "NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH", + NL80211_CQM_RSSI_BEACON_LOSS_EVENT: "NL80211_CQM_RSSI_BEACON_LOSS_EVENT", +} +nl80211_tx_power_setting2str = { + NL80211_TX_POWER_AUTOMATIC: "NL80211_TX_POWER_AUTOMATIC", + NL80211_TX_POWER_LIMITED: "NL80211_TX_POWER_LIMITED", + NL80211_TX_POWER_FIXED: "NL80211_TX_POWER_FIXED", +} +nl80211_wowlan_packet_pattern_attr2str = { + __NL80211_WOWLAN_PKTPAT_INVALID: "__NL80211_WOWLAN_PKTPAT_INVALID", + NL80211_WOWLAN_PKTPAT_MASK: "NL80211_WOWLAN_PKTPAT_MASK", + NL80211_WOWLAN_PKTPAT_PATTERN: "NL80211_WOWLAN_PKTPAT_PATTERN", + NL80211_WOWLAN_PKTPAT_OFFSET: "NL80211_WOWLAN_PKTPAT_OFFSET", + NUM_NL80211_WOWLAN_PKTPAT: "NUM_NL80211_WOWLAN_PKTPAT", +} +nl80211_wowlan_triggers2str = { + __NL80211_WOWLAN_TRIG_INVALID: "__NL80211_WOWLAN_TRIG_INVALID", + NL80211_WOWLAN_TRIG_ANY: "NL80211_WOWLAN_TRIG_ANY", + NL80211_WOWLAN_TRIG_DISCONNECT: "NL80211_WOWLAN_TRIG_DISCONNECT", + NL80211_WOWLAN_TRIG_MAGIC_PKT: "NL80211_WOWLAN_TRIG_MAGIC_PKT", + NL80211_WOWLAN_TRIG_PKT_PATTERN: "NL80211_WOWLAN_TRIG_PKT_PATTERN", + NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: "NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED", + NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE: "NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE", + NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST: "NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST", + NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: "NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE", + NL80211_WOWLAN_TRIG_RFKILL_RELEASE: "NL80211_WOWLAN_TRIG_RFKILL_RELEASE", + NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211: "NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211", + NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN: "NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN", + NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023: "NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023", + NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN: "NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN", + NL80211_WOWLAN_TRIG_TCP_CONNECTION: "NL80211_WOWLAN_TRIG_TCP_CONNECTION", + NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH: "NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH", + NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST: "NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST", + NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS: "NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS", + NUM_NL80211_WOWLAN_TRIG: "NUM_NL80211_WOWLAN_TRIG", +} +nl80211_wowlan_tcp_attrs2str = { + __NL80211_WOWLAN_TCP_INVALID: "__NL80211_WOWLAN_TCP_INVALID", + NL80211_WOWLAN_TCP_SRC_IPV4: "NL80211_WOWLAN_TCP_SRC_IPV4", + NL80211_WOWLAN_TCP_DST_IPV4: "NL80211_WOWLAN_TCP_DST_IPV4", + NL80211_WOWLAN_TCP_DST_MAC: "NL80211_WOWLAN_TCP_DST_MAC", + NL80211_WOWLAN_TCP_SRC_PORT: "NL80211_WOWLAN_TCP_SRC_PORT", + NL80211_WOWLAN_TCP_DST_PORT: "NL80211_WOWLAN_TCP_DST_PORT", + NL80211_WOWLAN_TCP_DATA_PAYLOAD: "NL80211_WOWLAN_TCP_DATA_PAYLOAD", + NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ: "NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ", + NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN: "NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN", + NL80211_WOWLAN_TCP_DATA_INTERVAL: "NL80211_WOWLAN_TCP_DATA_INTERVAL", + NL80211_WOWLAN_TCP_WAKE_PAYLOAD: "NL80211_WOWLAN_TCP_WAKE_PAYLOAD", + NL80211_WOWLAN_TCP_WAKE_MASK: "NL80211_WOWLAN_TCP_WAKE_MASK", + NUM_NL80211_WOWLAN_TCP: "NUM_NL80211_WOWLAN_TCP", +} +nl80211_iface_limit_attrs2str = { + NL80211_IFACE_LIMIT_UNSPEC: "NL80211_IFACE_LIMIT_UNSPEC", + NL80211_IFACE_LIMIT_MAX: "NL80211_IFACE_LIMIT_MAX", + NL80211_IFACE_LIMIT_TYPES: "NL80211_IFACE_LIMIT_TYPES", + NUM_NL80211_IFACE_LIMIT: "NUM_NL80211_IFACE_LIMIT", +} +nl80211_if_combination_attrs2str = { + NL80211_IFACE_COMB_UNSPEC: "NL80211_IFACE_COMB_UNSPEC", + NL80211_IFACE_COMB_LIMITS: "NL80211_IFACE_COMB_LIMITS", + NL80211_IFACE_COMB_MAXNUM: "NL80211_IFACE_COMB_MAXNUM", + NL80211_IFACE_COMB_STA_AP_BI_MATCH: "NL80211_IFACE_COMB_STA_AP_BI_MATCH", + NL80211_IFACE_COMB_NUM_CHANNELS: "NL80211_IFACE_COMB_NUM_CHANNELS", + NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: "NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS", + NUM_NL80211_IFACE_COMB: "NUM_NL80211_IFACE_COMB", +} +nl80211_plink_state2str = { + NL80211_PLINK_LISTEN: "NL80211_PLINK_LISTEN", + NL80211_PLINK_OPN_SNT: "NL80211_PLINK_OPN_SNT", + NL80211_PLINK_OPN_RCVD: "NL80211_PLINK_OPN_RCVD", + NL80211_PLINK_CNF_RCVD: "NL80211_PLINK_CNF_RCVD", + NL80211_PLINK_ESTAB: "NL80211_PLINK_ESTAB", + NL80211_PLINK_HOLDING: "NL80211_PLINK_HOLDING", + NL80211_PLINK_BLOCKED: "NL80211_PLINK_BLOCKED", + NUM_NL80211_PLINK_STATES: "NUM_NL80211_PLINK_STATES", +} +plink_actions2str = { + NL80211_PLINK_ACTION_NO_ACTION: "NL80211_PLINK_ACTION_NO_ACTION", + NL80211_PLINK_ACTION_OPEN: "NL80211_PLINK_ACTION_OPEN", + NL80211_PLINK_ACTION_BLOCK: "NL80211_PLINK_ACTION_BLOCK", + NUM_NL80211_PLINK_ACTIONS: "NUM_NL80211_PLINK_ACTIONS", +} +nl80211_rekey_data2str = { + __NL80211_REKEY_DATA_INVALID: "__NL80211_REKEY_DATA_INVALID", + NL80211_REKEY_DATA_KEK: "NL80211_REKEY_DATA_KEK", + NL80211_REKEY_DATA_KCK: "NL80211_REKEY_DATA_KCK", + NL80211_REKEY_DATA_REPLAY_CTR: "NL80211_REKEY_DATA_REPLAY_CTR", + NUM_NL80211_REKEY_DATA: "NUM_NL80211_REKEY_DATA", +} +nl80211_hidden_ssid2str = { + NL80211_HIDDEN_SSID_NOT_IN_USE: "NL80211_HIDDEN_SSID_NOT_IN_USE", + NL80211_HIDDEN_SSID_ZERO_LEN: "NL80211_HIDDEN_SSID_ZERO_LEN", + NL80211_HIDDEN_SSID_ZERO_CONTENTS: "NL80211_HIDDEN_SSID_ZERO_CONTENTS", +} +nl80211_sta_wme_attr2str = { + __NL80211_STA_WME_INVALID: "__NL80211_STA_WME_INVALID", + NL80211_STA_WME_UAPSD_QUEUES: "NL80211_STA_WME_UAPSD_QUEUES", + NL80211_STA_WME_MAX_SP: "NL80211_STA_WME_MAX_SP", + __NL80211_STA_WME_AFTER_LAST: "__NL80211_STA_WME_AFTER_LAST", +} +nl80211_pmksa_candidate_attr2str = { + __NL80211_PMKSA_CANDIDATE_INVALID: "__NL80211_PMKSA_CANDIDATE_INVALID", + NL80211_PMKSA_CANDIDATE_INDEX: "NL80211_PMKSA_CANDIDATE_INDEX", + NL80211_PMKSA_CANDIDATE_BSSID: "NL80211_PMKSA_CANDIDATE_BSSID", + NL80211_PMKSA_CANDIDATE_PREAUTH: "NL80211_PMKSA_CANDIDATE_PREAUTH", + NUM_NL80211_PMKSA_CANDIDATE: "NUM_NL80211_PMKSA_CANDIDATE", +} +nl80211_tdls_operation2str = { + NL80211_TDLS_DISCOVERY_REQ: "NL80211_TDLS_DISCOVERY_REQ", + NL80211_TDLS_SETUP: "NL80211_TDLS_SETUP", + NL80211_TDLS_TEARDOWN: "NL80211_TDLS_TEARDOWN", + NL80211_TDLS_ENABLE_LINK: "NL80211_TDLS_ENABLE_LINK", + NL80211_TDLS_DISABLE_LINK: "NL80211_TDLS_DISABLE_LINK", +} +nl80211_feature_flags2str = { + NL80211_FEATURE_SK_TX_STATUS: "NL80211_FEATURE_SK_TX_STATUS", + NL80211_FEATURE_HT_IBSS: "NL80211_FEATURE_HT_IBSS", + NL80211_FEATURE_INACTIVITY_TIMER: "NL80211_FEATURE_INACTIVITY_TIMER", + NL80211_FEATURE_CELL_BASE_REG_HINTS: "NL80211_FEATURE_CELL_BASE_REG_HINTS", + NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: "NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL", + NL80211_FEATURE_SAE: "NL80211_FEATURE_SAE", + NL80211_FEATURE_LOW_PRIORITY_SCAN: "NL80211_FEATURE_LOW_PRIORITY_SCAN", + NL80211_FEATURE_SCAN_FLUSH: "NL80211_FEATURE_SCAN_FLUSH", + NL80211_FEATURE_AP_SCAN: "NL80211_FEATURE_AP_SCAN", + NL80211_FEATURE_VIF_TXPOWER: "NL80211_FEATURE_VIF_TXPOWER", + NL80211_FEATURE_NEED_OBSS_SCAN: "NL80211_FEATURE_NEED_OBSS_SCAN", + NL80211_FEATURE_P2P_GO_CTWIN: "NL80211_FEATURE_P2P_GO_CTWIN", + NL80211_FEATURE_P2P_GO_OPPPS: "NL80211_FEATURE_P2P_GO_OPPPS", + NL80211_FEATURE_ADVERTISE_CHAN_LIMITS: "NL80211_FEATURE_ADVERTISE_CHAN_LIMITS", + NL80211_FEATURE_FULL_AP_CLIENT_STATE: "NL80211_FEATURE_FULL_AP_CLIENT_STATE", + NL80211_FEATURE_USERSPACE_MPM: "NL80211_FEATURE_USERSPACE_MPM", + NL80211_FEATURE_ACTIVE_MONITOR: "NL80211_FEATURE_ACTIVE_MONITOR", +} +nl80211_probe_resp_offload_support_attr2str = { + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS: "NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS", + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2: "NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2", + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P: "NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P", + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U: "NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U", +} +nl80211_connect_failed_reason2str = { + NL80211_CONN_FAIL_MAX_CLIENTS: "NL80211_CONN_FAIL_MAX_CLIENTS", + NL80211_CONN_FAIL_BLOCKED_CLIENT: "NL80211_CONN_FAIL_BLOCKED_CLIENT", +} +nl80211_scan_flags2str = { + NL80211_SCAN_FLAG_LOW_PRIORITY: "NL80211_SCAN_FLAG_LOW_PRIORITY", + NL80211_SCAN_FLAG_FLUSH: "NL80211_SCAN_FLAG_FLUSH", + NL80211_SCAN_FLAG_AP: "NL80211_SCAN_FLAG_AP", +} +nl80211_acl_policy2str = { + NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: "NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED", + NL80211_ACL_POLICY_DENY_UNLESS_LISTED: "NL80211_ACL_POLICY_DENY_UNLESS_LISTED", +} +nl80211_radar_event2str = { + NL80211_RADAR_DETECTED: "NL80211_RADAR_DETECTED", + NL80211_RADAR_CAC_FINISHED: "NL80211_RADAR_CAC_FINISHED", + NL80211_RADAR_CAC_ABORTED: "NL80211_RADAR_CAC_ABORTED", + NL80211_RADAR_NOP_FINISHED: "NL80211_RADAR_NOP_FINISHED", +} +nl80211_dfs_state2str = { + NL80211_DFS_USABLE: "NL80211_DFS_USABLE", + NL80211_DFS_UNAVAILABLE: "NL80211_DFS_UNAVAILABLE", + NL80211_DFS_AVAILABLE: "NL80211_DFS_AVAILABLE", +} +nl80211_protocol_features2str = { + NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP: "NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP", +} +nl80211_crit_proto_id2str = { + NL80211_CRIT_PROTO_UNSPEC: "NL80211_CRIT_PROTO_UNSPEC", + NL80211_CRIT_PROTO_DHCP: "NL80211_CRIT_PROTO_DHCP", + NL80211_CRIT_PROTO_EAPOL: "NL80211_CRIT_PROTO_EAPOL", + NL80211_CRIT_PROTO_APIPA: "NL80211_CRIT_PROTO_APIPA", + NUM_NL80211_CRIT_PROTO: "NUM_NL80211_CRIT_PROTO", +} diff --git a/libnl/python/examples/wiphy.py b/libnl/python/examples/wiphy.py new file mode 100644 index 0000000..572e95a --- /dev/null +++ b/libnl/python/examples/wiphy.py @@ -0,0 +1,161 @@ +from __future__ import print_function +import netlink.capi as nl +import netlink.genl.capi as genl +import nl80211 +import sys +import traceback + + +class test_class: + def __init__(self): + self.done = 1 + + +def freq_to_ch(freq): + if freq == 2484: + return 14 + + if freq < 2484: + return (freq - 2407) / 5 + + # FIXME: dot11ChannelStartingFactor (802.11-2007 17.3.8.3.2) + if freq < 45000: + return freq / 5 - 1000 + + if freq >= 58320 and freq <= 64800: + return (freq - 56160) / 2160 + + return 0 + + +def handle_freq(attr, pol): + e, fattr = nl.py_nla_parse_nested(nl80211.NL80211_FREQUENCY_ATTR_MAX, attr, pol) + if nl80211.NL80211_FREQUENCY_ATTR_FREQ in fattr: + freq = nl.nla_get_u32(fattr[nl80211.NL80211_FREQUENCY_ATTR_FREQ]) + sys.stdout.write("\t\tfreq %d MHz [%d]" % (freq, freq_to_ch(freq))) + if nl80211.NL80211_FREQUENCY_ATTR_MAX_TX_POWER in fattr and not ( + nl80211.NL80211_FREQUENCY_ATTR_DISABLED in fattr + ): + sys.stdout.write( + " (%.1f dBm)" + % ( + 0.01 + * nl.nla_get_u32(fattr[nl80211.NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) + ) + ) + if nl80211.NL80211_FREQUENCY_ATTR_DISABLED in fattr: + sys.stdout.write(" (disabled)") + sys.stdout.write("\n") + + +def handle_band(attr, fpol): + e, battr = nl.py_nla_parse_nested(nl80211.NL80211_BAND_ATTR_MAX, attr, None) + print("\tband %d:" % nl.nla_type(attr)) + if nl80211.NL80211_BAND_ATTR_FREQS in battr: + for fattr in nl.nla_get_nested(battr[nl80211.NL80211_BAND_ATTR_FREQS]): + handle_freq(fattr, fpol) + + +def cipher_name(suite): + suite_val = "%02x%02x%02x%02x" % tuple(reversed(suite)) + if suite_val == "000fac01": + return "WEP40 (00-0f-ac:1)" + elif suite_val == "000fac05": + return "WEP104 (00-0f-ac:5)" + elif suite_val == "000fac02": + return "TKIP (00-0f-ac:2)" + elif suite_val == "000fac04": + return "CCMP (00-0f-ac:4)" + elif suite_val == "000fac06": + return "CMAC (00-0f-ac:6)" + elif suite_val == "000fac08": + return "GCMP (00-0f-ac:8)" + elif suite_val == "00147201": + return "WPI-SMS4 (00-14-72:1)" + else: + return suite_val + + +def msg_handler(m, a): + try: + e, attr = genl.py_genlmsg_parse( + nl.nlmsg_hdr(m), 0, nl80211.NL80211_ATTR_MAX, None + ) + if nl80211.NL80211_ATTR_WIPHY_NAME in attr: + print("wiphy %s" % nl.nla_get_string(attr[nl80211.NL80211_ATTR_WIPHY_NAME])) + if nl80211.NL80211_ATTR_WIPHY_BANDS in attr: + fpol = nl.nla_policy_array(nl80211.NL80211_FREQUENCY_ATTR_MAX + 1) + fpol[nl80211.NL80211_FREQUENCY_ATTR_FREQ].type = nl.NLA_U32 + fpol[nl80211.NL80211_FREQUENCY_ATTR_DISABLED].type = nl.NLA_FLAG + fpol[nl80211.NL80211_FREQUENCY_ATTR_PASSIVE_SCAN].type = nl.NLA_FLAG + fpol[nl80211.NL80211_FREQUENCY_ATTR_NO_IBSS].type = nl.NLA_FLAG + fpol[nl80211.NL80211_FREQUENCY_ATTR_RADAR].type = nl.NLA_FLAG + fpol[nl80211.NL80211_FREQUENCY_ATTR_MAX_TX_POWER].type = nl.NLA_U32 + + nattrs = nl.nla_get_nested(attr[nl80211.NL80211_ATTR_WIPHY_BANDS]) + for nattr in nattrs: + handle_band(nattr, fpol) + if nl80211.NL80211_ATTR_CIPHER_SUITES in attr: + ciphers = nl.nla_data(attr[nl80211.NL80211_ATTR_CIPHER_SUITES]) + num = len(ciphers) / 4 + if num > 0: + print("\tSupported Ciphers:") + for i in range(0, num, 4): + print("\t\t* %s" % cipher_name(ciphers[i : i + 4])) + if nl80211.NL80211_ATTR_SUPPORTED_IFTYPES in attr: + print("\tSupported interface modes:") + ifattr = nl.nla_get_nested(attr[nl80211.NL80211_ATTR_SUPPORTED_IFTYPES]) + for nl_mode in ifattr: + print("\t\t* %s" % nl80211.nl80211_iftype2str[nl.nla_type(nl_mode)]) + if nl80211.NL80211_ATTR_SOFTWARE_IFTYPES in attr: + print("\tsoftware interface modes (can always be added):") + ifattr = nl.nla_get_nested(attr[nl80211.NL80211_ATTR_SOFTWARE_IFTYPES]) + for nl_mode in ifattr: + print("\t\t* %s" % nl80211.nl80211_iftype2str[nl.nla_type(nl_mode)]) + return nl.NL_SKIP + except Exception: + (t, v, tb) = sys.exc_info() + print(v.message) + traceback.print_tb(tb) + + +def error_handler(err, a): + a.done = err.error + return nl.NL_STOP + + +def finish_handler(m, a): + return nl.NL_SKIP + + +def ack_handler(m, a): + a.done = 0 + return nl.NL_STOP + + +try: + cbd = test_class() + tx_cb = nl.nl_cb_alloc(nl.NL_CB_DEFAULT) + rx_cb = nl.nl_cb_clone(tx_cb) + s = nl.nl_socket_alloc_cb(tx_cb) + nl.py_nl_cb_err(rx_cb, nl.NL_CB_CUSTOM, error_handler, cbd) + nl.py_nl_cb_set(rx_cb, nl.NL_CB_FINISH, nl.NL_CB_CUSTOM, finish_handler, cbd) + nl.py_nl_cb_set(rx_cb, nl.NL_CB_ACK, nl.NL_CB_CUSTOM, ack_handler, cbd) + nl.py_nl_cb_set(rx_cb, nl.NL_CB_VALID, nl.NL_CB_CUSTOM, msg_handler, cbd) + + genl.genl_connect(s) + family = genl.genl_ctrl_resolve(s, "nl80211") + m = nl.nlmsg_alloc() + genl.genlmsg_put(m, 0, 0, family, 0, 0, nl80211.NL80211_CMD_GET_WIPHY, 0) + nl.nla_put_u32(m, nl80211.NL80211_ATTR_WIPHY, 7) + + err = nl.nl_send_auto_complete(s, m) + if err < 0: + nl.nlmsg_free(m) + + while cbd.done > 0 and not err < 0: + err = nl.nl_recvmsgs(s, rx_cb) +except Exception: + (t, v, tb) = sys.exc_info() + print(v.message) + traceback.print_tb(tb) diff --git a/libnl/python/netlink/__init__.py b/libnl/python/netlink/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/libnl/python/netlink/core.py b/libnl/python/netlink/core.py new file mode 100644 index 0000000..b994a73 --- /dev/null +++ b/libnl/python/netlink/core.py @@ -0,0 +1,813 @@ +# +# Netlink interface based on libnl +# +# Copyright (c) 2011 Thomas Graf +# + +"""netlink library based on libnl + +This module provides an interface to netlink sockets + +The module contains the following public classes: + - Socket -- The netlink socket + - Message -- The netlink message + - Callback -- The netlink callback handler + - Object -- Abstract object (based on struct nl_obect in libnl) used as + base class for all object types which can be put into a Cache + - Cache -- A collection of objects which are derived from the base + class Object. Used for netlink protocols which maintain a list + or tree of objects. + - DumpParams -- + +The following exceptions are defined: + - NetlinkError -- Base exception for all general purpose exceptions raised. + - KernelError -- Raised when the kernel returns an error as response to a + request. + +All other classes or functions in this module are considered implementation +details. +""" +from __future__ import absolute_import + + +from . import capi +import sys +import socket + +__all__ = [ + "Socket", + "Message", + "Callback", + "DumpParams", + "Object", + "Cache", + "KernelError", + "NetlinkError", +] + +__version__ = "0.1" + +# netlink protocols +NETLINK_ROUTE = 0 +# NETLINK_UNUSED = 1 +NETLINK_USERSOCK = 2 +NETLINK_FIREWALL = 3 +NETLINK_INET_DIAG = 4 +NETLINK_NFLOG = 5 +NETLINK_XFRM = 6 +NETLINK_SELINUX = 7 +NETLINK_ISCSI = 8 +NETLINK_AUDIT = 9 +NETLINK_FIB_LOOKUP = 10 +NETLINK_CONNECTOR = 11 +NETLINK_NETFILTER = 12 +NETLINK_IP6_FW = 13 +NETLINK_DNRTMSG = 14 +NETLINK_KOBJECT_UEVENT = 15 +NETLINK_GENERIC = 16 +NETLINK_SCSITRANSPORT = 18 +NETLINK_ECRYPTFS = 19 + +NL_DONTPAD = 0 +NL_AUTO_PORT = 0 +NL_AUTO_SEQ = 0 + +NL_DUMP_LINE = 0 +NL_DUMP_DETAILS = 1 +NL_DUMP_STATS = 2 + +NLM_F_REQUEST = 1 +NLM_F_MULTI = 2 +NLM_F_ACK = 4 +NLM_F_ECHO = 8 + +NLM_F_ROOT = 0x100 +NLM_F_MATCH = 0x200 +NLM_F_ATOMIC = 0x400 +NLM_F_DUMP = NLM_F_ROOT | NLM_F_MATCH + +NLM_F_REPLACE = 0x100 +NLM_F_EXCL = 0x200 +NLM_F_CREATE = 0x400 +NLM_F_APPEND = 0x800 + + +class NetlinkError(Exception): + def __init__(self, error): + self._error = error + self._msg = capi.nl_geterror(error) + + def __str__(self): + return self._msg + + +class KernelError(NetlinkError): + def __str__(self): + return "Kernel returned: {0}".format(self._msg) + + +class ImmutableError(NetlinkError): + def __init__(self, msg): + self._msg = msg + + def __str__(self): + return "Immutable attribute: {0}".format(self._msg) + + +class Message(object): + """Netlink message""" + + def __init__(self, size=0): + if size == 0: + self._msg = capi.nlmsg_alloc() + else: + self._msg = capi.nlmsg_alloc_size(size) + + if self._msg is None: + raise Exception("Message allocation returned NULL") + + def __del__(self): + capi.nlmsg_free(self._msg) + + def __len__(self): + return capi.nlmsg_len(capi.nlmsg_hdr(self._msg)) + + @property + def protocol(self): + return capi.nlmsg_get_proto(self._msg) + + @protocol.setter + def protocol(self, value): + capi.nlmsg_set_proto(self._msg, value) + + @property + def maxSize(self): + return capi.nlmsg_get_max_size(self._msg) + + @property + def hdr(self): + return capi.nlmsg_hdr(self._msg) + + @property + def data(self): + return capi.nlmsg_data(self._msg) + + @property + def attrs(self): + return capi.nlmsg_attrdata(self._msg) + + def send(self, sock): + sock.send(self) + + +class Callback(object): + """Netlink callback""" + + def __init__(self, kind=capi.NL_CB_DEFAULT): + if isinstance(kind, Callback): + self._cb = capi.py_nl_cb_clone(kind._cb) + else: + self._cb = capi.nl_cb_alloc(kind) + + def __del__(self): + capi.py_nl_cb_put(self._cb) + + def set_type(self, t, k, handler, obj): + return capi.py_nl_cb_set(self._cb, t, k, handler, obj) + + def set_all(self, k, handler, obj): + return capi.py_nl_cb_set_all(self._cb, k, handler, obj) + + def set_err(self, k, handler, obj): + return capi.py_nl_cb_err(self._cb, k, handler, obj) + + def clone(self): + return Callback(self) + + +class Socket(object): + """Netlink socket""" + + def __init__(self, cb=None): + if isinstance(cb, Callback): + self._sock = capi.nl_socket_alloc_cb(cb._cb) + elif cb is None: + self._sock = capi.nl_socket_alloc() + else: + raise Exception("'cb' parameter has wrong type") + + if self._sock is None: + raise Exception("NULL pointer returned while allocating socket") + + def __del__(self): + capi.nl_socket_free(self._sock) + + def __str__(self): + return "nlsock<{0}>".format(self.local_port) + + @property + def local_port(self): + return capi.nl_socket_get_local_port(self._sock) + + @local_port.setter + def local_port(self, value): + capi.nl_socket_set_local_port(self._sock, int(value)) + + @property + def peer_port(self): + return capi.nl_socket_get_peer_port(self._sock) + + @peer_port.setter + def peer_port(self, value): + capi.nl_socket_set_peer_port(self._sock, int(value)) + + @property + def peer_groups(self): + return capi.nl_socket_get_peer_groups(self._sock) + + @peer_groups.setter + def peer_groups(self, value): + capi.nl_socket_set_peer_groups(self._sock, value) + + def set_bufsize(self, rx, tx): + capi.nl_socket_set_buffer_size(self._sock, rx, tx) + + def connect(self, proto): + capi.nl_connect(self._sock, proto) + return self + + def disconnect(self): + capi.nl_close(self._sock) + + def sendto(self, buf): + ret = capi.nl_sendto(self._sock, buf, len(buf)) + if ret < 0: + raise Exception("Failed to send") + else: + return ret + + def send_auto_complete(self, msg): + if not isinstance(msg, Message): + raise Exception("must provide Message instance") + ret = capi.nl_send_auto_complete(self._sock, msg._msg) + if ret < 0: + raise Exception("send_auto_complete failed: ret=%d" % ret) + return ret + + def recvmsgs(self, recv_cb): + if not isinstance(recv_cb, Callback): + raise Exception("must provide Callback instance") + ret = capi.nl_recvmsgs(self._sock, recv_cb._cb) + if ret < 0: + raise Exception("recvmsg failed: ret=%d" % ret) + + +_sockets = {} + + +def lookup_socket(protocol): + try: + sock = _sockets[protocol] + except KeyError: + sock = Socket() + sock.connect(protocol) + _sockets[protocol] = sock + + return sock + + +class DumpParams(object): + """Dumping parameters""" + + def __init__(self, type_=NL_DUMP_LINE): + self._dp = capi.alloc_dump_params() + if not self._dp: + raise Exception("Unable to allocate struct nl_dump_params") + + self._dp.dp_type = type_ + + def __del__(self): + capi.free_dump_params(self._dp) + + @property + def type(self): + return self._dp.dp_type + + @type.setter + def type(self, value): + self._dp.dp_type = value + + @property + def prefix(self): + return self._dp.dp_prefix + + @prefix.setter + def prefix(self, value): + self._dp.dp_prefix = value + + +# underscore this to make sure it is deleted first upon module deletion +_defaultDumpParams = DumpParams(NL_DUMP_LINE) + + +class Object(object): + """Cacheable object (base class)""" + + def __init__(self, obj_name, name, obj=None): + self._obj_name = obj_name + self._name = name + self._modules = [] + + if not obj: + obj = capi.object_alloc_name(self._obj_name) + + self._nl_object = obj + + # Create a clone which stores the original state to notice + # modifications + clone_obj = capi.nl_object_clone(self._nl_object) + self._orig = self._obj2type(clone_obj) + + def __del__(self): + if not self._nl_object: + raise ValueError() + + capi.nl_object_put(self._nl_object) + + def __str__(self): + if hasattr(self, "format"): + return self.format() + else: + return capi.nl_object_dump_buf(self._nl_object, 4096).rstrip() + + def _new_instance(self): + raise NotImplementedError() + + def clone(self): + """Clone object""" + return self._new_instance(capi.nl_object_clone(self._nl_object)) + + def _module_lookup(self, path, constructor=None): + """Lookup object specific module and load it + + Object implementations consisting of multiple types may + offload some type specific code to separate modules which + are loadable on demand, e.g. a VLAN link or a specific + queueing discipline implementation. + + Loads the module `path` and calls the constructor if + supplied or `module`.init() + + The constructor/init function typically assigns a new + object covering the type specific implementation aspects + to the new object, e.g. link.vlan = VLANLink() + """ + try: + __import__(path) + except ImportError: + return + + module = sys.modules[path] + + if constructor: + ret = getattr(module, constructor)(self) + else: + ret = module.init(self) + + if ret: + self._modules.append(ret) + + def _module_brief(self): + ret = "" + + for module in self._modules: + if hasattr(module, "brief"): + ret += module.brief() + + return ret + + def dump(self, params=None): + """Dump object as human readable text""" + if params is None: + params = _defaultDumpParams + + capi.nl_object_dump(self._nl_object, params._dp) + + @property + def mark(self): + return bool(capi.nl_object_is_marked(self._nl_object)) + + @mark.setter + def mark(self, value): + if value: + capi.nl_object_mark(self._nl_object) + else: + capi.nl_object_unmark(self._nl_object) + + @property + def shared(self): + return capi.nl_object_shared(self._nl_object) != 0 + + @property + def attrs(self): + attr_list = capi.nl_object_attr_list(self._nl_object, 1024) + return attr_list[0].split() + + @property + def refcnt(self): + return capi.nl_object_get_refcnt(self._nl_object) + + # this method resolves multiple levels of sub types to allow + # accessing properties of subclass/subtypes (e.g. link.vlan.id) + def _resolve(self, attr): + obj = self + lst = attr.split(".") + while len(lst) > 1: + obj = getattr(obj, lst.pop(0)) + return (obj, lst.pop(0)) + + def _setattr(self, attr, val): + obj, attr = self._resolve(attr) + return setattr(obj, attr, val) + + def _hasattr(self, attr): + obj, attr = self._resolve(attr) + return hasattr(obj, attr) + + +class ObjIterator(object): + def __init__(self, cache, obj): + self._cache = cache + self._nl_object = None + + if not obj: + self._end = 1 + else: + capi.nl_object_get(obj) + self._nl_object = obj + self._first = 1 + self._end = 0 + + def __del__(self): + if self._nl_object: + capi.nl_object_put(self._nl_object) + + def __iter__(self): + return self + + def get_next(self): + return capi.nl_cache_get_next(self._nl_object) + + def next(self): + return self.__next__() + + def __next__(self): + if self._end: + raise StopIteration() + + if self._first: + ret = self._nl_object + self._first = 0 + else: + ret = self.get_next() + if not ret: + self._end = 1 + raise StopIteration() + + # return ref of previous element and acquire ref of current + # element to have object stay around until we fetched the + # next ptr + capi.nl_object_put(self._nl_object) + capi.nl_object_get(ret) + self._nl_object = ret + + # reference used inside object + capi.nl_object_get(ret) + return self._cache._new_object(ret) + + +class ReverseObjIterator(ObjIterator): + def get_next(self): + return capi.nl_cache_get_prev(self._nl_object) + + +class Cache(object): + """Collection of netlink objects""" + + def __init__(self): + if self.__class__ is Cache: + raise NotImplementedError() + self.arg1 = None + self.arg2 = None + + def __del__(self): + capi.nl_cache_free(self._nl_cache) + + def __len__(self): + return capi.nl_cache_nitems(self._nl_cache) + + def __iter__(self): + obj = capi.nl_cache_get_first(self._nl_cache) + return ObjIterator(self, obj) + + def __reversed__(self): + obj = capi.nl_cache_get_last(self._nl_cache) + return ReverseObjIterator(self, obj) + + def __contains__(self, item): + obj = capi.nl_cache_search(self._nl_cache, item._nl_object) + if obj is None: + return False + else: + capi.nl_object_put(obj) + return True + + # called by sub classes to allocate type specific caches by name + @staticmethod + def _alloc_cache_name(name): + return capi.alloc_cache_name(name) + + # implemented by sub classes, must return new instasnce of cacheable + # object + @staticmethod + def _new_object(obj): + raise NotImplementedError() + + # implemented by sub classes, must return instance of sub class + def _new_cache(self, cache): + raise NotImplementedError() + + def subset(self, filter_): + """Return new cache containing subset of cache + + Cretes a new cache containing all objects which match the + specified filter. + """ + if not filter_: + raise ValueError() + + c = capi.nl_cache_subset(self._nl_cache, filter_._nl_object) + return self._new_cache(cache=c) + + def dump(self, params=None, filter_=None): + """Dump (print) cache as human readable text""" + if not params: + params = _defaultDumpParams + + if filter_: + filter_ = filter_._nl_object + + capi.nl_cache_dump_filter(self._nl_cache, params._dp, filter_) + + def clear(self): + """Remove all cache entries""" + capi.nl_cache_clear(self._nl_cache) + + # Called by sub classes to set first cache argument + def _set_arg1(self, arg): + self.arg1 = arg + capi.nl_cache_set_arg1(self._nl_cache, arg) + + # Called by sub classes to set second cache argument + def _set_arg2(self, arg): + self.arg2 = arg + capi.nl_cache_set_arg2(self._nl_cache, arg) + + def refill(self, socket=None): + """Clear cache and refill it""" + if socket is None: + socket = lookup_socket(self._protocol) + + capi.nl_cache_refill(socket._sock, self._nl_cache) + return self + + def resync(self, socket=None, cb=None, args=None): + """Synchronize cache with content in kernel""" + if socket is None: + socket = lookup_socket(self._protocol) + + capi.nl_cache_resync(socket._sock, self._nl_cache, cb, args) + + def provide(self): + """Provide this cache to others + + Caches which have been "provided" are made available + to other users (of the same application context) which + "require" it. F.e. a link cache is generally provided + to allow others to translate interface indexes to + link names + """ + + capi.nl_cache_mngt_provide(self._nl_cache) + + def unprovide(self): + """Unprovide this cache + + No longer make the cache available to others. If the cache + has been handed out already, that reference will still + be valid. + """ + capi.nl_cache_mngt_unprovide(self._nl_cache) + + +# Cache Manager (Work in Progress) +NL_AUTO_PROVIDE = 1 + + +class CacheManager(object): + def __init__(self, protocol, flags=None): + + self._sock = Socket() + self._sock.connect(protocol) + + if not flags: + flags = NL_AUTO_PROVIDE + + self._mngr = capi.cache_mngr_alloc(self._sock._sock, protocol, flags) + + def __del__(self): + if self._sock: + self._sock.disconnect() + + if self._mngr: + capi.nl_cache_mngr_free(self._mngr) + + def add(self, name): + capi.cache_mngr_add(self._mngr, name, None, None) + + +class AddressFamily(object): + """Address family representation + + af = AddressFamily('inet6') + # raises: + # - ValueError if family name is not known + # - TypeError if invalid type is specified for family + + print af # => 'inet6' (string representation) + print int(af) # => 10 (numeric representation) + print repr(af) # => AddressFamily('inet6') + """ + + def __init__(self, family=socket.AF_UNSPEC): + if isinstance(family, str): + family = capi.nl_str2af(family) + if family < 0: + raise ValueError("Unknown family name") + elif not isinstance(family, int): + raise TypeError() + + self._family = family + + def __str__(self): + return capi.nl_af2str(self._family, 32)[0] + + def __int__(self): + return self._family + + def __repr__(self): + return "AddressFamily({0!r})".format(str(self)) + + +class AbstractAddress(object): + """Abstract address object + + addr = AbstractAddress('127.0.0.1/8') + print addr # => '127.0.0.1/8' + print addr.prefixlen # => '8' + print addr.family # => 'inet' + print len(addr) # => '4' (32bit ipv4 address) + + a = AbstractAddress('10.0.0.1/24') + b = AbstractAddress('10.0.0.2/24') + print a == b # => False + + + """ + + def __init__(self, addr): + self._nl_addr = None + + if isinstance(addr, str): + # returns None on success I guess + # TO CORRECT + addr = capi.addr_parse(addr, socket.AF_UNSPEC) + if addr is None: + raise ValueError("Invalid address format") + elif addr: + capi.nl_addr_get(addr) + + self._nl_addr = addr + + def __del__(self): + if self._nl_addr: + capi.nl_addr_put(self._nl_addr) + + def __cmp__(self, other): + if isinstance(other, str): + other = AbstractAddress(other) + + diff = self.prefixlen - other.prefixlen + if diff == 0: + diff = capi.nl_addr_cmp(self._nl_addr, other._nl_addr) + + return diff + + def contains(self, item): + diff = int(self.family) - int(item.family) + if diff: + return False + + if item.prefixlen < self.prefixlen: + return False + + diff = capi.nl_addr_cmp_prefix(self._nl_addr, item._nl_addr) + return diff == 0 + + def __nonzero__(self): + if self._nl_addr: + return not capi.nl_addr_iszero(self._nl_addr) + else: + return False + + def __len__(self): + if self._nl_addr: + return capi.nl_addr_get_len(self._nl_addr) + else: + return 0 + + def __str__(self): + if self._nl_addr: + return capi.nl_addr2str(self._nl_addr, 64)[0] + else: + return "none" + + @property + def shared(self): + """True if address is shared (multiple users)""" + if self._nl_addr: + return capi.nl_addr_shared(self._nl_addr) != 0 + else: + return False + + @property + def prefixlen(self): + """Length of prefix (number of bits)""" + if self._nl_addr: + return capi.nl_addr_get_prefixlen(self._nl_addr) + else: + return 0 + + @prefixlen.setter + def prefixlen(self, value): + if not self._nl_addr: + raise TypeError() + + capi.nl_addr_set_prefixlen(self._nl_addr, int(value)) + + @property + def family(self): + """Address family""" + f = 0 + if self._nl_addr: + f = capi.nl_addr_get_family(self._nl_addr) + + return AddressFamily(f) + + @family.setter + def family(self, value): + if not self._nl_addr: + raise TypeError() + + if not isinstance(value, AddressFamily): + value = AddressFamily(value) + + capi.nl_addr_set_family(self._nl_addr, int(value)) + + +# keyword: +# type = { int | str } +# immutable = { True | False } +# fmt = func (formatting function) +# title = string + + +def nlattr(**kwds): + """netlink object attribute decorator + + decorator used to mark mutable and immutable properties + of netlink objects. All properties marked as such are + regarded to be accessable. + + @property + @netlink.nlattr(type=int) + def my_attr(self): + return self._my_attr + + """ + + def wrap_fn(func): + func.formatinfo = kwds + return func + + return wrap_fn diff --git a/libnl/python/netlink/genl/__init__.py b/libnl/python/netlink/genl/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/libnl/python/netlink/route/__init__.py b/libnl/python/netlink/route/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/libnl/python/netlink/route/address.py b/libnl/python/netlink/route/address.py new file mode 100644 index 0000000..541f428 --- /dev/null +++ b/libnl/python/netlink/route/address.py @@ -0,0 +1,366 @@ +# +# Copyright (c) 2011 Thomas Graf +# + +"""Module providing access to network addresses +""" + +from __future__ import absolute_import + + +__version__ = "1.0" +__all__ = ["AddressCache", "Address"] + +import datetime +from .. import core as netlink +from . import capi as capi +from . import link as Link +from .. import util as util + + +class AddressCache(netlink.Cache): + """Cache containing network addresses""" + + def __init__(self, cache=None): + if not cache: + cache = self._alloc_cache_name("route/addr") + + self._protocol = netlink.NETLINK_ROUTE + self._nl_cache = cache + + def __getitem__(self, key): + # Using ifindex=0 here implies that the local address itself + # is unique, otherwise the first occurence is returned. + return self.lookup(0, key) + + def lookup(self, ifindex, local): + if type(local) is str: + local = netlink.AbstractAddress(local) + + addr = capi.rtnl_addr_get(self._nl_cache, ifindex, local._nl_addr) + if addr is None: + raise KeyError() + + return Address._from_capi(addr) + + @staticmethod + def _new_object(obj): + return Address(obj) + + @staticmethod + def _new_cache(cache): + return AddressCache(cache=cache) + + +class Address(netlink.Object): + """Network address""" + + def __init__(self, obj=None): + netlink.Object.__init__(self, "route/addr", "address", obj) + self._rtnl_addr = self._obj2type(self._nl_object) + + @classmethod + def _from_capi(cls, obj): + return cls(capi.addr2obj(obj)) + + @staticmethod + def _obj2type(obj): + return capi.obj2addr(obj) + + def __cmp__(self, other): + # sort by: + # 1. network link + # 2. address family + # 3. local address (including prefixlen) + diff = self.ifindex - other.ifindex + + if diff == 0: + diff = self.family - other.family + if diff == 0: + diff = capi.nl_addr_cmp(self.local, other.local) + + return diff + + @staticmethod + def _new_instance(obj): + return Address(obj) + + @property + @netlink.nlattr(type=int, immutable=True, fmt=util.num) + def ifindex(self): + """interface index""" + return capi.rtnl_addr_get_ifindex(self._rtnl_addr) + + @ifindex.setter + def ifindex(self, value): + link = Link.resolve(value) + if not link: + raise ValueError() + + self.link = link + + @property + @netlink.nlattr(type=str, fmt=util.string) + def link(self): + link = capi.rtnl_addr_get_link(self._rtnl_addr) + if not link: + return None + + return Link.Link.from_capi(link) + + @link.setter + def link(self, value): + if type(value) is str: + try: + value = Link.resolve(value) + except KeyError: + raise ValueError() + + capi.rtnl_addr_set_link(self._rtnl_addr, value._rtnl_link) + + # ifindex is immutable but we assume that if _orig does not + # have an ifindex specified, it was meant to be given here + if capi.rtnl_addr_get_ifindex(self._orig) == 0: + capi.rtnl_addr_set_ifindex(self._orig, value.ifindex) + + @property + @netlink.nlattr(type=str, fmt=util.string) + def label(self): + """address label""" + return capi.rtnl_addr_get_label(self._rtnl_addr) + + @label.setter + def label(self, value): + capi.rtnl_addr_set_label(self._rtnl_addr, value) + + @property + @netlink.nlattr(type=str, fmt=util.string) + def flags(self): + """Flags + + Setting this property will *Not* reset flags to value you supply in + + Examples: + addr.flags = '+xxx' # add xxx flag + addr.flags = 'xxx' # exactly the same + addr.flags = '-xxx' # remove xxx flag + addr.flags = [ '+xxx', '-yyy' ] # list operation + """ + flags = capi.rtnl_addr_get_flags(self._rtnl_addr) + return capi.rtnl_addr_flags2str(flags, 256)[0].split(",") + + def _set_flag(self, flag): + if flag.startswith("-"): + i = capi.rtnl_addr_str2flags(flag[1:]) + capi.rtnl_addr_unset_flags(self._rtnl_addr, i) + elif flag.startswith("+"): + i = capi.rtnl_addr_str2flags(flag[1:]) + capi.rtnl_addr_set_flags(self._rtnl_addr, i) + else: + i = capi.rtnl_addr_str2flags(flag) + capi.rtnl_addr_set_flags(self._rtnl_addr, i) + + @flags.setter + def flags(self, value): + if type(value) is list: + for flag in value: + self._set_flag(flag) + else: + self._set_flag(value) + + @property + @netlink.nlattr(type=int, immutable=True, fmt=util.num) + def family(self): + """Address family""" + fam = capi.rtnl_addr_get_family(self._rtnl_addr) + return netlink.AddressFamily(fam) + + @family.setter + def family(self, value): + if not isinstance(value, netlink.AddressFamily): + value = netlink.AddressFamily(value) + + capi.rtnl_addr_set_family(self._rtnl_addr, int(value)) + + @property + @netlink.nlattr(type=int, fmt=util.num) + def scope(self): + """Address scope""" + scope = capi.rtnl_addr_get_scope(self._rtnl_addr) + return capi.rtnl_scope2str(scope, 32)[0] + + @scope.setter + def scope(self, value): + if type(value) is str: + value = capi.rtnl_str2scope(value) + capi.rtnl_addr_set_scope(self._rtnl_addr, value) + + @property + @netlink.nlattr(type=str, immutable=True, fmt=util.addr) + def local(self): + """Local address""" + a = capi.rtnl_addr_get_local(self._rtnl_addr) + return netlink.AbstractAddress(a) + + @local.setter + def local(self, value): + a = netlink.AbstractAddress(value) + capi.rtnl_addr_set_local(self._rtnl_addr, a._nl_addr) + + # local is immutable but we assume that if _orig does not + # have a local address specified, it was meant to be given here + if capi.rtnl_addr_get_local(self._orig) is None: + capi.rtnl_addr_set_local(self._orig, a._nl_addr) + + @property + @netlink.nlattr(type=str, fmt=util.addr) + def peer(self): + """Peer address""" + a = capi.rtnl_addr_get_peer(self._rtnl_addr) + return netlink.AbstractAddress(a) + + @peer.setter + def peer(self, value): + a = netlink.AbstractAddress(value) + capi.rtnl_addr_set_peer(self._rtnl_addr, a._nl_addr) + + @property + @netlink.nlattr(type=str, fmt=util.addr) + def broadcast(self): + """Broadcast address""" + a = capi.rtnl_addr_get_broadcast(self._rtnl_addr) + return netlink.AbstractAddress(a) + + @broadcast.setter + def broadcast(self, value): + a = netlink.AbstractAddress(value) + capi.rtnl_addr_set_broadcast(self._rtnl_addr, a._nl_addr) + + @property + @netlink.nlattr(type=str, fmt=util.addr) + def multicast(self): + """multicast address""" + a = capi.rtnl_addr_get_multicast(self._rtnl_addr) + return netlink.AbstractAddress(a) + + @multicast.setter + def multicast(self, value): + try: + a = netlink.AbstractAddress(value) + except ValueError as err: + raise AttributeError("multicast", err) + + capi.rtnl_addr_set_multicast(self._rtnl_addr, a._nl_addr) + + @property + @netlink.nlattr(type=str, fmt=util.addr) + def anycast(self): + """anycast address""" + a = capi.rtnl_addr_get_anycast(self._rtnl_addr) + return netlink.AbstractAddress(a) + + @anycast.setter + def anycast(self, value): + a = netlink.AbstractAddress(value) + capi.rtnl_addr_set_anycast(self._rtnl_addr, a._nl_addr) + + @property + @netlink.nlattr(type=int, immutable=True, fmt=util.num) + def valid_lifetime(self): + """Valid lifetime""" + msecs = capi.rtnl_addr_get_valid_lifetime(self._rtnl_addr) + if msecs == 0xFFFFFFFF: + return None + else: + return datetime.timedelta(seconds=msecs) + + @valid_lifetime.setter + def valid_lifetime(self, value): + capi.rtnl_addr_set_valid_lifetime(self._rtnl_addr, int(value)) + + @property + @netlink.nlattr(type=int, immutable=True, fmt=util.num) + def preferred_lifetime(self): + """Preferred lifetime""" + msecs = capi.rtnl_addr_get_preferred_lifetime(self._rtnl_addr) + if msecs == 0xFFFFFFFF: + return None + else: + return datetime.timedelta(seconds=msecs) + + @preferred_lifetime.setter + def preferred_lifetime(self, value): + capi.rtnl_addr_set_preferred_lifetime(self._rtnl_addr, int(value)) + + @property + @netlink.nlattr(type=int, immutable=True, fmt=util.num) + def create_time(self): + """Creation time""" + hsec = capi.rtnl_addr_get_create_time(self._rtnl_addr) + return datetime.timedelta(milliseconds=10 * hsec) + + @property + @netlink.nlattr(type=int, immutable=True, fmt=util.num) + def last_update(self): + """Last update""" + hsec = capi.rtnl_addr_get_last_update_time(self._rtnl_addr) + return datetime.timedelta(milliseconds=10 * hsec) + + def add(self, socket=None, flags=None): + if not socket: + socket = netlink.lookup_socket(netlink.NETLINK_ROUTE) + + if not flags: + flags = netlink.NLM_F_CREATE + + ret = capi.rtnl_addr_add(socket._sock, self._rtnl_addr, flags) + if ret < 0: + raise netlink.KernelError(ret) + + def delete(self, socket, flags=0): + """Attempt to delete this address in the kernel""" + ret = capi.rtnl_addr_delete(socket._sock, self._rtnl_addr, flags) + if ret < 0: + raise netlink.KernelError(ret) + + ################################################################### + # private properties + # + # Used for formatting output. USE AT OWN RISK + @property + def _flags(self): + return ",".join(self.flags) + + def format(self, details=False, stats=False, nodev=False, indent=""): + """Return address as formatted text""" + fmt = util.MyFormatter(self, indent) + + buf = fmt.format("{a|local!b}") + + if not nodev: + buf += fmt.format(" {a|ifindex}") + + buf += fmt.format(" {a|scope}") + + if self.label: + buf += fmt.format(' "{a|label}"') + + buf += fmt.format(" <{a|_flags}>") + + if details: + buf += fmt.nl("\t{t|broadcast} {t|multicast}") + fmt.nl( + "\t{t|peer} {t|anycast}" + ) + + if self.valid_lifetime: + buf += fmt.nl("\t{s|valid-lifetime!k} " "{a|valid_lifetime}") + + if self.preferred_lifetime: + buf += fmt.nl("\t{s|preferred-lifetime!k} " "{a|preferred_lifetime}") + + if stats and (self.create_time or self.last_update): + buf += self.nl( + "\t{s|created!k} {a|create_time}" " {s|last-updated!k} {a|last_update}" + ) + + return buf diff --git a/libnl/python/netlink/route/link.py b/libnl/python/netlink/route/link.py new file mode 100644 index 0000000..e6089e2 --- /dev/null +++ b/libnl/python/netlink/route/link.py @@ -0,0 +1,562 @@ +# +# Copyright (c) 2011 Thomas Graf +# + +"""Module providing access to network links + +This module provides an interface to view configured network links, +modify them and to add and delete virtual network links. + +The following is a basic example: + import netlink.core as netlink + import netlink.route.link as link + + sock = netlink.Socket() + sock.connect(netlink.NETLINK_ROUTE) + + cache = link.LinkCache() # create new empty link cache + cache.refill(sock) # fill cache with all configured links + eth0 = cache['eth0'] # lookup link "eth0" + print eth0 # print basic configuration + +The module contains the following public classes: + + - Link -- Represents a network link. Instances can be created directly + via the constructor (empty link objects) or via the refill() + method of a LinkCache. + - LinkCache -- Derived from netlink.Cache, holds any number of + network links (Link instances). Main purpose is to keep + a local list of all network links configured in the + kernel. + +The following public functions exist: + - get_from_kernel(socket, name) + +""" + +from __future__ import absolute_import + +__version__ = "0.1" +__all__ = [ + "LinkCache", + "Link", +] + +import socket +from .. import core as netlink +from .. import capi as core_capi +from . import capi as capi +from .links import inet as inet +from .. import util as util + +# Link statistics definitions +RX_PACKETS = 0 +TX_PACKETS = 1 +RX_BYTES = 2 +TX_BYTES = 3 +RX_ERRORS = 4 +TX_ERRORS = 5 +RX_DROPPED = 6 +TX_DROPPED = 7 +RX_COMPRESSED = 8 +TX_COMPRESSED = 9 +RX_FIFO_ERR = 10 +TX_FIFO_ERR = 11 +RX_LEN_ERR = 12 +RX_OVER_ERR = 13 +RX_CRC_ERR = 14 +RX_FRAME_ERR = 15 +RX_MISSED_ERR = 16 +TX_ABORT_ERR = 17 +TX_CARRIER_ERR = 18 +TX_HBEAT_ERR = 19 +TX_WIN_ERR = 20 +COLLISIONS = 21 +MULTICAST = 22 +IP6_INPKTS = 23 +IP6_INHDRERRORS = 24 +IP6_INTOOBIGERRORS = 25 +IP6_INNOROUTES = 26 +IP6_INADDRERRORS = 27 +IP6_INUNKNOWNPROTOS = 28 +IP6_INTRUNCATEDPKTS = 29 +IP6_INDISCARDS = 30 +IP6_INDELIVERS = 31 +IP6_OUTFORWDATAGRAMS = 32 +IP6_OUTPKTS = 33 +IP6_OUTDISCARDS = 34 +IP6_OUTNOROUTES = 35 +IP6_REASMTIMEOUT = 36 +IP6_REASMREQDS = 37 +IP6_REASMOKS = 38 +IP6_REASMFAILS = 39 +IP6_FRAGOKS = 40 +IP6_FRAGFAILS = 41 +IP6_FRAGCREATES = 42 +IP6_INMCASTPKTS = 43 +IP6_OUTMCASTPKTS = 44 +IP6_INBCASTPKTS = 45 +IP6_OUTBCASTPKTS = 46 +IP6_INOCTETS = 47 +IP6_OUTOCTETS = 48 +IP6_INMCASTOCTETS = 49 +IP6_OUTMCASTOCTETS = 50 +IP6_INBCASTOCTETS = 51 +IP6_OUTBCASTOCTETS = 52 +ICMP6_INMSGS = 53 +ICMP6_INERRORS = 54 +ICMP6_OUTMSGS = 55 +ICMP6_OUTERRORS = 56 + + +class LinkCache(netlink.Cache): + """Cache of network links""" + + def __init__(self, family=socket.AF_UNSPEC, cache=None): + if not cache: + cache = self._alloc_cache_name("route/link") + + self._info_module = None + self._protocol = netlink.NETLINK_ROUTE + self._nl_cache = cache + self._set_arg1(family) + + def __getitem__(self, key): + if type(key) is int: + link = capi.rtnl_link_get(self._nl_cache, key) + else: + link = capi.rtnl_link_get_by_name(self._nl_cache, key) + + if link is None: + raise KeyError() + else: + return Link.from_capi(link) + + @staticmethod + def _new_object(obj): + return Link(obj) + + def _new_cache(self, cache): + return LinkCache(family=self.arg1, cache=cache) + + +class Link(netlink.Object): + """Network link""" + + def __init__(self, obj=None): + netlink.Object.__init__(self, "route/link", "link", obj) + self._rtnl_link = self._obj2type(self._nl_object) + + if self.type: + self._module_lookup("netlink.route.links." + self.type) + + self.inet = inet.InetLink(self) + self.af = {"inet": self.inet} + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, tb): + if exc_type is None: + self.change() + else: + return False + + @classmethod + def from_capi(cls, obj): + return cls(capi.link2obj(obj)) + + @staticmethod + def _obj2type(obj): + return capi.obj2link(obj) + + def __cmp__(self, other): + return self.ifindex - other.ifindex + + @staticmethod + def _new_instance(obj): + if not obj: + raise ValueError() + + return Link(obj) + + @property + @netlink.nlattr(type=int, immutable=True, fmt=util.num) + def ifindex(self): + """interface index""" + return capi.rtnl_link_get_ifindex(self._rtnl_link) + + @ifindex.setter + def ifindex(self, value): + capi.rtnl_link_set_ifindex(self._rtnl_link, int(value)) + + # ifindex is immutable but we assume that if _orig does not + # have an ifindex specified, it was meant to be given here + if capi.rtnl_link_get_ifindex(self._orig) == 0: + capi.rtnl_link_set_ifindex(self._orig, int(value)) + + @property + @netlink.nlattr(type=str, fmt=util.bold) + def name(self): + """Name of link""" + return capi.rtnl_link_get_name(self._rtnl_link) + + @name.setter + def name(self, value): + capi.rtnl_link_set_name(self._rtnl_link, value) + + # name is the secondary identifier, if _orig does not have + # the name specified yet, assume it was meant to be specified + # here. ifindex will always take priority, therefore if ifindex + # is specified as well, this will be ignored automatically. + if capi.rtnl_link_get_name(self._orig) is None: + capi.rtnl_link_set_name(self._orig, value) + + @property + @netlink.nlattr(type=str, fmt=util.string) + def flags(self): + """Flags + Setting this property will *Not* reset flags to value you supply in + Examples: + link.flags = '+xxx' # add xxx flag + link.flags = 'xxx' # exactly the same + link.flags = '-xxx' # remove xxx flag + link.flags = [ '+xxx', '-yyy' ] # list operation + """ + flags = capi.rtnl_link_get_flags(self._rtnl_link) + return capi.rtnl_link_flags2str(flags, 256)[0].split(",") + + def _set_flag(self, flag): + if flag.startswith("-"): + i = capi.rtnl_link_str2flags(flag[1:]) + capi.rtnl_link_unset_flags(self._rtnl_link, i) + elif flag.startswith("+"): + i = capi.rtnl_link_str2flags(flag[1:]) + capi.rtnl_link_set_flags(self._rtnl_link, i) + else: + i = capi.rtnl_link_str2flags(flag) + capi.rtnl_link_set_flags(self._rtnl_link, i) + + @flags.setter + def flags(self, value): + if not (type(value) is str): + for flag in value: + self._set_flag(flag) + else: + self._set_flag(value) + + @property + @netlink.nlattr(type=int, fmt=util.num) + def mtu(self): + """Maximum Transmission Unit""" + return capi.rtnl_link_get_mtu(self._rtnl_link) + + @mtu.setter + def mtu(self, value): + capi.rtnl_link_set_mtu(self._rtnl_link, int(value)) + + @property + @netlink.nlattr(type=int, immutable=True, fmt=util.num) + def family(self): + """Address family""" + return capi.rtnl_link_get_family(self._rtnl_link) + + @family.setter + def family(self, value): + capi.rtnl_link_set_family(self._rtnl_link, value) + + @property + @netlink.nlattr(type=str, fmt=util.addr) + def address(self): + """Hardware address (MAC address)""" + a = capi.rtnl_link_get_addr(self._rtnl_link) + return netlink.AbstractAddress(a) + + @address.setter + def address(self, value): + capi.rtnl_link_set_addr(self._rtnl_link, value._addr) + + @property + @netlink.nlattr(type=str, fmt=util.addr) + def broadcast(self): + """Hardware broadcast address""" + a = capi.rtnl_link_get_broadcast(self._rtnl_link) + return netlink.AbstractAddress(a) + + @broadcast.setter + def broadcast(self, value): + capi.rtnl_link_set_broadcast(self._rtnl_link, value._addr) + + @property + @netlink.nlattr(type=str, immutable=True, fmt=util.string) + def qdisc(self): + """Name of qdisc (cannot be changed)""" + return capi.rtnl_link_get_qdisc(self._rtnl_link) + + @qdisc.setter + def qdisc(self, value): + capi.rtnl_link_set_qdisc(self._rtnl_link, value) + + @property + @netlink.nlattr(type=int, fmt=util.num) + def txqlen(self): + """Length of transmit queue""" + return capi.rtnl_link_get_txqlen(self._rtnl_link) + + @txqlen.setter + def txqlen(self, value): + capi.rtnl_link_set_txqlen(self._rtnl_link, int(value)) + + @property + @netlink.nlattr(type=str, immutable=True, fmt=util.string) + def arptype(self): + """Type of link (cannot be changed)""" + type_ = capi.rtnl_link_get_arptype(self._rtnl_link) + return core_capi.nl_llproto2str(type_, 64)[0] + + @arptype.setter + def arptype(self, value): + i = core_capi.nl_str2llproto(value) + capi.rtnl_link_set_arptype(self._rtnl_link, i) + + @property + @netlink.nlattr(type=str, immutable=True, fmt=util.string, title="state") + def operstate(self): + """Operational status""" + operstate = capi.rtnl_link_get_operstate(self._rtnl_link) + return capi.rtnl_link_operstate2str(operstate, 32)[0] + + @operstate.setter + def operstate(self, value): + i = capi.rtnl_link_str2operstate(value) + capi.rtnl_link_set_operstate(self._rtnl_link, i) + + @property + @netlink.nlattr(type=str, immutable=True, fmt=util.string) + def mode(self): + """Link mode""" + mode = capi.rtnl_link_get_linkmode(self._rtnl_link) + return capi.rtnl_link_mode2str(mode, 32)[0] + + @mode.setter + def mode(self, value): + i = capi.rtnl_link_str2mode(value) + capi.rtnl_link_set_linkmode(self._rtnl_link, i) + + @property + @netlink.nlattr(type=str, fmt=util.string) + def alias(self): + """Interface alias (SNMP)""" + return capi.rtnl_link_get_ifalias(self._rtnl_link) + + @alias.setter + def alias(self, value): + capi.rtnl_link_set_ifalias(self._rtnl_link, value) + + @property + @netlink.nlattr(type=str, fmt=util.string) + def type(self): + """Link type""" + return capi.rtnl_link_get_type(self._rtnl_link) + + @type.setter + def type(self, value): + if capi.rtnl_link_set_type(self._rtnl_link, value) < 0: + raise NameError("unknown info type") + + self._module_lookup("netlink.route.links." + value) + + def get_stat(self, stat): + """Retrieve statistical information""" + if type(stat) is str: + stat = capi.rtnl_link_str2stat(stat) + if stat < 0: + raise NameError("unknown name of statistic") + + return capi.rtnl_link_get_stat(self._rtnl_link, stat) + + def enslave(self, slave, sock=None): + if not sock: + sock = netlink.lookup_socket(netlink.NETLINK_ROUTE) + + return capi.rtnl_link_enslave(sock._sock, self._rtnl_link, slave._rtnl_link) + + def release(self, slave, sock=None): + if not sock: + sock = netlink.lookup_socket(netlink.NETLINK_ROUTE) + + return capi.rtnl_link_release(sock._sock, self._rtnl_link, slave._rtnl_link) + + def add(self, sock=None, flags=None): + if not sock: + sock = netlink.lookup_socket(netlink.NETLINK_ROUTE) + + if not flags: + flags = netlink.NLM_F_CREATE + + ret = capi.rtnl_link_add(sock._sock, self._rtnl_link, flags) + if ret < 0: + raise netlink.KernelError(ret) + + def change(self, sock=None, flags=0): + """Commit changes made to the link object""" + if sock is None: + sock = netlink.lookup_socket(netlink.NETLINK_ROUTE) + + if not self._orig: + raise netlink.NetlinkError("Original link not available") + ret = capi.rtnl_link_change(sock._sock, self._orig, self._rtnl_link, flags) + if ret < 0: + raise netlink.KernelError(ret) + + def delete(self, sock=None): + """Attempt to delete this link in the kernel""" + if sock is None: + sock = netlink.lookup_socket(netlink.NETLINK_ROUTE) + + ret = capi.rtnl_link_delete(sock._sock, self._rtnl_link) + if ret < 0: + raise netlink.KernelError(ret) + + ################################################################### + # private properties + # + # Used for formatting output. USE AT OWN RISK + @property + def _state(self): + if "up" in self.flags: + buf = util.good("up") + if "lowerup" not in self.flags: + buf += " " + util.bad("no-carrier") + else: + buf = util.bad("down") + return buf + + @property + def _brief(self): + return self._module_brief() + self._foreach_af("brief") + + @property + def _flags(self): + ignore = [ + "up", + "running", + "lowerup", + ] + return ",".join([flag for flag in self.flags if flag not in ignore]) + + def _foreach_af(self, name, args=None): + buf = "" + for af in self.af: + try: + func = getattr(self.af[af], name) + s = str(func(args)) + if len(s) > 0: + buf += " " + s + except AttributeError: + pass + return buf + + def format(self, details=False, stats=False, indent=""): + """Return link as formatted text""" + fmt = util.MyFormatter(self, indent) + + buf = fmt.format( + "{a|ifindex} {a|name} {a|arptype} {a|address} " + "{a|_state} <{a|_flags}> {a|_brief}" + ) + + if details: + buf += fmt.nl("\t{t|mtu} {t|txqlen} {t|weight} " "{t|qdisc} {t|operstate}") + buf += fmt.nl("\t{t|broadcast} {t|alias}") + + buf += self._foreach_af("details", fmt) + + if stats: + lst = [ + ["Packets", RX_PACKETS, TX_PACKETS], + ["Bytes", RX_BYTES, TX_BYTES], + ["Errors", RX_ERRORS, TX_ERRORS], + ["Dropped", RX_DROPPED, TX_DROPPED], + ["Compressed", RX_COMPRESSED, TX_COMPRESSED], + ["FIFO Errors", RX_FIFO_ERR, TX_FIFO_ERR], + ["Length Errors", RX_LEN_ERR, None], + ["Over Errors", RX_OVER_ERR, None], + ["CRC Errors", RX_CRC_ERR, None], + ["Frame Errors", RX_FRAME_ERR, None], + ["Missed Errors", RX_MISSED_ERR, None], + ["Abort Errors", None, TX_ABORT_ERR], + ["Carrier Errors", None, TX_CARRIER_ERR], + ["Heartbeat Errors", None, TX_HBEAT_ERR], + ["Window Errors", None, TX_WIN_ERR], + ["Collisions", None, COLLISIONS], + ["Multicast", None, MULTICAST], + ["", None, None], + ["Ipv6:", None, None], + ["Packets", IP6_INPKTS, IP6_OUTPKTS], + ["Bytes", IP6_INOCTETS, IP6_OUTOCTETS], + ["Discards", IP6_INDISCARDS, IP6_OUTDISCARDS], + ["Multicast Packets", IP6_INMCASTPKTS, IP6_OUTMCASTPKTS], + ["Multicast Bytes", IP6_INMCASTOCTETS, IP6_OUTMCASTOCTETS], + ["Broadcast Packets", IP6_INBCASTPKTS, IP6_OUTBCASTPKTS], + ["Broadcast Bytes", IP6_INBCASTOCTETS, IP6_OUTBCASTOCTETS], + ["Delivers", IP6_INDELIVERS, None], + ["Forwarded", None, IP6_OUTFORWDATAGRAMS], + ["No Routes", IP6_INNOROUTES, IP6_OUTNOROUTES], + ["Header Errors", IP6_INHDRERRORS, None], + ["Too Big Errors", IP6_INTOOBIGERRORS, None], + ["Address Errors", IP6_INADDRERRORS, None], + ["Unknown Protocol", IP6_INUNKNOWNPROTOS, None], + ["Truncated Packets", IP6_INTRUNCATEDPKTS, None], + ["Reasm Timeouts", IP6_REASMTIMEOUT, None], + ["Reasm Requests", IP6_REASMREQDS, None], + ["Reasm Failures", IP6_REASMFAILS, None], + ["Reasm OK", IP6_REASMOKS, None], + ["Frag Created", None, IP6_FRAGCREATES], + ["Frag Failures", None, IP6_FRAGFAILS], + ["Frag OK", None, IP6_FRAGOKS], + ["", None, None], + ["ICMPv6:", None, None], + ["Messages", ICMP6_INMSGS, ICMP6_OUTMSGS], + ["Errors", ICMP6_INERRORS, ICMP6_OUTERRORS], + ] + + buf += "\n\t%s%s%s%s\n" % ( + 33 * " ", + util.title("RX"), + 15 * " ", + util.title("TX"), + ) + + for row in lst: + row[0] = util.kw(row[0]) + row[1] = self.get_stat(row[1]) if row[1] else "" + row[2] = self.get_stat(row[2]) if row[2] else "" + buf += "\t{0[0]:27} {0[1]:>16} {0[2]:>16}\n".format(row) + + buf += self._foreach_af("stats") + + return buf + + +def get(name, sock=None): + """Lookup Link object directly from kernel""" + if not name: + raise ValueError() + + if not sock: + sock = netlink.lookup_socket(netlink.NETLINK_ROUTE) + + link = capi.get_from_kernel(sock._sock, 0, name) + if not link: + return None + + return Link.from_capi(link) + + +_link_cache = LinkCache() + + +def resolve(name): + _link_cache.refill() + return _link_cache[name] diff --git a/libnl/python/netlink/route/links/__init__.py b/libnl/python/netlink/route/links/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/libnl/python/netlink/route/links/bridge.py b/libnl/python/netlink/route/links/bridge.py new file mode 100644 index 0000000..d3e72e5 --- /dev/null +++ b/libnl/python/netlink/route/links/bridge.py @@ -0,0 +1,130 @@ +# +# Copyright (c) 2013 Nicolas PLANEL +# + +"""BRIDGE network link + +""" + +from __future__ import absolute_import +from __future__ import print_function + +from ... import core as netlink +from .. import capi as capi + + +class BRIDGELink(object): + def __init__(self, link): + self._link = link + self._has_ext_info = capi.rtnl_link_bridge_has_ext_info(self._link) + self._port_state_values = [ + "disabled", + "listening", + "learning", + "forwarding", + "blocking", + ] + + def bridge_assert_ext_info(self): + if not self._has_ext_info: + print( + """ + Please update your kernel to be able to call this method. + Your current kernel bridge version is too old to support this extention. + """ + ) + raise RuntimeWarning() + + def port_state2str(self, state): + return self._port_state_values[state] + + def str2port_state(self, str): + for value, port in enumerate(self._port_state_values): + if str.lower() == port: + return value + raise ValueError() + + @property + @netlink.nlattr(type=int) + def port_state(self): + """bridge state : + %s + """ % ( + self.port_state + ) + return capi.rtnl_link_bridge_get_state(self._link) + + @port_state.setter + def port_state(self, state): + capi.rtnl_link_bridge_set_state(self._link, int(state)) + + @property + @netlink.nlattr(type=int) + def priority(self): + """bridge prio""" + self.bridge_assert_ext_info() + return capi.rtnl_link_bridge_get_prio(self._link) + + @priority.setter + def priority(self, prio): + self.bridge_assert_ext_info() + if prio < 0 or prio >= 2**16: + raise ValueError() + capi.rtnl_link_bridge_set_prio(self._link, int(prio)) + + @property + @netlink.nlattr(type=int) + def cost(self): + """bridge prio""" + self.bridge_assert_ext_info() + return capi.rtnl_link_bridge_get_cost(self._link) + + @cost.setter + def cost(self, cost): + self.bridge_assert_ext_info() + if cost < 0 or cost >= 2**32: + raise ValueError() + capi.rtnl_link_bridge_set_cost(self._link, int(cost)) + + @property + @netlink.nlattr(type=str) + def flags(self): + """BRIDGE flags + Setting this property will *Not* reset flags to value you supply in + Examples: + link.flags = '+xxx' # add xxx flag + link.flags = 'xxx' # exactly the same + link.flags = '-xxx' # remove xxx flag + link.flags = [ '+xxx', '-yyy' ] # list operation + """ + self.bridge_assert_ext_info() + flags = capi.rtnl_link_bridge_get_flags(self._link) + return capi.rtnl_link_bridge_flags2str(flags, 256)[0].split(",") + + def _set_flag(self, flag): + if flag.startswith("-"): + i = capi.rtnl_link_bridge_str2flags(flag[1:]) + capi.rtnl_link_bridge_unset_flags(self._link, i) + elif flag.startswith("+"): + i = capi.rtnl_link_bridge_str2flags(flag[1:]) + capi.rtnl_link_bridge_set_flags(self._link, i) + else: + i = capi.rtnl_link_bridge_str2flags(flag) + capi.rtnl_link_bridge_set_flags(self._link, i) + + @flags.setter + def flags(self, value): + self.bridge_assert_ext_info() + if type(value) is list: + for flag in value: + self._set_flag(flag) + else: + self._set_flag(value) + + def brief(self): + return "bridge-has-ext-info {0}".format(self._has_ext_info) + + +def init(link): + link.bridge = BRIDGELink(link._rtnl_link) + return link.bridge diff --git a/libnl/python/netlink/route/links/dummy.py b/libnl/python/netlink/route/links/dummy.py new file mode 100644 index 0000000..1dc3c68 --- /dev/null +++ b/libnl/python/netlink/route/links/dummy.py @@ -0,0 +1,27 @@ +# +# Copyright (c) 2011 Thomas Graf +# + +"""Dummy + +""" +from __future__ import absolute_import + +__version__ = "1.0" +__all__ = [ + "init", +] + + +class DummyLink(object): + def __init__(self, link): + self._rtnl_link = link + + @staticmethod + def brief(): + return "dummy" + + +def init(link): + link.dummy = DummyLink(link._rtnl_link) + return link.dummy diff --git a/libnl/python/netlink/route/links/inet.py b/libnl/python/netlink/route/links/inet.py new file mode 100644 index 0000000..7aeedcd --- /dev/null +++ b/libnl/python/netlink/route/links/inet.py @@ -0,0 +1,157 @@ +# +# Copyright (c) 2011 Thomas Graf +# + +"""IPv4 + +""" + +from __future__ import absolute_import + +__all__ = [] + +from ... import core as netlink +from .. import capi as capi +from ... import util as util + +DEVCONF_FORWARDING = 1 +DEVCONF_MC_FORWARDING = 2 +DEVCONF_PROXY_ARP = 3 +DEVCONF_ACCEPT_REDIRECTS = 4 +DEVCONF_SECURE_REDIRECTS = 5 +DEVCONF_SEND_REDIRECTS = 6 +DEVCONF_SHARED_MEDIA = 7 +DEVCONF_RP_FILTER = 8 +DEVCONF_ACCEPT_SOURCE_ROUTE = 9 +DEVCONF_BOOTP_RELAY = 10 +DEVCONF_LOG_MARTIANS = 11 +DEVCONF_TAG = 12 +DEVCONF_ARPFILTER = 13 +DEVCONF_MEDIUM_ID = 14 +DEVCONF_NOXFRM = 15 +DEVCONF_NOPOLICY = 16 +DEVCONF_FORCE_IGMP_VERSION = 17 +DEVCONF_ARP_ANNOUNCE = 18 +DEVCONF_ARP_IGNORE = 19 +DEVCONF_PROMOTE_SECONDARIES = 20 +DEVCONF_ARP_ACCEPT = 21 +DEVCONF_ARP_NOTIFY = 22 +DEVCONF_ACCEPT_LOCAL = 23 +DEVCONF_SRC_VMARK = 24 +DEVCONF_PROXY_ARP_PVLAN = 25 +DEVCONF_MAX = DEVCONF_PROXY_ARP_PVLAN + + +def _resolve(id): + if type(id) is str: + id = capi.rtnl_link_inet_str2devconf(id)[0] + if id < 0: + raise NameError("unknown configuration id") + return id + + +class InetLink(object): + def __init__(self, link): + self._link = link + + def details(self, fmt): + buf = fmt.nl("\n\t{0}\n\t".format(util.title("Configuration:"))) + + for i in range(DEVCONF_FORWARDING, DEVCONF_MAX + 1): + if i & 1 and i > 1: + buf += fmt.nl("\t") + txt = util.kw(capi.rtnl_link_inet_devconf2str(i, 32)[0]) + buf += fmt.format("{0:28s} {1:12} ", txt, self.get_conf(i)) + + return buf + + def get_conf(self, id): + return capi.inet_get_conf(self._link._rtnl_link, _resolve(id)) + + def set_conf(self, id, value): + return capi.rtnl_link_inet_set_conf( + self._link._rtnl_link, _resolve(id), int(value) + ) + + @property + @netlink.nlattr(type=bool, fmt=util.boolean) + def forwarding(self): + return bool(self.get_conf(DEVCONF_FORWARDING)) + + @forwarding.setter + def forwarding(self, value): + self.set_conf(DEVCONF_FORWARDING, int(value)) + + @property + @netlink.nlattr(type=bool, fmt=util.boolean) + def mc_forwarding(self): + return bool(self.get_conf(DEVCONF_MC_FORWARDING)) + + @mc_forwarding.setter + def mc_forwarding(self, value): + self.set_conf(DEVCONF_MC_FORWARDING, int(value)) + + @property + @netlink.nlattr(type=bool, fmt=util.boolean) + def proxy_arp(self): + return bool(self.get_conf(DEVCONF_PROXY_ARP)) + + @proxy_arp.setter + def proxy_arp(self, value): + self.set_conf(DEVCONF_PROXY_ARP, int(value)) + + @property + @netlink.nlattr(type=bool, fmt=util.boolean) + def accept_redirects(self): + return bool(self.get_conf(DEVCONF_ACCEPT_REDIRECTS)) + + @accept_redirects.setter + def accept_redirects(self, value): + self.set_conf(DEVCONF_ACCEPT_REDIRECTS, int(value)) + + @property + @netlink.nlattr(type=bool, fmt=util.boolean) + def secure_redirects(self): + return bool(self.get_conf(DEVCONF_SECURE_REDIRECTS)) + + @secure_redirects.setter + def secure_redirects(self, value): + self.set_conf(DEVCONF_SECURE_REDIRECTS, int(value)) + + @property + @netlink.nlattr(type=bool, fmt=util.boolean) + def send_redirects(self): + return bool(self.get_conf(DEVCONF_SEND_REDIRECTS)) + + @send_redirects.setter + def send_redirects(self, value): + self.set_conf(DEVCONF_SEND_REDIRECTS, int(value)) + + @property + @netlink.nlattr(type=bool, fmt=util.boolean) + def shared_media(self): + return bool(self.get_conf(DEVCONF_SHARED_MEDIA)) + + @shared_media.setter + def shared_media(self, value): + self.set_conf(DEVCONF_SHARED_MEDIA, int(value)) + + +# 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, diff --git a/libnl/python/netlink/route/links/vlan.py b/libnl/python/netlink/route/links/vlan.py new file mode 100644 index 0000000..8bee3c9 --- /dev/null +++ b/libnl/python/netlink/route/links/vlan.py @@ -0,0 +1,74 @@ +# +# Copyright (c) 2011 Thomas Graf +# + +"""VLAN network link + +""" + +from __future__ import absolute_import + + +from ... import core as netlink +from .. import capi as capi + + +class VLANLink(object): + def __init__(self, link): + self._link = link + + @property + @netlink.nlattr(type=int) + def id(self): + """vlan identifier""" + return capi.rtnl_link_vlan_get_id(self._link) + + @id.setter + def id(self, value): + capi.rtnl_link_vlan_set_id(self._link, int(value)) + + @property + @netlink.nlattr(type=str) + def flags(self): + """VLAN flags + Setting this property will *Not* reset flags to value you supply in + Examples: + link.flags = '+xxx' # add xxx flag + link.flags = 'xxx' # exactly the same + link.flags = '-xxx' # remove xxx flag + link.flags = [ '+xxx', '-yyy' ] # list operation + """ + flags = capi.rtnl_link_vlan_get_flags(self._link) + return capi.rtnl_link_vlan_flags2str(flags, 256)[0].split(",") + + def _set_flag(self, flag): + if flag.startswith("-"): + i = capi.rtnl_link_vlan_str2flags(flag[1:]) + capi.rtnl_link_vlan_unset_flags(self._link, i) + elif flag.startswith("+"): + i = capi.rtnl_link_vlan_str2flags(flag[1:]) + capi.rtnl_link_vlan_set_flags(self._link, i) + else: + i = capi.rtnl_link_vlan_str2flags(flag) + capi.rtnl_link_vlan_set_flags(self._link, i) + + @flags.setter + def flags(self, value): + if type(value) is list: + for flag in value: + self._set_flag(flag) + else: + self._set_flag(value) + + ################################################################### + # TODO: + # - ingress map + # - egress map + + def brief(self): + return "vlan-id {0}".format(self.id) + + +def init(link): + link.vlan = VLANLink(link._rtnl_link) + return link.vlan diff --git a/libnl/python/netlink/route/qdisc/__init__.py b/libnl/python/netlink/route/qdisc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/libnl/python/netlink/route/qdisc/htb.py b/libnl/python/netlink/route/qdisc/htb.py new file mode 100644 index 0000000..0dc70f4 --- /dev/null +++ b/libnl/python/netlink/route/qdisc/htb.py @@ -0,0 +1,149 @@ +# +# Copyright (c) 2011 Thomas Graf +# + +"""HTB qdisc + +""" + +from __future__ import absolute_import + +from ... import core as netlink +from ... import util as util +from .. import capi as capi +from .. import tc as tc + + +class HTBQdisc(object): + def __init__(self, qdisc): + self._qdisc = qdisc + + @property + @netlink.nlattr(type=int) + def default_class(self): + return tc.Handle(capi.rtnl_htb_get_defcls(self._qdisc._rtnl_qdisc)) + + @default_class.setter + def default_class(self, value): + capi.rtnl_htb_set_defcls(self._qdisc._rtnl_qdisc, int(value)) + + @property + @netlink.nlattr(type=int) + def r2q(self): + return capi.rtnl_htb_get_rate2quantum(self._qdisc._rtnl_qdisc) + + @r2q.setter + def r2q(self, value): + capi.rtnl_htb_get_rate2quantum(self._qdisc._rtnl_qdisc, int(value)) + + def brief(self): + fmt = util.MyFormatter(self) + + ret = " {s|default-class!k} {a|default_class}" + + if self.r2q: + ret += " {s|r2q!k} {a|r2q}" + + return fmt.format(ret) + + +class HTBClass(object): + def __init__(self, cl): + self._class = cl + + @property + @netlink.nlattr(type=str) + def rate(self): + rate = capi.rtnl_htb_get_rate(self._class._rtnl_class) + return util.Rate(rate) + + @rate.setter + def rate(self, value): + capi.rtnl_htb_set_rate(self._class._rtnl_class, int(value)) + + @property + @netlink.nlattr(type=str) + def ceil(self): + ceil = capi.rtnl_htb_get_ceil(self._class._rtnl_class) + return util.Rate(ceil) + + @ceil.setter + def ceil(self, value): + capi.rtnl_htb_set_ceil(self._class._rtnl_class, int(value)) + + @property + @netlink.nlattr(type=str) + def burst(self): + burst = capi.rtnl_htb_get_rbuffer(self._class._rtnl_class) + return util.Size(burst) + + @burst.setter + def burst(self, value): + capi.rtnl_htb_set_rbuffer(self._class._rtnl_class, int(value)) + + @property + @netlink.nlattr(type=str) + def ceil_burst(self): + burst = capi.rtnl_htb_get_cbuffer(self._class._rtnl_class) + return util.Size(burst) + + @ceil_burst.setter + def ceil_burst(self, value): + capi.rtnl_htb_set_cbuffer(self._class._rtnl_class, int(value)) + + @property + @netlink.nlattr(type=int) + def prio(self): + return capi.rtnl_htb_get_prio(self._class._rtnl_class) + + @prio.setter + def prio(self, value): + capi.rtnl_htb_set_prio(self._class._rtnl_class, int(value)) + + @property + @netlink.nlattr(type=int) + def quantum(self): + return capi.rtnl_htb_get_quantum(self._class._rtnl_class) + + @quantum.setter + def quantum(self, value): + capi.rtnl_htb_set_quantum(self._class._rtnl_class, int(value)) + + @property + @netlink.nlattr(type=int) + def level(self): + return capi.rtnl_htb_get_level(self._class._rtnl_class) + + @level.setter + def level(self, value): + capi.rtnl_htb_set_level(self._class._rtnl_class, int(value)) + + def brief(self): + fmt = util.MyFormatter(self) + + ret = " {t|prio} {t|rate}" + + if self.rate != self.ceil: + ret += " {s|borrow-up-to!k} {a|ceil}" + + ret += " {t|burst}" + + return fmt.format(ret) + + def details(self): + fmt = util.MyFormatter(self) + + return fmt.nl("\t{t|level} {t|quantum}") + + +def init_qdisc(qdisc): + qdisc.htb = HTBQdisc(qdisc) + return qdisc.htb + + +def init_class(cl): + cl.htb = HTBClass(cl) + return cl.htb + + +# extern void rtnl_htb_set_quantum(struct rtnl_class *, uint32_t quantum); diff --git a/libnl/python/netlink/route/tc.py b/libnl/python/netlink/route/tc.py new file mode 100644 index 0000000..eb0037e --- /dev/null +++ b/libnl/python/netlink/route/tc.py @@ -0,0 +1,612 @@ +# +# Copyright (c) 2011 Thomas Graf +# +from __future__ import absolute_import + +__all__ = [ + "TcCache", + "Tc", + "QdiscCache", + "Qdisc", + "TcClassCache", + "TcClass", +] + +from .. import core as netlink +from . import capi as capi +from .. import util as util +from . import link as Link + +TC_PACKETS = 0 +TC_BYTES = 1 +TC_RATE_BPS = 2 +TC_RATE_PPS = 3 +TC_QLEN = 4 +TC_BACKLOG = 5 +TC_DROPS = 6 +TC_REQUEUES = 7 +TC_OVERLIMITS = 9 + +TC_H_ROOT = 0xFFFFFFFF +TC_H_INGRESS = 0xFFFFFFF1 + +STAT_PACKETS = 0 +STAT_BYTES = 1 +STAT_RATE_BPS = 2 +STAT_RATE_PPS = 3 +STAT_QLEN = 4 +STAT_BACKLOG = 5 +STAT_DROPS = 6 +STAT_REQUEUES = 7 +STAT_OVERLIMITS = 8 +STAT_MAX = STAT_OVERLIMITS + + +class Handle(object): + """Traffic control handle + + Representation of a traffic control handle which uniquely identifies + each traffic control object in its link namespace. + + handle = tc.Handle('10:20') + handle = tc.handle('root') + print int(handle) + print str(handle) + """ + + def __init__(self, val=None): + if type(val) is str: + val = capi.tc_str2handle(val) + elif not val: + val = 0 + + self._val = int(val) + + def __cmp__(self, other): + if other is None: + other = 0 + + if isinstance(other, Handle): + return int(self) - int(other) + elif isinstance(other, int): + return int(self) - other + else: + raise TypeError() + + def __int__(self): + return self._val + + def __str__(self): + return capi.rtnl_tc_handle2str(self._val, 64)[0] + + def isroot(self): + return self._val == TC_H_ROOT or self._val == TC_H_INGRESS + + +class TcCache(netlink.Cache): + """Cache of traffic control object""" + + def __getitem__(self, key): + raise NotImplementedError() + + +class Tc(netlink.Object): + def __cmp__(self, other): + diff = self.ifindex - other.ifindex + if diff == 0: + diff = int(self.handle) - int(other.handle) + return diff + + def _tc_module_lookup(self): + self._module_lookup(self._module_path + self.kind, "init_" + self._name) + + @property + def root(self): + """True if tc object is a root object""" + return self.parent.isroot() + + @property + def ifindex(self): + """interface index""" + return capi.rtnl_tc_get_ifindex(self._rtnl_tc) + + @ifindex.setter + def ifindex(self, value): + capi.rtnl_tc_set_ifindex(self._rtnl_tc, int(value)) + + @property + def link(self): + link = capi.rtnl_tc_get_link(self._rtnl_tc) + if not link: + return None + + return Link.Link.from_capi(link) + + @link.setter + def link(self, value): + capi.rtnl_tc_set_link(self._rtnl_tc, value._link) + + @property + def mtu(self): + return capi.rtnl_tc_get_mtu(self._rtnl_tc) + + @mtu.setter + def mtu(self, value): + capi.rtnl_tc_set_mtu(self._rtnl_tc, int(value)) + + @property + def mpu(self): + return capi.rtnl_tc_get_mpu(self._rtnl_tc) + + @mpu.setter + def mpu(self, value): + capi.rtnl_tc_set_mpu(self._rtnl_tc, int(value)) + + @property + def overhead(self): + return capi.rtnl_tc_get_overhead(self._rtnl_tc) + + @overhead.setter + def overhead(self, value): + capi.rtnl_tc_set_overhead(self._rtnl_tc, int(value)) + + @property + def linktype(self): + return capi.rtnl_tc_get_linktype(self._rtnl_tc) + + @linktype.setter + def linktype(self, value): + capi.rtnl_tc_set_linktype(self._rtnl_tc, int(value)) + + @property + @netlink.nlattr(fmt=util.handle) + def handle(self): + return Handle(capi.rtnl_tc_get_handle(self._rtnl_tc)) + + @handle.setter + def handle(self, value): + capi.rtnl_tc_set_handle(self._rtnl_tc, int(value)) + + @property + @netlink.nlattr(fmt=util.handle) + def parent(self): + return Handle(capi.rtnl_tc_get_parent(self._rtnl_tc)) + + @parent.setter + def parent(self, value): + capi.rtnl_tc_set_parent(self._rtnl_tc, int(value)) + + @property + @netlink.nlattr(fmt=util.bold) + def kind(self): + return capi.rtnl_tc_get_kind(self._rtnl_tc) + + @kind.setter + def kind(self, value): + capi.rtnl_tc_set_kind(self._rtnl_tc, value) + self._tc_module_lookup() + + def get_stat(self, id): + return capi.rtnl_tc_get_stat(self._rtnl_tc, id) + + @property + def _dev(self): + buf = util.kw("dev") + " " + + if self.link: + return buf + util.string(self.link.name) + else: + return buf + util.num(self.ifindex) + + def brief(self, title, nodev=False, noparent=False): + ret = title + " {a|kind} {a|handle}" + + if not nodev: + ret += " {a|_dev}" + + if not noparent: + ret += " {t|parent}" + + return ret + self._module_brief() + + @staticmethod + def details(): + return "{t|mtu} {t|mpu} {t|overhead} {t|linktype}" + + @property + def packets(self): + return self.get_stat(STAT_PACKETS) + + @property + def bytes(self): + return self.get_stat(STAT_BYTES) + + @property + def qlen(self): + return self.get_stat(STAT_QLEN) + + @staticmethod + def stats(fmt): + return fmt.nl("{t|packets} {t|bytes} {t|qlen}") + + +class QdiscCache(netlink.Cache): + """Cache of qdiscs""" + + def __init__(self, cache=None): + if not cache: + cache = self._alloc_cache_name("route/qdisc") + + self._protocol = netlink.NETLINK_ROUTE + self._nl_cache = cache + + # def __getitem__(self, key): + # if type(key) is int: + # link = capi.rtnl_link_get(self._this, key) + # elif type(key) is str: + # link = capi.rtnl_link_get_by_name(self._this, key) + # + # if qdisc is None: + # raise KeyError() + # else: + # return Qdisc._from_capi(capi.qdisc2obj(qdisc)) + + @staticmethod + def _new_object(obj): + return Qdisc(obj) + + @staticmethod + def _new_cache(cache): + return QdiscCache(cache=cache) + + +class Qdisc(Tc): + """Queueing discipline""" + + def __init__(self, obj=None): + netlink.Object.__init__(self, "route/qdisc", "qdisc", obj) + self._module_path = "netlink.route.qdisc." + self._rtnl_qdisc = self._obj2type(self._nl_object) + self._rtnl_tc = capi.obj2tc(self._nl_object) + + if self.kind: + self._tc_module_lookup() + + @classmethod + def from_capi(cls, obj): + return cls(capi.qdisc2obj(obj)) + + @staticmethod + def _obj2type(obj): + return capi.obj2qdisc(obj) + + @staticmethod + def _new_instance(obj): + if not obj: + raise ValueError() + + return Qdisc(obj) + + @property + def childs(self): + ret = [] + + if int(self.handle): + ret += get_cls(self.ifindex, parent=self.handle) + + if self.root: + ret += get_class(self.ifindex, parent=TC_H_ROOT) + + ret += get_class(self.ifindex, parent=self.handle) + + return ret + + # def add(self, socket, flags=None): + # if not flags: + # flags = netlink.NLM_F_CREATE + # + # ret = capi.rtnl_link_add(socket._sock, self._link, flags) + # if ret < 0: + # raise netlink.KernelError(ret) + # + # def change(self, socket, flags=0): + # """Commit changes made to the link object""" + # if not self._orig: + # raise NetlinkError('Original link not available') + # ret = capi.rtnl_link_change(socket._sock, self._orig, self._link, flags) + # if ret < 0: + # raise netlink.KernelError(ret) + # + # def delete(self, socket): + # """Attempt to delete this link in the kernel""" + # ret = capi.rtnl_link_delete(socket._sock, self._link) + # if ret < 0: + # raise netlink.KernelError(ret) + + def format( + self, details=False, stats=False, nodev=False, noparent=False, indent="" + ): + """Return qdisc as formatted text""" + fmt = util.MyFormatter(self, indent) + + buf = fmt.format(self.brief("qdisc", nodev, noparent)) + + if details: + buf += fmt.nl("\t" + self.details()) + + if stats: + buf += self.stats(fmt) + + # if stats: + # l = [['Packets', RX_PACKETS, TX_PACKETS], + # ['Bytes', RX_BYTES, TX_BYTES], + # ['Errors', RX_ERRORS, TX_ERRORS], + # ['Dropped', RX_DROPPED, TX_DROPPED], + # ['Compressed', RX_COMPRESSED, TX_COMPRESSED], + # ['FIFO Errors', RX_FIFO_ERR, TX_FIFO_ERR], + # ['Length Errors', RX_LEN_ERR, None], + # ['Over Errors', RX_OVER_ERR, None], + # ['CRC Errors', RX_CRC_ERR, None], + # ['Frame Errors', RX_FRAME_ERR, None], + # ['Missed Errors', RX_MISSED_ERR, None], + # ['Abort Errors', None, TX_ABORT_ERR], + # ['Carrier Errors', None, TX_CARRIER_ERR], + # ['Heartbeat Errors', None, TX_HBEAT_ERR], + # ['Window Errors', None, TX_WIN_ERR], + # ['Collisions', None, COLLISIONS], + # ['Multicast', None, MULTICAST], + # ['', None, None], + # ['Ipv6:', None, None], + # ['Packets', IP6_INPKTS, IP6_OUTPKTS], + # ['Bytes', IP6_INOCTETS, IP6_OUTOCTETS], + # ['Discards', IP6_INDISCARDS, IP6_OUTDISCARDS], + # ['Multicast Packets', IP6_INMCASTPKTS, IP6_OUTMCASTPKTS], + # ['Multicast Bytes', IP6_INMCASTOCTETS, IP6_OUTMCASTOCTETS], + # ['Broadcast Packets', IP6_INBCASTPKTS, IP6_OUTBCASTPKTS], + # ['Broadcast Bytes', IP6_INBCASTOCTETS, IP6_OUTBCASTOCTETS], + # ['Delivers', IP6_INDELIVERS, None], + # ['Forwarded', None, IP6_OUTFORWDATAGRAMS], + # ['No Routes', IP6_INNOROUTES, IP6_OUTNOROUTES], + # ['Header Errors', IP6_INHDRERRORS, None], + # ['Too Big Errors', IP6_INTOOBIGERRORS, None], + # ['Address Errors', IP6_INADDRERRORS, None], + # ['Unknown Protocol', IP6_INUNKNOWNPROTOS, None], + # ['Truncated Packets', IP6_INTRUNCATEDPKTS, None], + # ['Reasm Timeouts', IP6_REASMTIMEOUT, None], + # ['Reasm Requests', IP6_REASMREQDS, None], + # ['Reasm Failures', IP6_REASMFAILS, None], + # ['Reasm OK', IP6_REASMOKS, None], + # ['Frag Created', None, IP6_FRAGCREATES], + # ['Frag Failures', None, IP6_FRAGFAILS], + # ['Frag OK', None, IP6_FRAGOKS], + # ['', None, None], + # ['ICMPv6:', None, None], + # ['Messages', ICMP6_INMSGS, ICMP6_OUTMSGS], + # ['Errors', ICMP6_INERRORS, ICMP6_OUTERRORS]] + # + # buf += '\n\t%s%s%s%s\n' % (33 * ' ', util.title('RX'), + # 15 * ' ', util.title('TX')) + # + # for row in l: + # row[0] = util.kw(row[0]) + # row[1] = self.get_stat(row[1]) if row[1] else '' + # row[2] = self.get_stat(row[2]) if row[2] else '' + # buf += '\t{0:27} {1:>16} {2:>16}\n'.format(*row) + + return buf + + +class TcClassCache(netlink.Cache): + """Cache of traffic classes""" + + def __init__(self, ifindex, cache=None): + if not cache: + cache = self._alloc_cache_name("route/class") + + self._protocol = netlink.NETLINK_ROUTE + self._nl_cache = cache + self._set_arg1(ifindex) + + @staticmethod + def _new_object(obj): + return TcClass(obj) + + def _new_cache(self, cache): + return TcClassCache(self.arg1, cache=cache) + + +class TcClass(Tc): + """Traffic Class""" + + def __init__(self, obj=None): + netlink.Object.__init__(self, "route/class", "class", obj) + self._module_path = "netlink.route.qdisc." + self._rtnl_class = self._obj2type(self._nl_object) + self._rtnl_tc = capi.obj2tc(self._nl_object) + + if self.kind: + self._tc_module_lookup() + + @classmethod + def from_capi(cls, obj): + return cls(capi.class2obj(obj)) + + @staticmethod + def _obj2type(obj): + return capi.obj2class(obj) + + @staticmethod + def _new_instance(obj): + if not obj: + raise ValueError() + + return TcClass(obj) + + @property + def childs(self): + ret = [] + + # classes can have classifiers, child classes and leaf + # qdiscs + ret += get_cls(self.ifindex, parent=self.handle) + ret += get_class(self.ifindex, parent=self.handle) + ret += get_qdisc(self.ifindex, parent=self.handle) + + return ret + + def format( + self, details=False, _stats=False, nodev=False, noparent=False, indent="" + ): + """Return class as formatted text""" + fmt = util.MyFormatter(self, indent) + + buf = fmt.format(self.brief("class", nodev, noparent)) + + if details: + buf += fmt.nl("\t" + self.details()) + + return buf + + +class ClassifierCache(netlink.Cache): + """Cache of traffic classifiers objects""" + + def __init__(self, ifindex, parent, cache=None): + if not cache: + cache = self._alloc_cache_name("route/cls") + + self._protocol = netlink.NETLINK_ROUTE + self._nl_cache = cache + self._set_arg1(ifindex) + self._set_arg2(int(parent)) + + @staticmethod + def _new_object(obj): + return Classifier(obj) + + def _new_cache(self, cache): + return ClassifierCache(self.arg1, self.arg2, cache=cache) + + +class Classifier(Tc): + """Classifier""" + + def __init__(self, obj=None): + netlink.Object.__init__(self, "route/cls", "cls", obj) + self._module_path = "netlink.route.cls." + self._rtnl_cls = self._obj2type(self._nl_object) + self._rtnl_tc = capi.obj2tc(self._nl_object) + + @classmethod + def from_capi(cls, obj): + return cls(capi.cls2obj(obj)) + + @staticmethod + def _obj2type(obj): + return capi.obj2cls(obj) + + @staticmethod + def _new_instance(obj): + if not obj: + raise ValueError() + + return Classifier(obj) + + @property + def priority(self): + return capi.rtnl_cls_get_prio(self._rtnl_cls) + + @priority.setter + def priority(self, value): + capi.rtnl_cls_set_prio(self._rtnl_cls, int(value)) + + @property + def protocol(self): + return capi.rtnl_cls_get_protocol(self._rtnl_cls) + + @protocol.setter + def protocol(self, value): + capi.rtnl_cls_set_protocol(self._rtnl_cls, int(value)) + + @property + def childs(self): + return [] + + def format( + self, details=False, _stats=False, nodev=False, noparent=False, indent="" + ): + """Return class as formatted text""" + fmt = util.MyFormatter(self, indent) + + buf = fmt.format(self.brief("classifier", nodev, noparent)) + buf += fmt.format(" {t|priority} {t|protocol}") + + if details: + buf += fmt.nl("\t" + self.details()) + + return buf + + +_qdisc_cache = QdiscCache() + + +def get_qdisc(ifindex, handle=None, parent=None): + lst = [] + + _qdisc_cache.refill() + + for qdisc in _qdisc_cache: + if qdisc.ifindex != ifindex: + continue + if (handle is not None) and (qdisc.handle != handle): + continue + if (parent is not None) and (qdisc.parent != parent): + continue + lst.append(qdisc) + + return lst + + +_class_cache = {} + + +def get_class(ifindex, parent, handle=None): + lst = [] + + try: + cache = _class_cache[ifindex] + except KeyError: + cache = TcClassCache(ifindex) + _class_cache[ifindex] = cache + + cache.refill() + + for cl in cache: + if (parent is not None) and (cl.parent != parent): + continue + if (handle is not None) and (cl.handle != handle): + continue + lst.append(cl) + + return lst + + +_cls_cache = {} + + +def get_cls(ifindex, parent, handle=None): + + chain = _cls_cache.get(ifindex, {}) + + try: + cache = chain[parent] + except KeyError: + cache = ClassifierCache(ifindex, parent) + chain[parent] = cache + + cache.refill() + + if handle is None: + return list(cache) + + return [cls for cls in cache if cls.handle == handle] diff --git a/libnl/python/netlink/util.py b/libnl/python/netlink/util.py new file mode 100644 index 0000000..3adc509 --- /dev/null +++ b/libnl/python/netlink/util.py @@ -0,0 +1,196 @@ +# +# Utilities +# +# Copyright (c) 2011 Thomas Graf +# + +"""utility module for netlink + +""" + +from __future__ import absolute_import + +from . import capi as capi +from string import Formatter + +__version__ = "1.0" + + +# rename into colored_output +def _color(t, c): + return "{esc}[{color}m{text}{esc}[0m".format(esc=b"\x1b".decode(), color=c, text=t) + + +def black(t): + return _color(t, 30) + + +def red(t): + return _color(t, 31) + + +def green(t): + return _color(t, 32) + + +def yellow(t): + return _color(t, 33) + + +def blue(t): + return _color(t, 34) + + +def magenta(t): + return _color(t, 35) + + +def cyan(t): + return _color(t, 36) + + +def white(t): + return _color(t, 37) + + +def bold(t): + return _color(t, 1) + + +def kw(t): + return yellow(t) + + +def num(t): + return str(t) + + +def string(t): + return t + + +def addr(t): + return str(t) + + +def bad(t): + return red(t) + + +def good(t): + return green(t) + + +def title(t): + return t + + +def boolean(t): + return str(t) + + +def handle(t): + return str(t) + + +class MyFormatter(Formatter): + def __init__(self, obj, indent=""): + self._obj = obj + self._indent = indent + + def _nlattr(self, key): + value = getattr(self._obj.__class__, key) + if not isinstance(value, property): + raise ValueError("Invalid formatting string {0}".format(key)) + + d = getattr(value.fget, "formatinfo", {}) + + # value = value.fget() is exactly the same + value = getattr(self._obj, key) + + if "fmt" in d: + value = d["fmt"](value) + + title_ = d.get("title", None) + + return title_, str(value) + + def get_value(self, key, args, kwds): + # Let default get_value() handle ints + if not isinstance(key, str): + return Formatter.get_value(self, key, args, kwds) + + # HACK, we allow defining strings via fields to allow + # conversions + if key[:2] == "s|": + return key[2:] + + if key[:2] == "t|": + # title mode ("TITLE ATTR") + include_title = True + elif key[:2] == "a|": + # plain attribute mode ("ATTR") + include_title = False + else: + # No special field, have default get_value() get it + return Formatter.get_value(self, key, args, kwds) + + key = key[2:] + (title_, value) = self._nlattr(key) + + if include_title: + if not title_: + title_ = key # fall back to key as title + value = "{0} {1}".format(kw(title_), value) + + return value + + def convert_field(self, value, conversion): + if conversion == "r": + return repr(value) + elif conversion == "s": + return str(value) + elif conversion == "k": + return kw(value) + elif conversion == "b": + return bold(value) + elif conversion is None: + return value + + raise ValueError("Unknown converion specifier {0!s}".format(conversion)) + + def nl(self, format_string=""): + return "\n" + self._indent + self.format(format_string) + + +NL_BYTE_RATE = 0 +NL_BIT_RATE = 1 + + +class Rate(object): + def __init__(self, rate, mode=NL_BYTE_RATE): + self._rate = rate + self._mode = mode + + def __str__(self): + return capi.nl_rate2str(self._rate, self._mode, 32)[1] + + def __int__(self): + return self._rate + + def __cmp__(self, other): + return int(self) - int(other) + + +class Size(object): + def __init__(self, size): + self._size = size + + def __str__(self): + return capi.nl_size2str(self._size, 32)[0] + + def __int__(self): + return self._size + + def __cmp__(self, other): + return int(self) - int(other) diff --git a/libnl/python/netlink/utils.h b/libnl/python/netlink/utils.h new file mode 100644 index 0000000..3ec2fb5 --- /dev/null +++ b/libnl/python/netlink/utils.h @@ -0,0 +1,41 @@ +struct list_head { + struct list_head *next; +}; + +#define LIST_HEAD(name) \ + struct list_head name = { &(name) } + +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +static inline void list_add(struct list_head *new, struct list_head *head) +{ + new->next = head->next; + head->next = new; +} + +static inline void list_del(struct list_head *entry, struct list_head *prev) +{ + prev->next = entry->next; + entry->next = entry; +} + +#define list_for_each_safe(pos, n, head) \ + for (n = (head), pos = (head)->next; pos != (head); \ + n = pos, pos = n->next) + +#undef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#define container_of(ptr, type, member) ({ \ + const __typeof__( ((type *)0)->member ) *__mptr = (ptr);\ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#ifdef DEBUG +#define pynl_dbg(fmt, ...) \ + fprintf(stderr, "%s: " fmt, __func__, __VA_ARGS__) +#else +#define pynl_dbg(fmt, ...) +#endif diff --git a/libnl/python/setup.py.in b/libnl/python/setup.py.in new file mode 100644 index 0000000..83ffdb0 --- /dev/null +++ b/libnl/python/setup.py.in @@ -0,0 +1,55 @@ +#!/usr/bin/env python + +from distutils.core import setup, Extension + +opts = ["-O", "-nodefaultctor"] +include = ["@top_builddir@/include", "@top_srcdir@/include"] +library_dirs = ["@top_builddir@/lib/.libs"] + +netlink_capi = Extension( + "netlink/_capi", + sources=["@srcdir@/netlink/capi.i"], + include_dirs=include, + swig_opts=opts, + library_dirs=library_dirs, + libraries=["nl-3"], +) + +route_capi = Extension( + "netlink/route/_capi", + sources=["@srcdir@/netlink/route/capi.i"], + include_dirs=include, + swig_opts=opts, + library_dirs=library_dirs, + libraries=["nl-3", "nl-route-3"], +) + +genl_capi = Extension( + "netlink/genl/_capi", + sources=["@srcdir@/netlink/genl/capi.i"], + include_dirs=include, + swig_opts=opts, + library_dirs=library_dirs, + libraries=["nl-3", "nl-genl-3"], +) + +setup( + name="netlink", + version="1.0", + description="Python wrapper for netlink protocols", + author="Thomas Graf", + author_email="tgraf@suug.ch", + url="http://www.infradead.org/~tgr/libnl/", + license="LGPL 2", + platforms="linux2", + long_description="Experimental python bindings for libnl", + ext_modules=[netlink_capi, route_capi, genl_capi], + package_dir={"": "@srcdir@"}, + packages=[ + "netlink", + "netlink.genl", + "netlink.route", + "netlink.route.links", + "netlink.route.qdisc", + ], +) diff --git a/libnl/python/tests/test-create-bridge.py b/libnl/python/tests/test-create-bridge.py new file mode 100644 index 0000000..bfb1459 --- /dev/null +++ b/libnl/python/tests/test-create-bridge.py @@ -0,0 +1,29 @@ +from __future__ import print_function +import netlink.core as netlink +import netlink.route.capi as capi +import netlink.route.link as link + +sock = netlink.lookup_socket(netlink.NETLINK_ROUTE) + +cache = link.LinkCache() +cache.refill(sock) + +testtap1 = cache["testtap1"] +print(testtap1) + +lbr = link.Link() +lbr.type = "bridge" +lbr.name = "testbridge" +print(lbr) +lbr.add() + +cache.refill(sock) +lbr = cache["testbridge"] +print(lbr) + +lbr.enslave(testtap1) +cache.refill(sock) +testtap1 = cache["testtap1"] + +print(capi.rtnl_link_is_bridge(lbr._rtnl_link)) +print(capi.rtnl_link_get_master(testtap1._rtnl_link)) diff --git a/libnl/src/genl-ctrl-list.c b/libnl/src/genl-ctrl-list.c new file mode 100644 index 0000000..035b3e2 --- /dev/null +++ b/libnl/src/genl-ctrl-list.c @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include + +static struct nl_cache *alloc_genl_family_cache(struct nl_sock *sk) +{ + return nl_cli_alloc_cache(sk, "generic netlink family", + genl_ctrl_alloc_cache); +} + +static void print_usage(void) +{ + printf( + "Usage: genl-ctrl-list [--details]\n" + "\n" + "Options\n" + " -d, --details Include detailed information in the list\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nl_cache *family_cache; + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_GENERIC); + family_cache = alloc_genl_family_cache(sock); + + for (;;) { + int c, optidx = 0; + static struct option long_opts[] = { + { "details", 0, 0, 'd' }, + { "format", 1, 0, 'f' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "df:hv", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break; + case 'd': params.dp_type = NL_DUMP_DETAILS; break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + } + } + + nl_cache_dump(family_cache, ¶ms); + + return 0; +} diff --git a/libnl/src/idiag-socket-details.c b/libnl/src/idiag-socket-details.c new file mode 100644 index 0000000..9f2da41 --- /dev/null +++ b/libnl/src/idiag-socket-details.c @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Sassano Systems LLC + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +static void print_usage(void) +{ + printf( +"Usage: idiag-socket-details [OPTION]\n" +"\n" +"Options\n" +" --summary Show socket detail summary.\n" +" --details Show socket details on multiple lines.\n" +" --stats Show full socket statistics.\n" +" -h, --help Show this help.\n" +" -v, --version Show versioning information.\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nl_cache *idiag_cache; + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_nl_cb = NULL, + .dp_fd = stdout, + }; + int err = 0; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_INET_DIAG); + for (;;) { + int c, optidx = 0; + enum { + ARG_SUMMARY = 257, + ARG_DETAILS = 258, + ARG_STATS = 259, + ARG_FAMILY, + }; + static struct option long_opts[] = { + { "details", 0, 0, ARG_DETAILS }, + { "summary", 0, 0, ARG_SUMMARY }, + { "stats", 0, 0, ARG_STATS}, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "hv", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case '?': exit(NLE_INVAL); + case ARG_SUMMARY: params.dp_type = NL_DUMP_LINE; break; + case ARG_DETAILS: params.dp_type = NL_DUMP_DETAILS; break; + case ARG_STATS: params.dp_type = NL_DUMP_STATS; break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + } + } + + if ((err = idiagnl_msg_alloc_cache(sock, AF_INET, IDIAGNL_SS_ALL, + &idiag_cache))) { + nl_cli_fatal(err, "Unable to allocate idiag msg cache: %s", + nl_geterror(err)); + } + + nl_cache_mngt_provide(idiag_cache); + + nl_cache_dump_filter(idiag_cache, ¶ms, NULL); + + nl_cache_mngt_unprovide(idiag_cache); + nl_cache_free(idiag_cache); + nl_socket_free(sock); + + return 0; +} diff --git a/libnl/src/lib/addr.c b/libnl/src/lib/addr.c new file mode 100644 index 0000000..7b12f36 --- /dev/null +++ b/libnl/src/lib/addr.c @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2009 Thomas Graf + */ + +/** + * @ingroup cli + * @defgroup cli_addr Addresses + * + * @{ + */ + +#include "nl-default.h" + +#include +#include + +struct rtnl_addr *nl_cli_addr_alloc(void) +{ + struct rtnl_addr *addr; + + addr = rtnl_addr_alloc(); + if (!addr) + nl_cli_fatal(ENOMEM, "Unable to allocate address object"); + + return addr; +} + +void nl_cli_addr_parse_family(struct rtnl_addr *addr, char *arg) +{ + int family; + + if ((family = nl_str2af(arg)) != AF_UNSPEC) + rtnl_addr_set_family(addr, family); +} + +void nl_cli_addr_parse_local(struct rtnl_addr *addr, char *arg) +{ + struct nl_addr *a; + int err; + + a = nl_cli_addr_parse(arg, rtnl_addr_get_family(addr)); + if ((err = rtnl_addr_set_local(addr, a)) < 0) + nl_cli_fatal(err, "Unable to set local address: %s", + nl_geterror(err)); + + nl_addr_put(a); +} + +void nl_cli_addr_parse_dev(struct rtnl_addr *addr, struct nl_cache *link_cache, + char *arg) +{ + int ival; + + if (!(ival = rtnl_link_name2i(link_cache, arg))) + nl_cli_fatal(ENOENT, "Link \"%s\" does not exist", arg); + + rtnl_addr_set_ifindex(addr, ival); +} + +void nl_cli_addr_parse_label(struct rtnl_addr *addr, char *arg) +{ + int err; + + if ((err = rtnl_addr_set_label(addr, arg)) < 0) + nl_cli_fatal(err, "Unable to set address label: %s", + nl_geterror(err)); +} + +void nl_cli_addr_parse_peer(struct rtnl_addr *addr, char *arg) +{ + struct nl_addr *a; + int err; + + a = nl_cli_addr_parse(arg, rtnl_addr_get_family(addr)); + if ((err = rtnl_addr_set_peer(addr, a)) < 0) + nl_cli_fatal(err, "Unable to set peer address: %s", + nl_geterror(err)); + + nl_addr_put(a); +} + +void nl_cli_addr_parse_scope(struct rtnl_addr *addr, char *arg) +{ + int ival; + + if ((ival = rtnl_str2scope(arg)) < 0) + nl_cli_fatal(EINVAL, "Unknown address scope \"%s\"", arg); + + rtnl_addr_set_scope(addr, ival); +} + +void nl_cli_addr_parse_broadcast(struct rtnl_addr *addr, char *arg) +{ + struct nl_addr *a; + int err; + + a = nl_cli_addr_parse(arg, rtnl_addr_get_family(addr)); + if ((err = rtnl_addr_set_broadcast(addr, a)) < 0) + nl_cli_fatal(err, "Unable to set broadcast address: %s", + nl_geterror(err)); + + nl_addr_put(a); +} + +static uint32_t parse_lifetime(const char *arg) +{ + uint64_t msecs; + int err; + + if (!strcasecmp(arg, "forever")) + return 0xFFFFFFFFU; + + if ((err = nl_str2msec(arg, &msecs)) < 0) + nl_cli_fatal(err, "Unable to parse time string \"%s\": %s", + arg, nl_geterror(err)); + + return (msecs / 1000); +} + +void nl_cli_addr_parse_preferred(struct rtnl_addr *addr, char *arg) +{ + rtnl_addr_set_preferred_lifetime(addr, parse_lifetime(arg)); +} + +void nl_cli_addr_parse_valid(struct rtnl_addr *addr, char *arg) +{ + rtnl_addr_set_valid_lifetime(addr, parse_lifetime(arg)); +} + +/** @} */ diff --git a/libnl/src/lib/class.c b/libnl/src/lib/class.c new file mode 100644 index 0000000..9373b07 --- /dev/null +++ b/libnl/src/lib/class.c @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2011 Thomas Graf + */ + +/** + * @ingroup cli + * @defgroup cli_class Traffic Classes + * @{ + */ + +#include "nl-default.h" + +#include +#include + +struct rtnl_class *nl_cli_class_alloc(void) +{ + struct rtnl_class *class; + + if (!(class = rtnl_class_alloc())) + nl_cli_fatal(ENOMEM, "Unable to allocate class object"); + + return class; +} + +struct nl_cache *nl_cli_class_alloc_cache(struct nl_sock *sock, int ifindex) +{ + struct nl_cache *cache; + int err; + + if ((err = rtnl_class_alloc_cache(sock, ifindex, &cache)) < 0) + nl_cli_fatal(err, "Unable to allocate class cache: %s", + nl_geterror(err)); + + nl_cache_mngt_provide(cache); + + return cache; +} + +/** @} */ diff --git a/libnl/src/lib/cls.c b/libnl/src/lib/cls.c new file mode 100644 index 0000000..9c94b0d --- /dev/null +++ b/libnl/src/lib/cls.c @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2011 Thomas Graf + */ + +/** + * @ingroup cli + * @defgroup cli_cls Classifiers + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include + +struct rtnl_cls *nl_cli_cls_alloc(void) +{ + struct rtnl_cls *cls; + + if (!(cls = rtnl_cls_alloc())) + nl_cli_fatal(ENOMEM, "Unable to allocate classifier object"); + + return cls; +} + +struct nl_cache *nl_cli_cls_alloc_cache(struct nl_sock *sock, int ifindex, + uint32_t parent) +{ + struct nl_cache *cache; + int err; + + if ((err = rtnl_cls_alloc_cache(sock, ifindex, parent, &cache)) < 0) + nl_cli_fatal(err, "Unable to allocate classifier cache: %s", + nl_geterror(err)); + + return cache; +} + +void nl_cli_cls_parse_proto(struct rtnl_cls *cls, char *arg) +{ + int proto; + + if ((proto = nl_str2ether_proto(arg)) < 0) + nl_cli_fatal(proto, "Unknown protocol \"%s\".", arg); + + rtnl_cls_set_protocol(cls, proto); +} + +struct rtnl_ematch_tree *nl_cli_cls_parse_ematch(struct rtnl_cls *cls, char *arg) +{ + struct rtnl_ematch_tree *tree; + char *errstr = NULL; + int err; + + if ((err = rtnl_ematch_parse_expr(arg, &errstr, &tree)) < 0) + nl_cli_fatal(err, "Unable to parse ematch expression: %s", + errstr); + + if (errstr) + free(errstr); + + return tree; +} + +/** @} */ diff --git a/libnl/src/lib/ct.c b/libnl/src/lib/ct.c new file mode 100644 index 0000000..959a850 --- /dev/null +++ b/libnl/src/lib/ct.c @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2009 Thomas Graf + */ + +/** + * @ingroup cli + * @defgroup cli_ct Connection Tracking + * + * @{ + */ + +#include "nl-default.h" + +#include +#include + +struct nfnl_ct *nl_cli_ct_alloc(void) +{ + struct nfnl_ct *ct; + + ct = nfnl_ct_alloc(); + if (!ct) + nl_cli_fatal(ENOMEM, "Unable to allocate conntrack object"); + + return ct; +} + +struct nl_cache *nl_cli_ct_alloc_cache(struct nl_sock *sk) +{ + return nl_cli_alloc_cache(sk, "conntrack", nfnl_ct_alloc_cache); +} + +void nl_cli_ct_parse_family(struct nfnl_ct *ct, char *arg) +{ + int family; + + if ((family = nl_str2af(arg)) == AF_UNSPEC) + nl_cli_fatal(EINVAL, + "Unable to nl_cli_ct_parse family \"%s\": %s", + arg, nl_geterror(NLE_INVAL)); + + nfnl_ct_set_family(ct, family); +} + +void nl_cli_ct_parse_protocol(struct nfnl_ct *ct, char *arg) +{ + int proto; + + if ((proto = nl_str2ip_proto(arg)) < 0) + nl_cli_fatal(proto, + "Unable to nl_cli_ct_parse protocol \"%s\": %s", + arg, nl_geterror(proto)); + + nfnl_ct_set_proto(ct, proto); +} + +void nl_cli_ct_parse_mark(struct nfnl_ct *ct, char *arg) +{ + uint32_t mark = nl_cli_parse_u32(arg); + nfnl_ct_set_mark(ct, mark); +} + +void nl_cli_ct_parse_timeout(struct nfnl_ct *ct, char *arg) +{ + uint32_t timeout = nl_cli_parse_u32(arg); + nfnl_ct_set_timeout(ct, timeout); +} + +void nl_cli_ct_parse_id(struct nfnl_ct *ct, char *arg) +{ + uint32_t id = nl_cli_parse_u32(arg); + nfnl_ct_set_id(ct, id); +} + +void nl_cli_ct_parse_use(struct nfnl_ct *ct, char *arg) +{ + uint32_t use = nl_cli_parse_u32(arg); + nfnl_ct_set_use(ct, use); +} + +void nl_cli_ct_parse_src(struct nfnl_ct *ct, int reply, char *arg) +{ + int err; + struct nl_addr *a = nl_cli_addr_parse(arg, nfnl_ct_get_family(ct)); + if ((err = nfnl_ct_set_src(ct, reply, a)) < 0) + nl_cli_fatal(err, "Unable to set source address: %s", + nl_geterror(err)); +} + +void nl_cli_ct_parse_dst(struct nfnl_ct *ct, int reply, char *arg) +{ + int err; + struct nl_addr *a = nl_cli_addr_parse(arg, nfnl_ct_get_family(ct)); + if ((err = nfnl_ct_set_dst(ct, reply, a)) < 0) + nl_cli_fatal(err, "Unable to set destination address: %s", + nl_geterror(err)); +} + +void nl_cli_ct_parse_src_port(struct nfnl_ct *ct, int reply, char *arg) +{ + uint32_t port = nl_cli_parse_u32(arg); + nfnl_ct_set_src_port(ct, reply, port); +} + +void nl_cli_ct_parse_dst_port(struct nfnl_ct *ct, int reply, char *arg) +{ + uint32_t port = nl_cli_parse_u32(arg); + nfnl_ct_set_dst_port(ct, reply, port); +} + +void nl_cli_ct_parse_tcp_state(struct nfnl_ct *ct, char *arg) +{ + int state; + + if ((state = nfnl_ct_str2tcp_state(arg)) < 0) + nl_cli_fatal(state, + "Unable to nl_cli_ct_parse tcp state \"%s\": %s", + arg, nl_geterror(state)); + + nfnl_ct_set_tcp_state(ct, state); +} + +void nl_cli_ct_parse_status(struct nfnl_ct *ct, char *arg) +{ + int status; + + if ((status = nfnl_ct_str2status(arg)) < 0) + nl_cli_fatal(status, + "Unable to nl_cli_ct_parse flags \"%s\": %s", + arg, nl_geterror(status)); + + nfnl_ct_set_status(ct, status); +} + +void nl_cli_ct_parse_zone(struct nfnl_ct *ct, char *arg) +{ + uint32_t zone = nl_cli_parse_u32(arg); + nfnl_ct_set_zone(ct, zone); +} + +#if 0 + } else if (arg_match("origicmpid")) { + if (argc > ++idx) + nfnl_ct_set_icmp_id(ct, 0, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("origicmptype")) { + if (argc > ++idx) + nfnl_ct_set_icmp_type(ct, 0, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("origicmpcode")) { + if (argc > ++idx) + nfnl_ct_set_icmp_code(ct, 0, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("replyicmpid")) { + if (argc > ++idx) + nfnl_ct_set_icmp_id(ct, 1, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("replyicmptype")) { + if (argc > ++idx) + nfnl_ct_set_icmp_type(ct, 1, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("replyicmpcode")) { + if (argc > ++idx) + nfnl_ct_set_icmp_code(ct, 1, strtoul(argv[idx++], NULL, 0)); + } +#endif + +/** @} */ diff --git a/libnl/src/lib/exp.c b/libnl/src/lib/exp.c new file mode 100644 index 0000000..dcda30c --- /dev/null +++ b/libnl/src/lib/exp.c @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2009 Thomas Graf + * Copyright (c) 2012 Rich Fought + */ + +/** + * @ingroup cli + * @defgroup cli_exp Expectation Tracking + * + * @{ + */ + +#include "nl-default.h" + +#include +#include + +struct nfnl_exp *nl_cli_exp_alloc(void) +{ + struct nfnl_exp *exp; + + exp = nfnl_exp_alloc(); + if (!exp) + nl_cli_fatal(ENOMEM, "Unable to allocate expectation object"); + + return exp; +} + +struct nl_cache *nl_cli_exp_alloc_cache(struct nl_sock *sk) +{ + return nl_cli_alloc_cache(sk, "expectation", nfnl_exp_alloc_cache); +} + +void nl_cli_exp_parse_family(struct nfnl_exp *exp, char *arg) +{ + int family; + + if ((family = nl_str2af(arg)) == AF_UNSPEC) + nl_cli_fatal(EINVAL, + "Unable to nl_cli_exp_parse family \"%s\": %s", + arg, nl_geterror(NLE_INVAL)); + + nfnl_exp_set_family(exp, family); +} + +void nl_cli_exp_parse_timeout(struct nfnl_exp *exp, char *arg) +{ + uint32_t timeout = nl_cli_parse_u32(arg); + nfnl_exp_set_timeout(exp, timeout); +} + +void nl_cli_exp_parse_id(struct nfnl_exp *exp, char *arg) +{ + uint32_t id = nl_cli_parse_u32(arg); + nfnl_exp_set_id(exp, id); +} + +void nl_cli_exp_parse_helper_name(struct nfnl_exp *exp, char *arg) +{ + nfnl_exp_set_helper_name(exp, arg); +} + +void nl_cli_exp_parse_zone(struct nfnl_exp *exp, char *arg) +{ + uint32_t zone = nl_cli_parse_u32(arg); + nfnl_exp_set_zone(exp, zone); +} + +void nl_cli_exp_parse_flags(struct nfnl_exp *exp, char *arg) +{ + uint32_t flags = nl_cli_parse_u32(arg); + nfnl_exp_set_flags(exp, flags); +} + +void nl_cli_exp_parse_class(struct nfnl_exp *exp, char *arg) +{ + uint32_t class = nl_cli_parse_u32(arg); + nfnl_exp_set_class(exp, class); +} + +void nl_cli_exp_parse_nat_dir(struct nfnl_exp *exp, char *arg) +{ + uint32_t nat_dir = nl_cli_parse_u32(arg); + nfnl_exp_set_nat_dir(exp, nat_dir); +} + +void nl_cli_exp_parse_fn(struct nfnl_exp *exp, char *arg) +{ + nfnl_exp_set_fn(exp, arg); +} + +void nl_cli_exp_parse_src(struct nfnl_exp *exp, int tuple, char *arg) +{ + int err; + struct nl_addr *a = nl_cli_addr_parse(arg, nfnl_exp_get_family(exp)); + if ((err = nfnl_exp_set_src(exp, tuple, a)) < 0) + nl_cli_fatal(err, "Unable to set source address: %s", + nl_geterror(err)); +} + +void nl_cli_exp_parse_dst(struct nfnl_exp *exp, int tuple, char *arg) +{ + int err; + struct nl_addr *a = nl_cli_addr_parse(arg, nfnl_exp_get_family(exp)); + if ((err = nfnl_exp_set_dst(exp, tuple, a)) < 0) + nl_cli_fatal(err, "Unable to set destination address: %s", + nl_geterror(err)); +} + +void nl_cli_exp_parse_l4protonum(struct nfnl_exp *exp, int tuple, char *arg) +{ + int l4protonum; + + if ((l4protonum = nl_str2ip_proto(arg)) < 0) + nl_cli_fatal(l4protonum, + "Unable to nl_cli_exp_parse protocol \"%s\": %s", + arg, nl_geterror(l4protonum)); + + nfnl_exp_set_l4protonum(exp, tuple, l4protonum); +} + +void nl_cli_exp_parse_src_port(struct nfnl_exp *exp, int tuple, char *arg) +{ + uint32_t sport = nl_cli_parse_u32(arg); + uint16_t dport = nfnl_exp_get_dst_port(exp, tuple); + nfnl_exp_set_ports(exp, tuple, sport, dport); +} + +void nl_cli_exp_parse_dst_port(struct nfnl_exp *exp, int tuple, char *arg) +{ + uint32_t dport = nl_cli_parse_u32(arg); + uint16_t sport = nfnl_exp_get_src_port(exp, tuple); + nfnl_exp_set_ports(exp, tuple, sport, dport); +} + +void nl_cli_exp_parse_icmp_id(struct nfnl_exp *exp, int tuple, char *arg) +{ + uint32_t id = nl_cli_parse_u32(arg); + uint8_t type = nfnl_exp_get_icmp_type(exp, tuple); + uint8_t code = nfnl_exp_get_icmp_code(exp, tuple); + nfnl_exp_set_icmp(exp, tuple, id, type, code); +} + +void nl_cli_exp_parse_icmp_type(struct nfnl_exp *exp, int tuple, char *arg) +{ + uint32_t type = nl_cli_parse_u32(arg); + uint16_t id = nfnl_exp_get_icmp_id(exp, tuple); + uint8_t code = nfnl_exp_get_icmp_code(exp, tuple); + nfnl_exp_set_icmp(exp, tuple, id, type, code); +} + +void nl_cli_exp_parse_icmp_code(struct nfnl_exp *exp, int tuple, char *arg) +{ + uint32_t code = nl_cli_parse_u32(arg); + uint16_t id = nfnl_exp_get_icmp_id(exp, tuple); + uint8_t type = nfnl_exp_get_icmp_type(exp, tuple); + nfnl_exp_set_icmp(exp, tuple, id, type, code); +} + +/** @} */ diff --git a/libnl/src/lib/link.c b/libnl/src/lib/link.c new file mode 100644 index 0000000..67580b4 --- /dev/null +++ b/libnl/src/lib/link.c @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2010 Thomas Graf + */ + +/** + * @ingroup cli + * @defgroup cli_link Links + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include + +struct rtnl_link *nl_cli_link_alloc(void) +{ + struct rtnl_link *link; + + link = rtnl_link_alloc(); + if (!link) + nl_cli_fatal(ENOMEM, "Unable to allocate link object"); + + return link; +} + +struct nl_cache *nl_cli_link_alloc_cache_family_flags(struct nl_sock *sock, + int family, + unsigned int flags) +{ + struct nl_cache *cache; + int err; + + if ((err = rtnl_link_alloc_cache_flags(sock, family, &cache, flags)) < 0) + nl_cli_fatal(err, "Unable to allocate link cache: %s", + nl_geterror(err)); + + nl_cache_mngt_provide(cache); + + return cache; +} + +struct nl_cache *nl_cli_link_alloc_cache_family(struct nl_sock *sock, int family) +{ + return nl_cli_link_alloc_cache_family_flags(sock, family, 0); +} + +struct nl_cache *nl_cli_link_alloc_cache(struct nl_sock *sock) +{ + return nl_cli_link_alloc_cache_family(sock, AF_UNSPEC); +} + +struct nl_cache *nl_cli_link_alloc_cache_flags(struct nl_sock *sock, + unsigned int flags) +{ + return nl_cli_link_alloc_cache_family_flags(sock, AF_UNSPEC, flags); +} + +void nl_cli_link_parse_family(struct rtnl_link *link, char *arg) +{ + int family; + + if ((family = nl_str2af(arg)) < 0) + nl_cli_fatal(EINVAL, + "Unable to translate address family \"%s\"", arg); + + rtnl_link_set_family(link, family); +} + +void nl_cli_link_parse_name(struct rtnl_link *link, char *arg) +{ + rtnl_link_set_name(link, arg); +} + +void nl_cli_link_parse_mtu(struct rtnl_link *link, char *arg) +{ + uint32_t mtu = nl_cli_parse_u32(arg); + rtnl_link_set_mtu(link, mtu); +} + +void nl_cli_link_parse_ifindex(struct rtnl_link *link, char *arg) +{ + uint32_t index = nl_cli_parse_u32(arg); + rtnl_link_set_ifindex(link, index); +} + +void nl_cli_link_parse_txqlen(struct rtnl_link *link, char *arg) +{ + uint32_t qlen = nl_cli_parse_u32(arg); + rtnl_link_set_txqlen(link, qlen); +} + +void nl_cli_link_parse_weight(struct rtnl_link *link, char *arg) +{ +} + +void nl_cli_link_parse_ifalias(struct rtnl_link *link, char *arg) +{ + if (strlen(arg) > IFALIASZ) + nl_cli_fatal(ERANGE, + "Link ifalias too big, must not exceed %u in length.", + IFALIASZ); + + rtnl_link_set_ifalias(link, arg); +} + +/** @} */ diff --git a/libnl/src/lib/neigh.c b/libnl/src/lib/neigh.c new file mode 100644 index 0000000..a4b211e --- /dev/null +++ b/libnl/src/lib/neigh.c @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2009 Thomas Graf + */ + +/** + * @ingroup cli + * @defgroup cli_neigh Neighbour + * + * @{ + */ + +#include "nl-default.h" + +#include +#include + +struct rtnl_neigh *nl_cli_neigh_alloc(void) +{ + struct rtnl_neigh *neigh; + + neigh = rtnl_neigh_alloc(); + if (!neigh) + nl_cli_fatal(ENOMEM, "Unable to allocate neighbour object"); + + return neigh; +} + +void nl_cli_neigh_parse_dst(struct rtnl_neigh *neigh, char *arg) +{ + struct nl_addr *a; + int err; + + a = nl_cli_addr_parse(arg, rtnl_neigh_get_family(neigh)); + if ((err = rtnl_neigh_set_dst(neigh, a)) < 0) + nl_cli_fatal(err, "Unable to set local address: %s", + nl_geterror(err)); + + nl_addr_put(a); +} + +void nl_cli_neigh_parse_lladdr(struct rtnl_neigh *neigh, char *arg) +{ + struct nl_addr *a; + + a = nl_cli_addr_parse(arg, AF_UNSPEC); + rtnl_neigh_set_lladdr(neigh, a); + nl_addr_put(a); +} + +void nl_cli_neigh_parse_dev(struct rtnl_neigh *neigh, + struct nl_cache *link_cache, char *arg) +{ + int ival; + + if (!(ival = rtnl_link_name2i(link_cache, arg))) + nl_cli_fatal(ENOENT, "Link \"%s\" does not exist", arg); + + rtnl_neigh_set_ifindex(neigh, ival); +} + +void nl_cli_neigh_parse_family(struct rtnl_neigh *neigh, char *arg) +{ + int family; + + if ((family = nl_str2af(arg)) == AF_UNSPEC) + nl_cli_fatal(EINVAL, + "Unable to translate address family \"%s\"", arg); + + rtnl_neigh_set_family(neigh, family); +} + +void nl_cli_neigh_parse_state(struct rtnl_neigh *neigh, char *arg) +{ + int state; + + if ((state = rtnl_neigh_str2state(arg)) < 0) + nl_cli_fatal(state, "Unable to translate state \"%s\": %s", + arg, state); + + rtnl_neigh_set_state(neigh, state); +} + +/** @} */ diff --git a/libnl/src/lib/nh.c b/libnl/src/lib/nh.c new file mode 100644 index 0000000..c1f5137 --- /dev/null +++ b/libnl/src/lib/nh.c @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2022 Stanislav Zaikin + */ + +/** + * @ingroup cli + * @defgroup cli_nh nhs + * + * @{ + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +struct rtnl_nh *nl_cli_nh_alloc(void) +{ + struct rtnl_nh *nh; + + nh = rtnl_nh_alloc(); + if (!nh) + nl_cli_fatal(ENOMEM, "Unable to allocate nh object"); + + return nh; +} + +struct nl_cache *nl_cli_nh_alloc_cache_family_flags(struct nl_sock *sock, + int family, + unsigned int flags) +{ + struct nl_cache *cache; + int err; + + if ((err = rtnl_nh_alloc_cache(sock, family, &cache)) < 0) + nl_cli_fatal(err, "Unable to allocate nh cache: %s", + nl_geterror(err)); + + nl_cache_mngt_provide(cache); + + return cache; +} + +struct nl_cache *nl_cli_nh_alloc_cache_family(struct nl_sock *sock, int family) +{ + return nl_cli_nh_alloc_cache_family_flags(sock, family, 0); +} + +struct nl_cache *nl_cli_nh_alloc_cache(struct nl_sock *sock) +{ + return nl_cli_nh_alloc_cache_family(sock, AF_UNSPEC); +} + +struct nl_cache *nl_cli_nh_alloc_cache_flags(struct nl_sock *sock, + unsigned int flags) +{ + return nl_cli_nh_alloc_cache_family_flags(sock, AF_UNSPEC, flags); +} + +/** @} */ diff --git a/libnl/src/lib/qdisc.c b/libnl/src/lib/qdisc.c new file mode 100644 index 0000000..659594a --- /dev/null +++ b/libnl/src/lib/qdisc.c @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2011 Thomas Graf + */ + +/** + * @ingroup cli + * @defgroup cli_qdisc Queueing Disciplines + * @{ + */ + +#include "nl-default.h" + +#include +#include +#include + +struct rtnl_qdisc *nl_cli_qdisc_alloc(void) +{ + struct rtnl_qdisc *qdisc; + + if (!(qdisc = rtnl_qdisc_alloc())) + nl_cli_fatal(ENOMEM, "Unable to allocate qdisc object"); + + return qdisc; +} + +/** @} */ diff --git a/libnl/src/lib/route.c b/libnl/src/lib/route.c new file mode 100644 index 0000000..d814950 --- /dev/null +++ b/libnl/src/lib/route.c @@ -0,0 +1,288 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2009 Thomas Graf + */ + +/** + * @ingroup cli + * @defgroup cli_route Routing + * + * @{ + */ + +#include "nl-default.h" + +#include +#include + +struct rtnl_route *nl_cli_route_alloc(void) +{ + struct rtnl_route *route; + + route = rtnl_route_alloc(); + if (!route) + nl_cli_fatal(ENOMEM, "Unable to allocate route object"); + + return route; +} + +struct nl_cache *nl_cli_route_alloc_cache(struct nl_sock *sk, int flags) +{ + struct nl_cache *cache; + int err; + + if ((err = rtnl_route_alloc_cache(sk, AF_UNSPEC, flags, &cache)) < 0) + nl_cli_fatal(err, "Unable to allocate route cache: %s\n", + nl_geterror(err)); + + nl_cache_mngt_provide(cache); + + return cache; +} + +void nl_cli_route_parse_family(struct rtnl_route *route, char *arg) +{ + int family; + + if ((family = nl_str2af(arg)) != AF_UNSPEC) + rtnl_route_set_family(route, family); +} + +void nl_cli_route_parse_dst(struct rtnl_route *route, char *arg) +{ + struct nl_addr *addr; + int err; + + addr = nl_cli_addr_parse(arg, rtnl_route_get_family(route)); + if ((err = rtnl_route_set_dst(route, addr)) < 0) + nl_cli_fatal(err, "Unable to set destination address: %s", + nl_geterror(err)); + + nl_addr_put(addr); +} + +void nl_cli_route_parse_src(struct rtnl_route *route, char *arg) +{ + struct nl_addr *addr; + int err; + + addr = nl_cli_addr_parse(arg, rtnl_route_get_family(route)); + if ((err = rtnl_route_set_src(route, addr)) < 0) + nl_cli_fatal(err, "Unable to set source address: %s", + nl_geterror(err)); + + nl_addr_put(addr); +} + +void nl_cli_route_parse_pref_src(struct rtnl_route *route, char *arg) +{ + struct nl_addr *addr; + int err; + + addr = nl_cli_addr_parse(arg, rtnl_route_get_family(route)); + if ((err = rtnl_route_set_pref_src(route, addr)) < 0) + nl_cli_fatal(err, "Unable to set preferred source address: %s", + nl_geterror(err)); + + nl_addr_put(addr); +} + +void nl_cli_route_parse_metric(struct rtnl_route *route, char *subopts) +{ + /* strict equal order to RTAX_* */ + static char *const tokens[] = { + "unspec", + "lock", + "mtu", + "window", + "rtt", + "rttvar", + "sstresh", + "cwnd", + "advmss", + "reordering", + "hoplimit", + "initcwnd", + "features", + NULL, + }; + unsigned long lval; + char *arg, *endptr; + + while (*subopts != '\0') { + int ret = getsubopt(&subopts, tokens, &arg); + if (ret == -1) + nl_cli_fatal(EINVAL, "Unknown metric token \"%s\"", arg); + + if (ret == 0) + nl_cli_fatal(EINVAL, "Invalid metric \"%s\"", tokens[ret]); + + if (arg == NULL) + nl_cli_fatal(EINVAL, "Metric \"%s\", no value given", tokens[ret]); + + lval = strtoul(arg, &endptr, 0); + if (endptr == arg) + nl_cli_fatal(EINVAL, "Metric \"%s\", value not numeric", tokens[ret]); + + if ((ret = rtnl_route_set_metric(route, ret, lval)) < 0) + nl_cli_fatal(ret, "Unable to set metric: %s", + nl_geterror(ret)); + } +} + +void nl_cli_route_parse_nexthop(struct rtnl_route *route, char *subopts, + struct nl_cache *link_cache) +{ + enum { + NH_DEV, + NH_VIA, + NH_WEIGHT, + NH_AS, + }; + static char *const tokens[] = { + "dev", + "via", + "weight", + "as", + NULL, + }; + struct rtnl_nexthop *nh; + unsigned long lval; + struct nl_addr *addr; + int ival; + char *arg, *endptr; + + if (!(nh = rtnl_route_nh_alloc())) + nl_cli_fatal(ENOMEM, "Out of memory"); + + while (*subopts != '\0') { + int ret = getsubopt(&subopts, tokens, &arg); + if (ret == -1) + nl_cli_fatal(EINVAL, "Unknown nexthop token \"%s\"", arg); + + if (arg == NULL) + nl_cli_fatal(EINVAL, "Missing argument to option \"%s\"\n", + tokens[ret]); + + switch (ret) { + case NH_DEV: + if (!(ival = rtnl_link_name2i(link_cache, arg))) + nl_cli_fatal(ENOENT,"Link \"%s\" does not exist", arg); + + rtnl_route_nh_set_ifindex(nh, ival); + break; + + case NH_VIA: + if (rtnl_route_get_family(route) == AF_MPLS) { + addr = nl_cli_addr_parse(arg, 0); + rtnl_route_nh_set_via(nh, addr); + } else { + addr = nl_cli_addr_parse(arg,rtnl_route_get_family(route)); + rtnl_route_nh_set_gateway(nh, addr); + } + nl_addr_put(addr); + break; + + case NH_AS: + addr = nl_cli_addr_parse(arg, + rtnl_route_get_family(route)); + rtnl_route_nh_set_newdst(nh, addr); + nl_addr_put(addr); + break; + + case NH_WEIGHT: + lval = strtoul(arg, &endptr, 0); + if (endptr == arg) + nl_cli_fatal(EINVAL, + "Invalid weight \"%s\", not numeric", + arg); + rtnl_route_nh_set_weight(nh, lval); + break; + } + } + + rtnl_route_add_nexthop(route, nh); +} + +void nl_cli_route_parse_table(struct rtnl_route *route, char *arg) +{ + unsigned long lval; + char *endptr; + int table; + + lval = strtoul(arg, &endptr, 0); + if (endptr == arg) { + if ((table = rtnl_route_str2table(arg)) < 0) + nl_cli_fatal(EINVAL, "Unknown table name \"%s\"", arg); + } + else { + table = lval; + } + + rtnl_route_set_table(route, table); +} + +void nl_cli_route_parse_prio(struct rtnl_route *route, char *arg) +{ + unsigned long lval; + char *endptr; + + lval = strtoul(arg, &endptr, 0); + if (endptr == arg) + nl_cli_fatal(EINVAL, "Invalid priority value, not numeric"); + rtnl_route_set_priority(route, lval); +} + +void nl_cli_route_parse_scope(struct rtnl_route *route, char *arg) +{ + int ival; + + if ((ival = rtnl_str2scope(arg)) < 0) + nl_cli_fatal(EINVAL, "Unknown routing scope \"%s\"", arg); + + rtnl_route_set_scope(route, ival); +} + +void nl_cli_route_parse_protocol(struct rtnl_route *route, char *arg) +{ + unsigned long lval; + char *endptr; + int proto; + + lval = strtoul(arg, &endptr, 0); + if (endptr == arg) { + if ((proto = rtnl_route_str2proto(arg)) < 0) + nl_cli_fatal(EINVAL, + "Unknown routing protocol name \"%s\"", + arg); + } + else { + proto = lval; + } + + rtnl_route_set_protocol(route, proto); +} + +void nl_cli_route_parse_type(struct rtnl_route *route, char *arg) +{ + int ival; + + if ((ival = nl_str2rtntype(arg)) < 0) + nl_cli_fatal(EINVAL, "Unknown routing type \"%s\"", arg); + + if ((ival = rtnl_route_set_type(route, ival)) < 0) + nl_cli_fatal(ival, "Unable to set routing type: %s", + nl_geterror(ival)); +} + +void nl_cli_route_parse_iif(struct rtnl_route *route, char *arg, struct nl_cache *link_cache) +{ + int ival; + + if (!(ival = rtnl_link_name2i(link_cache, arg))) + nl_cli_fatal(ENOENT, "Link \"%s\" does not exist", arg); + + rtnl_route_set_iif(route, ival); +} + +/** @} */ diff --git a/libnl/src/lib/rule.c b/libnl/src/lib/rule.c new file mode 100644 index 0000000..8a54e73 --- /dev/null +++ b/libnl/src/lib/rule.c @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2009 Thomas Graf + */ + +/** + * @ingroup cli + * @defgroup cli_rule Routing Rules + * + * @{ + */ + +#include "nl-default.h" + +#include +#include + +struct rtnl_rule *nl_cli_rule_alloc(void) +{ + struct rtnl_rule *rule; + + rule = rtnl_rule_alloc(); + if (!rule) + nl_cli_fatal(ENOMEM, "Unable to allocate rule object"); + + return rule; +} + +struct nl_cache *nl_cli_rule_alloc_cache(struct nl_sock *sk) +{ + struct nl_cache *cache; + int err; + + if ((err = rtnl_rule_alloc_cache(sk, AF_UNSPEC, &cache)) < 0) + nl_cli_fatal(err, "Unable to allocate routing rule cache: %s\n", + nl_geterror(err)); + + nl_cache_mngt_provide(cache); + + return cache; +} + +void nl_cli_rule_parse_family(struct rtnl_rule *rule, char *arg) +{ + int family; + + if ((family = nl_str2af(arg)) != AF_UNSPEC) + rtnl_rule_set_family(rule, family); +} + +/** @} */ diff --git a/libnl/src/lib/tc.c b/libnl/src/lib/tc.c new file mode 100644 index 0000000..3bed636 --- /dev/null +++ b/libnl/src/lib/tc.c @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#include "nl-default.h" + +#include +#include + +#include "nl-priv-dynamic-route/nl-priv-dynamic-route.h" + +/** + * @ingroup cli + * @defgroup cli_tc Traffic Control + * @{ + */ +void nl_cli_tc_parse_dev(struct rtnl_tc *tc, struct nl_cache *link_cache, char *name) +{ + struct rtnl_link *link; + + link = rtnl_link_get_by_name(link_cache, name); + if (!link) + nl_cli_fatal(ENOENT, "Link \"%s\" does not exist.", name); + + rtnl_tc_set_link(tc, link); + rtnl_link_put(link); +} + +void nl_cli_tc_parse_parent(struct rtnl_tc *tc, char *arg) +{ + uint32_t parent; + int err; + + if ((err = rtnl_tc_str2handle(arg, &parent)) < 0) + nl_cli_fatal(err, "Unable to parse handle \"%s\": %s", + arg, nl_geterror(err)); + + rtnl_tc_set_parent(tc, parent); +} + +void nl_cli_tc_parse_handle(struct rtnl_tc *tc, char *arg, int create) +{ + uint32_t handle, parent; + int err; + + parent = rtnl_tc_get_parent(tc); + + if ((err = rtnl_tc_str2handle(arg, &handle)) < 0) { + if (err == -NLE_OBJ_NOTFOUND && create) + err = rtnl_classid_generate(arg, &handle, parent); + + if (err < 0) + nl_cli_fatal(err, "Unable to parse handle \"%s\": %s", + arg, nl_geterror(err)); + } + + rtnl_tc_set_handle(tc, handle); +} + +void nl_cli_tc_parse_mtu(struct rtnl_tc *tc, char *arg) +{ + rtnl_tc_set_mtu(tc, nl_cli_parse_u32(arg)); +} + +void nl_cli_tc_parse_mpu(struct rtnl_tc *tc, char *arg) +{ + rtnl_tc_set_mpu(tc, nl_cli_parse_u32(arg)); +} + +void nl_cli_tc_parse_overhead(struct rtnl_tc *tc, char *arg) +{ + rtnl_tc_set_overhead(tc, nl_cli_parse_u32(arg)); +} + +void nl_cli_tc_parse_kind(struct rtnl_tc *tc, char *arg) +{ + rtnl_tc_set_kind(tc, arg); +} + +void nl_cli_tc_parse_linktype(struct rtnl_tc *tc, char *arg) +{ + int type; + + if ((type = nl_str2llproto(arg)) < 0) + nl_cli_fatal(type, "Unable to parse linktype \"%s\": %s", + arg, nl_geterror(type)); + + rtnl_tc_set_linktype(tc, type); +} + +static NL_LIST_HEAD(tc_modules); + +static struct nl_cli_tc_module *__nl_cli_tc_lookup(struct rtnl_tc_ops *ops) +{ + struct nl_cli_tc_module *tm; + + nl_list_for_each_entry(tm, &tc_modules, tm_list) + if (tm->tm_ops == ops) + return tm; + + return NULL; +} + +struct nl_cli_tc_module *nl_cli_tc_lookup(struct rtnl_tc_ops *ops) +{ + struct nl_cli_tc_module *tm; + + if ((tm = __nl_cli_tc_lookup(ops))) + return tm; + + switch (ops->to_type) { + case RTNL_TC_TYPE_QDISC: + case RTNL_TC_TYPE_CLASS: + nl_cli_load_module("cli/qdisc", ops->to_kind); + break; + + case RTNL_TC_TYPE_CLS: + nl_cli_load_module("cli/cls", ops->to_kind); + break; + + default: + nl_cli_fatal(EINVAL, "BUG: unhandled TC object type %d", + ops->to_type); + } + + if (!(tm = __nl_cli_tc_lookup(ops))) { + nl_cli_fatal(EINVAL, "Application bug: The shared library for " + "the tc object \"%s\" was successfully loaded but it " + "seems that module did not register itself", + ops->to_kind); + } + + return tm; +} + +void nl_cli_tc_register(struct nl_cli_tc_module *tm) +{ + struct rtnl_tc_ops *ops; + + if (!(ops = rtnl_tc_lookup_ops(tm->tm_type, tm->tm_name))) { + nl_cli_fatal(ENOENT, "Unable to register CLI TC module " + "\"%s\": No matching libnl TC module found.", tm->tm_name); + } + + if (__nl_cli_tc_lookup(ops)) { + nl_cli_fatal(EEXIST, "Unable to register CLI TC module " + "\"%s\": Module already registered.", tm->tm_name); + } + + tm->tm_ops = ops; + + nl_list_add_tail(&tm->tm_list, &tc_modules); +} + +void nl_cli_tc_unregister(struct nl_cli_tc_module *tm) +{ + nl_list_del(&tm->tm_list); +} + + +/** @} */ diff --git a/libnl/src/lib/utils.c b/libnl/src/lib/utils.c new file mode 100644 index 0000000..8afb926 --- /dev/null +++ b/libnl/src/lib/utils.c @@ -0,0 +1,246 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +/** + * @defgroup cli Command Line Interface API + * + * @{ + * + * These modules provide an interface for text based applications. The + * functions provided are wrappers for their libnl equivalent with + * added error handling. The functions check for allocation failures, + * invalid input, and unknown types and will print error messages + * accordingly via nl_cli_fatal(). + */ + +#include "nl-default.h" + +#include +#ifdef HAVE_DLFCN_H +#include +#endif + +#include + +/** + * Parse a text based 32 bit unsigned integer argument + * @arg arg Integer in text form. + * + * Tries to convert the number provided in arg to a uint32_t. Will call + * nl_cli_fatal() if the conversion fails. + * + * @return 32bit unsigned integer. + */ +uint32_t nl_cli_parse_u32(const char *arg) +{ + unsigned long lval; + char *endptr; + + lval = strtoul(arg, &endptr, 0); + if (endptr == arg || lval == ULONG_MAX) + nl_cli_fatal(EINVAL, "Unable to parse \"%s\", not a number.", + arg); + + return (uint32_t) lval; +} + +void nl_cli_print_version(void) +{ + printf("libnl tools version %s\n", LIBNL_VERSION); + printf( + "Copyright (C) 2003-2010 Thomas Graf \n" + "\n" + "This program comes with ABSOLUTELY NO WARRANTY. This is free \n" + "software, and you are welcome to redistribute it under certain\n" + "conditions. See the GNU General Public License for details.\n" + ); + + exit(0); +} + +/** + * Print error message and quit application + * @arg err Error code. + * @arg fmt Error message. + * + * Prints the formatted error message to stderr and quits the application + * using the provided error code. + */ +void nl_cli_fatal(int err, const char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "Error: "); + + if (fmt) { + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + } else { + char *buf; +#ifdef HAVE_STRERROR_L + locale_t loc = newlocale(LC_MESSAGES_MASK, "", (locale_t)0); + if (loc == (locale_t)0) { + if (errno == ENOENT) + loc = newlocale(LC_MESSAGES_MASK, + "POSIX", (locale_t)0); + if (loc == (locale_t)0) + buf = "newlocale() failed"; + } + if (loc != (locale_t)0) + buf = strerror_l(err, loc); +#else + buf = strerror(err); +#endif + fprintf(stderr, "%s\n", buf); +#ifdef HAVE_STRERROR_L + if (loc != (locale_t)0) + freelocale(loc); +#endif + } + + exit(abs(err)); +} + +int nl_cli_connect(struct nl_sock *sk, int protocol) +{ + int err; + + if ((err = nl_connect(sk, protocol)) < 0) + nl_cli_fatal(err, "Unable to connect netlink socket: %s", + nl_geterror(err)); + + return err; +} + +struct nl_sock *nl_cli_alloc_socket(void) +{ + struct nl_sock *sock; + + if (!(sock = nl_socket_alloc())) + nl_cli_fatal(ENOBUFS, "Unable to allocate netlink socket"); + + return sock; +} + +struct nl_addr *nl_cli_addr_parse(const char *str, int family) +{ + struct nl_addr *addr; + int err; + + if ((err = nl_addr_parse(str, family, &addr)) < 0) + nl_cli_fatal(err, "Unable to parse address \"%s\": %s", + str, nl_geterror(err)); + + return addr; +} + +int nl_cli_parse_dumptype(const char *str) +{ + if (!strcasecmp(str, "brief")) + return NL_DUMP_LINE; + else if (!strcasecmp(str, "details") || !strcasecmp(str, "detailed")) + return NL_DUMP_DETAILS; + else if (!strcasecmp(str, "stats")) + return NL_DUMP_STATS; + else + nl_cli_fatal(EINVAL, "Invalid dump type \"%s\".\n", str); + + return 0; +} + +int nl_cli_confirm(struct nl_object *obj, struct nl_dump_params *params, + int default_yes) +{ + nl_object_dump(obj, params); + + for (;;) { + char buf[32] = { 0 }; + int answer; + + printf("Delete? (%c/%c) ", + default_yes ? 'Y' : 'y', + default_yes ? 'n' : 'N'); + + if (!fgets(buf, sizeof(buf), stdin)) { + fprintf(stderr, "Error while reading\n."); + continue; + } + + switch ((answer = tolower(buf[0]))) { + case '\n': + answer = default_yes ? 'y' : 'n'; + /* fall through */ + case 'y': + case 'n': + return answer == 'y'; + } + + fprintf(stderr, "Invalid input, try again.\n"); + } + + return 0; + +} + +struct nl_cache *nl_cli_alloc_cache(struct nl_sock *sock, const char *name, + int (*ac)(struct nl_sock *, struct nl_cache **)) +{ + struct nl_cache *cache; + int err; + + if ((err = ac(sock, &cache)) < 0) + nl_cli_fatal(err, "Unable to allocate %s cache: %s", + name, nl_geterror(err)); + + nl_cache_mngt_provide(cache); + + return cache; +} + +struct nl_cache *nl_cli_alloc_cache_flags(struct nl_sock *sock, + const char *name, unsigned int flags, + int (*ac)(struct nl_sock *, struct nl_cache **, + unsigned int)) +{ + struct nl_cache *cache; + int err; + + if ((err = ac(sock, &cache, flags)) < 0) + nl_cli_fatal(err, "Unable to allocate %s cache: %s", + name, nl_geterror(err)); + + nl_cache_mngt_provide(cache); + + return cache; +} + +void nl_cli_load_module(const char *prefix, const char *name) +{ + char path[FILENAME_MAX+1]; + + snprintf(path, sizeof(path), "%s/%s/%s.so", + _NL_PKGLIBDIR, prefix, name); + +#ifdef HAVE_DLFCN_H + { + void *handle; + + handle = dlopen(path, RTLD_NOW); + if (!handle) { + nl_cli_fatal(ENOENT, "Unable to load module \"%s\": %s\n", + path, dlerror()); + } + /* We intentionally leak the dlopen handle. */ + /* coverity[RESOURCE_LEAK] */ + } +#else + nl_cli_fatal(ENOTSUP, "Unable to load module \"%s\": built without dynamic libraries support\n", + path); +#endif +} + +/** @} */ diff --git a/libnl/src/nf-ct-add.c b/libnl/src/nf-ct-add.c new file mode 100644 index 0000000..cefa837 --- /dev/null +++ b/libnl/src/nf-ct-add.c @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#include "nl-default.h" + +#include + +#include +#include + +static int quiet = 0; + +static void print_usage(void) +{ + printf( + "Usage: nf-ct-add [OPTION]... [CONNTRACK ENTRY]\n" + "\n" + "Options\n" + " -q, --quiet Do not print informal notifications.\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + "\n" + "Conntrack Selection\n" + " -p, --proto=PROTOCOL Protocol\n" + " --orig-src=ADDR Original source address\n" + " --orig-sport=PORT Original source port\n" + " --orig-dst=ADDR Original destination address\n" + " --orig-dport=PORT Original destination port\n" + " --reply-src=ADDR Reply source address\n" + " --reply-sport=PORT Reply source port\n" + " --reply-dst=ADDR Reply destination address\n" + " --reply-dport=PORT Reply destination port\n" + " -F, --family=FAMILY Address family\n" + " --mark=NUM Mark value\n" + " --timeout=NUM Timeout value\n" + " --status Bitset representing status of connection.\n" + " --zone=NUM Zone value\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nfnl_ct *ct; + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + int err, nlflags = NLM_F_CREATE; + + ct = nl_cli_ct_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_ORIG_SRC = 257, + ARG_ORIG_SPORT = 258, + ARG_ORIG_DST, + ARG_ORIG_DPORT, + ARG_REPLY_SRC, + ARG_REPLY_SPORT, + ARG_REPLY_DST, + ARG_REPLY_DPORT, + ARG_MARK, + ARG_TIMEOUT, + ARG_STATUS, + ARG_ZONE, + }; + static struct option long_opts[] = { + { "quiet", 0, 0, 'q' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "proto", 1, 0, 'p' }, + { "orig-src", 1, 0, ARG_ORIG_SRC }, + { "orig-sport", 1, 0, ARG_ORIG_SPORT }, + { "orig-dst", 1, 0, ARG_ORIG_DST }, + { "orig-dport", 1, 0, ARG_ORIG_DPORT }, + { "reply-src", 1, 0, ARG_REPLY_SRC }, + { "reply-sport", 1, 0, ARG_REPLY_SPORT }, + { "reply-dst", 1, 0, ARG_REPLY_DST }, + { "reply-dport", 1, 0, ARG_REPLY_DPORT }, + { "family", 1, 0, 'F' }, + { "mark", 1, 0, ARG_MARK }, + { "timeout", 1, 0, ARG_TIMEOUT }, + { "status", 1, 0, ARG_STATUS }, + { "zone", 1, 0, ARG_ZONE }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "46q:hv:p:F:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case '?': exit(NLE_INVAL); + case 'q': quiet = 1; break; + case '4': nfnl_ct_set_family(ct, AF_INET); break; + case '6': nfnl_ct_set_family(ct, AF_INET6); break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'p': nl_cli_ct_parse_protocol(ct, optarg); break; + case ARG_ORIG_SRC: nl_cli_ct_parse_src(ct, 0, optarg); break; + case ARG_ORIG_SPORT: nl_cli_ct_parse_src_port(ct, 0, optarg); break; + case ARG_ORIG_DST: nl_cli_ct_parse_dst(ct, 0, optarg); break; + case ARG_ORIG_DPORT: nl_cli_ct_parse_dst_port(ct, 0, optarg); break; + case ARG_REPLY_SRC: nl_cli_ct_parse_src(ct, 1, optarg); break; + case ARG_REPLY_SPORT: nl_cli_ct_parse_src_port(ct, 1, optarg); break; + case ARG_REPLY_DST: nl_cli_ct_parse_dst(ct, 1, optarg); break; + case ARG_REPLY_DPORT: nl_cli_ct_parse_dst_port(ct, 1, optarg); break; + case 'F': nl_cli_ct_parse_family(ct, optarg); break; + case ARG_MARK: nl_cli_ct_parse_mark(ct, optarg); break; + case ARG_TIMEOUT: nl_cli_ct_parse_timeout(ct, optarg); break; + case ARG_STATUS: nl_cli_ct_parse_status(ct, optarg); break; + case ARG_ZONE: nl_cli_ct_parse_zone(ct, optarg); break; + } + } + + if (!quiet) { + printf("Adding "); + nl_object_dump(OBJ_CAST(ct), ¶ms); + } + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_NETFILTER); + + if ((err = nfnl_ct_add(sock, ct, nlflags)) < 0) + nl_cli_fatal(err, "Unable to add conntrack: %s", nl_geterror(err)); + + if (!quiet) { + printf("Added "); + nl_object_dump(OBJ_CAST(ct), ¶ms); + } + + return 0; +} diff --git a/libnl/src/nf-ct-events.c b/libnl/src/nf-ct-events.c new file mode 100644 index 0000000..cd69045 --- /dev/null +++ b/libnl/src/nf-ct-events.c @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2018 Avast software + */ + +#include "nl-default.h" + +#include +#include +#include + +#include +#include + +struct private_nl_object +{ + int ce_refcnt; + struct nl_object_ops * ce_ops; + struct nl_cache * ce_cache; + struct nl_list_head ce_list; + int ce_msgtype; + int ce_flags; + uint64_t ce_mask; +}; + +static void nf_conntrack_parse_callback(struct nl_object *obj, void *opaque) +{ + struct nl_dump_params params = { + .dp_fd = stdout, + .dp_type = NL_DUMP_DETAILS, + }; + + nl_object_dump(obj, ¶ms); +} + +static int nf_conntrack_event_callback(struct nl_msg *msg, void *opaque) +{ + int err; + struct nlmsghdr *hdr = nlmsg_hdr(msg); + + enum cntl_msg_types type = (enum cntl_msg_types) NFNL_MSG_TYPE(hdr->nlmsg_type); + + int flags = hdr->nlmsg_flags; + + if (type == IPCTNL_MSG_CT_DELETE) { + printf("DELETE "); + } else if (type == IPCTNL_MSG_CT_NEW) { + if (flags & (NLM_F_CREATE|NLM_F_EXCL)) { + printf("NEW "); + } else { + printf("UPDATE "); + } + } else { + printf("UNKNOWN "); + } + + if ((err = nl_msg_parse(msg, &nf_conntrack_parse_callback, opaque)) < 0) { + nl_cli_fatal(err, "nl_msg_parse: %s", nl_geterror(err)); + } + /* Continue with next event */ + return NL_OK; +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *socket; + int err; + + socket = nl_cli_alloc_socket(); + if (socket == NULL) { + nl_cli_fatal(ENOBUFS, "Unable to allocate netlink socket"); + } + + /* + * Disable sequence number checking. + * This is required to allow messages to be processed which were not requested by + * a preceding request message, e.g. netlink events. + */ + nl_socket_disable_seq_check(socket); + + /* subscribe conntrack events */ + nl_join_groups(socket, NF_NETLINK_CONNTRACK_NEW | + NF_NETLINK_CONNTRACK_UPDATE | + NF_NETLINK_CONNTRACK_DESTROY | + NF_NETLINK_CONNTRACK_EXP_NEW | + NF_NETLINK_CONNTRACK_EXP_UPDATE | + NF_NETLINK_CONNTRACK_EXP_DESTROY); + + nl_cli_connect(socket, NETLINK_NETFILTER); + + nl_socket_modify_cb(socket, NL_CB_VALID, NL_CB_CUSTOM, &nf_conntrack_event_callback, 0); + + while (1) { + + errno = 0; + if ((err = nl_recvmsgs_default(socket)) < 0) { + switch (errno) { + case ENOBUFS: + // just print warning + fprintf(stderr, "Lost events because of ENOBUFS\n"); + break; + case EAGAIN: + case EINTR: + // continue reading + break; + default: + nl_cli_fatal(err, "Failed to receive: %s", nl_geterror(err)); + } + } + } +} diff --git a/libnl/src/nf-ct-list.c b/libnl/src/nf-ct-list.c new file mode 100644 index 0000000..3b34572 --- /dev/null +++ b/libnl/src/nf-ct-list.c @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#include "nl-default.h" + +#include + +#include +#include + +static void print_usage(void) +{ + printf( + "Usage: nf-ct-list [OPTION]... [CONNTRACK ENTRY]\n" + "\n" + "Options\n" + " -f, --format=TYPE Output format { brief | details | stats }\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + "\n" + "Conntrack Selection\n" + " -i, --id=NUM Identifier\n" + " -p, --proto=PROTOCOL Protocol\n" + " --tcp-state=STATE TCP connection state\n" + " --orig-src=ADDR Original source address\n" + " --orig-sport=PORT Original source port\n" + " --orig-dst=ADDR Original destination address\n" + " --orig-dport=PORT Original destination port\n" + " --reply-src=ADDR Reply source address\n" + " --reply-sport=PORT Reply source port\n" + " --reply-dst=ADDR Reply destination address\n" + " --reply-dport=PORT Reply destination port\n" + " -F, --family=FAMILY Address family\n" + " --mark=NUM Mark value\n" + " --timeout=NUM Timeout value\n" + " --refcnt=NUM Use counter value\n" + " --flags Flags\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nl_cache *ct_cache; + struct nfnl_ct *ct; + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + + ct = nl_cli_ct_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_MARK = 257, + ARG_TCP_STATE = 258, + ARG_ORIG_SRC, + ARG_ORIG_SPORT, + ARG_ORIG_DST, + ARG_ORIG_DPORT, + ARG_REPLY_SRC, + ARG_REPLY_SPORT, + ARG_REPLY_DST, + ARG_REPLY_DPORT, + ARG_TIMEOUT, + ARG_REFCNT, + ARG_FLAGS, + }; + static struct option long_opts[] = { + { "format", 1, 0, 'f' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "id", 1, 0, 'i' }, + { "proto", 1, 0, 'p' }, + { "tcp-state", 1, 0, ARG_TCP_STATE }, + { "orig-src", 1, 0, ARG_ORIG_SRC }, + { "orig-sport", 1, 0, ARG_ORIG_SPORT }, + { "orig-dst", 1, 0, ARG_ORIG_DST }, + { "orig-dport", 1, 0, ARG_ORIG_DPORT }, + { "reply-src", 1, 0, ARG_REPLY_SRC }, + { "reply-sport", 1, 0, ARG_REPLY_SPORT }, + { "reply-dst", 1, 0, ARG_REPLY_DST }, + { "reply-dport", 1, 0, ARG_REPLY_DPORT }, + { "family", 1, 0, 'F' }, + { "mark", 1, 0, ARG_MARK }, + { "timeout", 1, 0, ARG_TIMEOUT }, + { "refcnt", 1, 0, ARG_REFCNT }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "46f:hvi:p:F:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case '?': exit(NLE_INVAL); + case '4': nfnl_ct_set_family(ct, AF_INET); break; + case '6': nfnl_ct_set_family(ct, AF_INET6); break; + case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'i': nl_cli_ct_parse_id(ct, optarg); break; + case 'p': nl_cli_ct_parse_protocol(ct, optarg); break; + case ARG_TCP_STATE: nl_cli_ct_parse_tcp_state(ct, optarg); break; + case ARG_ORIG_SRC: nl_cli_ct_parse_src(ct, 0, optarg); break; + case ARG_ORIG_SPORT: nl_cli_ct_parse_src_port(ct, 0, optarg); break; + case ARG_ORIG_DST: nl_cli_ct_parse_dst(ct, 0, optarg); break; + case ARG_ORIG_DPORT: nl_cli_ct_parse_dst_port(ct, 0, optarg); break; + case ARG_REPLY_SRC: nl_cli_ct_parse_src(ct, 1, optarg); break; + case ARG_REPLY_SPORT: nl_cli_ct_parse_src_port(ct, 1, optarg); break; + case ARG_REPLY_DST: nl_cli_ct_parse_dst(ct, 1, optarg); break; + case ARG_REPLY_DPORT: nl_cli_ct_parse_dst_port(ct, 1, optarg); break; + case 'F': nl_cli_ct_parse_family(ct, optarg); break; + case ARG_MARK: nl_cli_ct_parse_mark(ct, optarg); break; + case ARG_TIMEOUT: nl_cli_ct_parse_timeout(ct, optarg); break; + case ARG_REFCNT: nl_cli_ct_parse_use(ct, optarg); break; + case ARG_FLAGS: nl_cli_ct_parse_status(ct, optarg); break; + } + } + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_NETFILTER); + ct_cache = nl_cli_ct_alloc_cache(sock); + + nl_cache_dump_filter(ct_cache, ¶ms, OBJ_CAST(ct)); + + return 0; +} diff --git a/libnl/src/nf-exp-add.c b/libnl/src/nf-exp-add.c new file mode 100644 index 0000000..68f0898 --- /dev/null +++ b/libnl/src/nf-exp-add.c @@ -0,0 +1,184 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2012 Rich Fought + */ + +#include "nl-default.h" + +#include + +#include +#include + +static int quiet = 0; + +static void print_usage(void) +{ + printf( + "Usage: nf-exp-list [OPTION]... [CONNTRACK ENTRY]\n" + "\n" + "Options\n" + " --replace Replace the address if it exists.\n" + " -q, --quiet Do not print informal notifications.\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + "\n" + "Expectation Selection\n" + " -i, --id=NUM Identifier\n" + " --expect-proto=PROTOCOL Expectation protocol\n" + " --expect-src=ADDR Expectation source address\n" + " --expect-sport=PORT Expectation source port\n" + " --expect-dst=ADDR Expectation destination address\n" + " --expect-dport=PORT Expectation destination port\n" + " --master-proto=PROTOCOL Master conntrack protocol\n" + " --master-src=ADDR Master conntrack source address\n" + " --master-sport=PORT Master conntrack source port\n" + " --master-dst=ADDR Master conntrack destination address\n" + " --master-dport=PORT Master conntrack destination port\n" + " --mask-proto=PROTOCOL Mask protocol\n" + " --mask-src=ADDR Mask source address\n" + " --mask-sport=PORT Mask source port\n" + " --mask-dst=ADDR Mask destination address\n" + " --mask-dport=PORT Mask destination port\n" + " -F, --family=FAMILY Address family\n" + " --timeout=NUM Timeout value\n" + " --helper=STRING Helper Name\n" + " --flags Flags (Kernel 2.6.37)\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nfnl_exp *exp; + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + int err, nlflags = NLM_F_CREATE; + + exp = nl_cli_exp_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_MARK = 270, + ARG_TCP_STATE = 271, + ARG_EXPECT_PROTO, + ARG_EXPECT_SRC, + ARG_EXPECT_SPORT, + ARG_EXPECT_DST, + ARG_EXPECT_DPORT, + ARG_MASTER_PROTO, + ARG_MASTER_SRC, + ARG_MASTER_SPORT, + ARG_MASTER_DST, + ARG_MASTER_DPORT, + ARG_MASK_PROTO, + ARG_MASK_SRC, + ARG_MASK_SPORT, + ARG_MASK_DST, + ARG_MASK_DPORT, + ARG_NAT_PROTO, + ARG_NAT_SRC, + ARG_NAT_SPORT, + ARG_NAT_DST, + ARG_NAT_DPORT, + ARG_NAT_DIR, + ARG_TIMEOUT, + ARG_HELPER_NAME, + ARG_REPLACE, + ARG_FLAGS, + }; + static struct option long_opts[] = { + { "replace", 1, 0, ARG_REPLACE }, + { "quiet", 0, 0, 'q' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "id", 1, 0, 'i' }, + { "expect-proto", 1, 0, ARG_EXPECT_PROTO }, + { "expect-src", 1, 0, ARG_EXPECT_SRC }, + { "expect-sport", 1, 0, ARG_EXPECT_SPORT }, + { "expect-dst", 1, 0, ARG_EXPECT_DST }, + { "expect-dport", 1, 0, ARG_EXPECT_DPORT }, + { "master-proto", 1, 0, ARG_MASTER_PROTO }, + { "master-src", 1, 0, ARG_MASTER_SRC }, + { "master-sport", 1, 0, ARG_MASTER_SPORT }, + { "master-dst", 1, 0, ARG_MASTER_DST }, + { "master-dport", 1, 0, ARG_MASTER_DPORT }, + { "mask-proto", 1, 0, ARG_MASK_PROTO }, + { "mask-src", 1, 0, ARG_MASK_SRC }, + { "mask-sport", 1, 0, ARG_MASK_SPORT }, + { "mask-dst", 1, 0, ARG_MASK_DST }, + { "mask-dport", 1, 0, ARG_MASK_DPORT }, + { "nat-proto", 1, 0, ARG_NAT_PROTO }, + { "nat-src", 1, 0, ARG_NAT_SRC }, + { "nat-sport", 1, 0, ARG_NAT_SPORT }, + { "nat-dst", 1, 0, ARG_NAT_DST }, + { "nat-dport", 1, 0, ARG_NAT_DPORT }, + { "nat-dir", 1, 0, ARG_NAT_DIR }, + { "family", 1, 0, 'F' }, + { "timeout", 1, 0, ARG_TIMEOUT }, + { "helper", 1, 0, ARG_HELPER_NAME }, + { "flags", 1, 0, ARG_FLAGS}, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "46f:hvi:p:F:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case '?': exit(NLE_INVAL); + case ARG_REPLACE: nlflags |= NLM_F_REPLACE; break; + case 'q': quiet = 1; break; + case '4': nfnl_exp_set_family(exp, AF_INET); break; + case '6': nfnl_exp_set_family(exp, AF_INET6); break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'i': nl_cli_exp_parse_id(exp, optarg); break; + case ARG_EXPECT_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_MASTER_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASK_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case ARG_MASK_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case ARG_MASK_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case ARG_MASK_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case ARG_MASK_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case ARG_NAT_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_NAT, optarg); break; + case ARG_NAT_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_NAT, optarg); break; + case ARG_NAT_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_NAT, optarg); break; + case ARG_NAT_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_NAT, optarg); break; + case ARG_NAT_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_NAT, optarg); break; + case ARG_NAT_DIR: nl_cli_exp_parse_nat_dir(exp, optarg); break; + case 'F': nl_cli_exp_parse_family(exp, optarg); break; + case ARG_TIMEOUT: nl_cli_exp_parse_timeout(exp, optarg); break; + case ARG_HELPER_NAME: nl_cli_exp_parse_helper_name(exp, optarg); break; + case ARG_FLAGS: nl_cli_exp_parse_flags(exp, optarg); break; + } + } + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_NETFILTER); + + if ((err = nfnl_exp_add(sock, exp, nlflags)) < 0) + nl_cli_fatal(err, "Unable to add expectation: %s", nl_geterror(err)); + + if (!quiet) { + printf("Added "); + nl_object_dump(OBJ_CAST(exp), ¶ms); + } + + return 0; +} diff --git a/libnl/src/nf-exp-delete.c b/libnl/src/nf-exp-delete.c new file mode 100644 index 0000000..a57a69d --- /dev/null +++ b/libnl/src/nf-exp-delete.c @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2012 Rich Fought + */ + +#include "nl-default.h" + +#include + +#include +#include + +static int quiet = 0; + +static void print_usage(void) +{ + printf( + "Usage: nf-exp-list [OPTION]... [CONNTRACK ENTRY]\n" + "\n" + "Options\n" + " --replace Replace the address if it exists.\n" + " -q, --quiet Do not print informal notifications.\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + "\n" + "Expectation Selection\n" + " -i, --id=NUM Identifier\n" + " --expect-proto=PROTOCOL Expectation protocol\n" + " --expect-src=ADDR Expectation source address\n" + " --expect-sport=PORT Expectation source port\n" + " --expect-dst=ADDR Expectation destination address\n" + " --expect-dport=PORT Expectation destination port\n" + " --master-proto=PROTOCOL Master conntrack protocol\n" + " --master-src=ADDR Master conntrack source address\n" + " --master-sport=PORT Master conntrack source port\n" + " --master-dst=ADDR Master conntrack destination address\n" + " --master-dport=PORT Master conntrack destination port\n" + " --mask-proto=PROTOCOL Mask protocol\n" + " --mask-src=ADDR Mask source address\n" + " --mask-sport=PORT Mask source port\n" + " --mask-dst=ADDR Mask destination address\n" + " --mask-dport=PORT Mask destination port\n" + " -F, --family=FAMILY Address family\n" + " --timeout=NUM Timeout value\n" + " --helper=STRING Helper Name\n" + " --flags Flags\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nfnl_exp *exp; + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + int err, nlflags = 0; + + exp = nl_cli_exp_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_MARK = 270, + ARG_TCP_STATE = 271, + ARG_EXPECT_PROTO, + ARG_EXPECT_SRC, + ARG_EXPECT_SPORT, + ARG_EXPECT_DST, + ARG_EXPECT_DPORT, + ARG_MASTER_PROTO, + ARG_MASTER_SRC, + ARG_MASTER_SPORT, + ARG_MASTER_DST, + ARG_MASTER_DPORT, + ARG_MASK_PROTO, + ARG_MASK_SRC, + ARG_MASK_SPORT, + ARG_MASK_DST, + ARG_MASK_DPORT, + ARG_TIMEOUT, + ARG_HELPER_NAME, + ARG_FLAGS, + }; + static struct option long_opts[] = { + { "quiet", 0, 0, 'q' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "id", 1, 0, 'i' }, + { "expect-proto", 1, 0, ARG_EXPECT_PROTO }, + { "expect-src", 1, 0, ARG_EXPECT_SRC }, + { "expect-sport", 1, 0, ARG_EXPECT_SPORT }, + { "expect-dst", 1, 0, ARG_EXPECT_DST }, + { "expect-dport", 1, 0, ARG_EXPECT_DPORT }, + { "master-proto", 1, 0, ARG_MASTER_PROTO }, + { "master-src", 1, 0, ARG_MASTER_SRC }, + { "master-sport", 1, 0, ARG_MASTER_SPORT }, + { "master-dst", 1, 0, ARG_MASTER_DST }, + { "master-dport", 1, 0, ARG_MASTER_DPORT }, + { "mask-proto", 1, 0, ARG_MASK_PROTO }, + { "mask-src", 1, 0, ARG_MASK_SRC }, + { "mask-sport", 1, 0, ARG_MASK_SPORT }, + { "mask-dst", 1, 0, ARG_MASK_DST }, + { "mask-dport", 1, 0, ARG_MASK_DPORT }, + { "family", 1, 0, 'F' }, + { "timeout", 1, 0, ARG_TIMEOUT }, + { "helper", 1, 0, ARG_HELPER_NAME }, + { "flags", 1, 0, ARG_FLAGS}, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "46f:hvi:p:F:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case '?': exit(NLE_INVAL); + case 'q': quiet = 1; break; + case '4': nfnl_exp_set_family(exp, AF_INET); break; + case '6': nfnl_exp_set_family(exp, AF_INET6); break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'i': nl_cli_exp_parse_id(exp, optarg); break; + case ARG_EXPECT_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_MASTER_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASK_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case ARG_MASK_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case ARG_MASK_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case ARG_MASK_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case ARG_MASK_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case 'F': nl_cli_exp_parse_family(exp, optarg); break; + case ARG_TIMEOUT: nl_cli_exp_parse_timeout(exp, optarg); break; + case ARG_HELPER_NAME: nl_cli_exp_parse_helper_name(exp, optarg); break; + case ARG_FLAGS: nl_cli_exp_parse_flags(exp, optarg); break; + } + } + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_NETFILTER); + + if ((err = nfnl_exp_del(sock, exp, nlflags)) < 0) + nl_cli_fatal(err, "Unable to delete expectation: %s", nl_geterror(err)); + + if (!quiet) { + printf("Deleted "); + nl_object_dump(OBJ_CAST(exp), ¶ms); + } + + return 0; +} diff --git a/libnl/src/nf-exp-list.c b/libnl/src/nf-exp-list.c new file mode 100644 index 0000000..f469fea --- /dev/null +++ b/libnl/src/nf-exp-list.c @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2012 Rich Fought + */ + +#include "nl-default.h" + +#include + +#include +#include + +static void print_usage(void) +{ + printf( + "Usage: nf-exp-list [OPTION]... [EXPECTATION ENTRY]\n" + "\n" + "Options\n" + " -f, --format=TYPE Output format { brief | details }\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + "\n" + "Expectation Selection\n" + " -i, --id=NUM Identifier\n" + " --expect-proto=PROTOCOL Expectation protocol\n" + " --expect-src=ADDR Expectation source address\n" + " --expect-sport=PORT Expectation source port\n" + " --expect-dst=ADDR Expectation destination address\n" + " --expect-dport=PORT Expectation destination port\n" + " --master-proto=PROTOCOL Master conntrack protocol\n" + " --master-src=ADDR Master conntrack source address\n" + " --master-sport=PORT Master conntrack source port\n" + " --master-dst=ADDR Master conntrack destination address\n" + " --master-dport=PORT Master conntrack destination port\n" + " -F, --family=FAMILY Address family\n" + " --timeout=NUM Timeout value\n" + " --helper=STRING Helper Name\n" + //" --flags Flags\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nl_cache *exp_cache; + struct nfnl_exp *exp; + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + + exp = nl_cli_exp_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_MARK = 270, + ARG_TCP_STATE = 271, + ARG_EXPECT_PROTO, + ARG_EXPECT_SRC, + ARG_EXPECT_SPORT, + ARG_EXPECT_DST, + ARG_EXPECT_DPORT, + ARG_MASTER_PROTO, + ARG_MASTER_SRC, + ARG_MASTER_SPORT, + ARG_MASTER_DST, + ARG_MASTER_DPORT, + ARG_TIMEOUT, + ARG_HELPER_NAME, + ARG_FLAGS, + }; + static struct option long_opts[] = { + { "format", 1, 0, 'f' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "id", 1, 0, 'i' }, + { "expect-proto", 1, 0, ARG_EXPECT_PROTO }, + { "expect-src", 1, 0, ARG_EXPECT_SRC }, + { "expect-sport", 1, 0, ARG_EXPECT_SPORT }, + { "expect-dst", 1, 0, ARG_EXPECT_DST }, + { "expect-dport", 1, 0, ARG_EXPECT_DPORT }, + { "master-proto", 1, 0, ARG_MASTER_PROTO }, + { "master-src", 1, 0, ARG_MASTER_SRC }, + { "master-sport", 1, 0, ARG_MASTER_SPORT }, + { "master-dst", 1, 0, ARG_MASTER_DST }, + { "master-dport", 1, 0, ARG_MASTER_DPORT }, + { "family", 1, 0, 'F' }, + { "timeout", 1, 0, ARG_TIMEOUT }, + { "helper", 1, 0, ARG_HELPER_NAME }, + { "flags", 1, 0, ARG_FLAGS}, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "46f:hvi:p:F:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case '?': exit(NLE_INVAL); + case '4': nfnl_exp_set_family(exp, AF_INET); break; + case '6': nfnl_exp_set_family(exp, AF_INET6); break; + case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'i': nl_cli_exp_parse_id(exp, optarg); break; + case ARG_EXPECT_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_MASTER_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case 'F': nl_cli_exp_parse_family(exp, optarg); break; + case ARG_TIMEOUT: nl_cli_exp_parse_timeout(exp, optarg); break; + case ARG_HELPER_NAME: nl_cli_exp_parse_helper_name(exp, optarg); break; + case ARG_FLAGS: nl_cli_exp_parse_flags(exp, optarg); break; + } + } + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_NETFILTER); + exp_cache = nl_cli_exp_alloc_cache(sock); + + nl_cache_dump_filter(exp_cache, ¶ms, OBJ_CAST(exp)); + + return 0; +} diff --git a/libnl/src/nf-log.c b/libnl/src/nf-log.c new file mode 100644 index 0000000..17f73a8 --- /dev/null +++ b/libnl/src/nf-log.c @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#include "nl-default.h" + +#include +#include + +#include +#include +#include +#include + +static struct nfnl_log *alloc_log(void) +{ + struct nfnl_log *log; + + log = nfnl_log_alloc(); + if (!log) + nl_cli_fatal(ENOMEM, "Unable to allocate log object"); + + return log; +} + +static void obj_input(struct nl_object *obj, void *arg) +{ + struct nl_dump_params dp = { + .dp_type = NL_DUMP_STATS, + .dp_fd = stdout, + .dp_dump_msgtype = 1, + }; + + nl_object_dump(obj, &dp); +} + +static int event_input(struct nl_msg *msg, void *arg) +{ + if (nl_msg_parse(msg, &obj_input, NULL) < 0) + fprintf(stderr, "<> Unknown message type\n"); + + /* Exit nl_recvmsgs_def() and return to the main select() */ + return NL_STOP; +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *nf_sock; + struct nl_sock *rt_sock; + struct nfnl_log *log; + int copy_mode; + uint32_t copy_range; + int err; + int family; + + nf_sock = nl_cli_alloc_socket(); + nl_socket_disable_seq_check(nf_sock); + nl_socket_modify_cb(nf_sock, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL); + + if ((argc > 1 && !strcasecmp(argv[1], "-h")) || argc < 3) { + printf("Usage: nf-log family group [ copy_mode ] " + "[copy_range] \n"); + return 2; + } + + nl_cli_connect(nf_sock, NETLINK_NETFILTER); + + family = nl_str2af(argv[1]); + if (family == AF_UNSPEC) + nl_cli_fatal(NLE_INVAL, "Unknown family \"%s\": %s", + argv[1], nl_geterror(family)); + + nfnl_log_pf_unbind(nf_sock, family); + if ((err = nfnl_log_pf_bind(nf_sock, family)) < 0) + nl_cli_fatal(err, "Unable to bind logger: %s", + nl_geterror(err)); + + log = alloc_log(); + nfnl_log_set_group(log, atoi(argv[2])); + + copy_mode = NFNL_LOG_COPY_META; + if (argc > 3) { + copy_mode = nfnl_log_str2copy_mode(argv[3]); + if (copy_mode < 0) + nl_cli_fatal(copy_mode, + "Unable to parse copy mode \"%s\": %s", + argv[3], nl_geterror(copy_mode)); + } + nfnl_log_set_copy_mode(log, copy_mode); + + copy_range = 0xFFFF; + if (argc > 4) + copy_range = atoi(argv[4]); + nfnl_log_set_copy_range(log, copy_range); + + if ((err = nfnl_log_create(nf_sock, log)) < 0) + nl_cli_fatal(err, "Unable to bind instance: %s", + nl_geterror(err)); + + { + struct nl_dump_params dp = { + .dp_type = NL_DUMP_STATS, + .dp_fd = stdout, + .dp_dump_msgtype = 1, + }; + + printf("log params: "); + nl_object_dump((struct nl_object *) log, &dp); + } + + rt_sock = nl_cli_alloc_socket(); + nl_cli_connect(rt_sock, NETLINK_ROUTE); + nl_cli_link_alloc_cache(rt_sock); + + while (1) { + fd_set rfds; + int nffd, rtfd, maxfd, retval; + + FD_ZERO(&rfds); + + maxfd = nffd = nl_socket_get_fd(nf_sock); + FD_SET(nffd, &rfds); + + rtfd = nl_socket_get_fd(rt_sock); + FD_SET(rtfd, &rfds); + if (maxfd < rtfd) + maxfd = rtfd; + + /* wait for an incoming message on the netlink nf_socket */ + retval = select(maxfd+1, &rfds, NULL, NULL, NULL); + + if (retval) { + if (FD_ISSET(nffd, &rfds)) + nl_recvmsgs_default(nf_sock); + if (FD_ISSET(rtfd, &rfds)) + nl_recvmsgs_default(rt_sock); + } + } + + return 0; +} diff --git a/libnl/src/nf-monitor.c b/libnl/src/nf-monitor.c new file mode 100644 index 0000000..21f8edd --- /dev/null +++ b/libnl/src/nf-monitor.c @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#include "nl-default.h" + +#include +#include + +#include +#include + +static void obj_input(struct nl_object *obj, void *arg) +{ + struct nl_dump_params dp = { + .dp_type = NL_DUMP_STATS, + .dp_fd = stdout, + .dp_dump_msgtype = 1, + }; + + nl_object_dump(obj, &dp); +} + +static int event_input(struct nl_msg *msg, void *arg) +{ + if (nl_msg_parse(msg, &obj_input, NULL) < 0) + fprintf(stderr, "<> Unknown message type\n"); + + /* Exit nl_recvmsgs_def() and return to the main select() */ + return NL_STOP; +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + int err; + int i, idx; + + static const struct { + enum nfnetlink_groups gr_id; + const char* gr_name; + } groups[] = { + { NFNLGRP_CONNTRACK_NEW, "ct-new" }, + { NFNLGRP_CONNTRACK_UPDATE, "ct-update" }, + { NFNLGRP_CONNTRACK_DESTROY, "ct-destroy" }, + { NFNLGRP_NONE, NULL } + }; + + sock = nl_cli_alloc_socket(); + nl_socket_disable_seq_check(sock); + nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL); + + if (argc > 1 && !strcasecmp(argv[1], "-h")) { + printf("Usage: nf-monitor []\n"); + + printf("Known groups:"); + for (i = 0; groups[i].gr_id != NFNLGRP_NONE; i++) + printf(" %s", groups[i].gr_name); + printf("\n"); + return 2; + } + + nl_cli_connect(sock, NETLINK_NETFILTER); + + for (idx = 1; argc > idx; idx++) { + for (i = 0; groups[i].gr_id != NFNLGRP_NONE; i++) { + if (strcmp(argv[idx], groups[i].gr_name)) + continue; + + err = nl_socket_add_membership(sock, groups[i].gr_id); + if (err < 0) + nl_cli_fatal(err, + "Unable to add membership: %s", + nl_geterror(err)); + break; + } + + if (groups[i].gr_id == NFNLGRP_NONE) + nl_cli_fatal(NLE_OBJ_NOTFOUND, "Unknown group: \"%s\"", + argv[idx]); + } + + while (1) { + fd_set rfds; + int fd, retval; + + fd = nl_socket_get_fd(sock); + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + /* wait for an incoming message on the netlink socket */ + retval = select(fd+1, &rfds, NULL, NULL, NULL); + + if (retval) { + /* FD_ISSET(fd, &rfds) will be true */ + nl_recvmsgs_default(sock); + } + } + + return 0; +} diff --git a/libnl/src/nf-queue.c b/libnl/src/nf-queue.c new file mode 100644 index 0000000..38f20ad --- /dev/null +++ b/libnl/src/nf-queue.c @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2007, 2008 Patrick McHardy + * Copyright (c) 2010 Karl Hiramoto + */ + +#include "nl-default.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +static struct nl_sock *nf_sock; + +static struct nfnl_queue *alloc_queue(void) +{ + struct nfnl_queue *queue; + + queue = nfnl_queue_alloc(); + if (!queue) + nl_cli_fatal(ENOMEM, "Unable to allocate queue object"); + + return queue; +} + + +static void obj_input(struct nl_object *obj, void *arg) +{ + struct nfnl_queue_msg *msg = (struct nfnl_queue_msg *) obj; + struct nl_dump_params dp = { + .dp_type = NL_DUMP_STATS, + .dp_fd = stdout, + .dp_dump_msgtype = 1, + }; + + nfnl_queue_msg_set_verdict(msg, NF_ACCEPT); + nl_object_dump(obj, &dp); + nfnl_queue_msg_send_verdict(nf_sock, msg); +} + +static int event_input(struct nl_msg *msg, void *arg) +{ + if (nl_msg_parse(msg, &obj_input, NULL) < 0) + fprintf(stderr, "<> Unknown message type\n"); + + /* Exit nl_recvmsgs_def() and return to the main select() */ + return NL_STOP; +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *rt_sock; + struct nfnl_queue *queue; + int copy_mode; + uint32_t copy_range; + int err = 1; + int family; + + nf_sock = nfnl_queue_socket_alloc(); + if (nf_sock == NULL) + nl_cli_fatal(ENOBUFS, "Unable to allocate netlink socket"); + + nl_socket_disable_seq_check(nf_sock); + nl_socket_modify_cb(nf_sock, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL); + + if ((argc > 1 && !strcasecmp(argv[1], "-h")) || argc < 3) { + printf("Usage: nf-queue family group [ copy_mode ] " + "[ copy_range ]\n"); + printf("family: [ inet | inet6 | ... ] \n"); + printf("group: the --queue-num arg that you gave to iptables\n"); + printf("copy_mode: [ none | meta | packet ] \n"); + return 2; + } + + nl_cli_connect(nf_sock, NETLINK_NETFILTER); + + if ((family = nl_str2af(argv[1])) == AF_UNSPEC) + nl_cli_fatal(NLE_INVAL, "Unknown family \"%s\"", argv[1]); + + nfnl_queue_pf_unbind(nf_sock, family); + if ((err = nfnl_queue_pf_bind(nf_sock, family)) < 0) + nl_cli_fatal(err, "Unable to bind logger: %s", + nl_geterror(err)); + + queue = alloc_queue(); + nfnl_queue_set_group(queue, atoi(argv[2])); + + copy_mode = NFNL_QUEUE_COPY_PACKET; + if (argc > 3) { + copy_mode = nfnl_queue_str2copy_mode(argv[3]); + if (copy_mode < 0) + nl_cli_fatal(copy_mode, + "Unable to parse copy mode \"%s\": %s", + argv[3], nl_geterror(copy_mode)); + } + nfnl_queue_set_copy_mode(queue, copy_mode); + + copy_range = 0xFFFF; + if (argc > 4) + copy_range = atoi(argv[4]); + nfnl_queue_set_copy_range(queue, copy_range); + + if ((err = nfnl_queue_create(nf_sock, queue)) < 0) + nl_cli_fatal(err, "Unable to bind queue: %s", nl_geterror(err)); + + rt_sock = nl_cli_alloc_socket(); + nl_cli_connect(rt_sock, NETLINK_ROUTE); + nl_cli_link_alloc_cache(rt_sock); + + nl_socket_set_buffer_size(nf_sock, 1024*127, 1024*127); + + while (1) { + fd_set rfds; + int nffd, rtfd, maxfd, retval; + + FD_ZERO(&rfds); + + maxfd = nffd = nl_socket_get_fd(nf_sock); + FD_SET(nffd, &rfds); + + rtfd = nl_socket_get_fd(rt_sock); + FD_SET(rtfd, &rfds); + if (maxfd < rtfd) + maxfd = rtfd; + + /* wait for an incoming message on the netlink socket */ + retval = select(maxfd+1, &rfds, NULL, NULL, NULL); + + if (retval) { + if (FD_ISSET(nffd, &rfds)) + nl_recvmsgs_default(nf_sock); + if (FD_ISSET(rtfd, &rfds)) + nl_recvmsgs_default(rt_sock); + } + } + + return 0; +} diff --git a/libnl/src/nl-addr-add.c b/libnl/src/nl-addr-add.c new file mode 100644 index 0000000..3b73cb2 --- /dev/null +++ b/libnl/src/nl-addr-add.c @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +static int quiet = 0; + +static void print_usage(void) +{ + printf( +"Usage: nl-addr-add [OPTION]... [ADDRESS]\n" +"\n" +"Options\n" +" --replace Replace the address if it exists.\n" +" -q, --quiet Do not print informal notifications.\n" +" -h, --help Show this help.\n" +" -v, --version Show versioning information.\n" +"\n" +"Address Options\n" +" -a, --local=ADDR Address to be considered local.\n" +" -d, --dev=DEV Device the address should be assigned to.\n" +" --family=FAMILY Address family (normally autodetected).\n" +" --broadcast=ADDR Broadcast address of network (IPv4).\n" +" --peer=ADDR Peer address (IPv4).\n" +" --label=STRING Additional address label (IPv4).\n" +" --scope=SCOPE Scope of local address (IPv4).\n" +" --preferred=TIME Preferred lifetime (IPv6).\n" +" --valid=TIME Valid lifetime (IPv6).\n" + ); + + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct rtnl_addr *addr; + struct nl_cache *link_cache; + struct nl_dump_params dp = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + int err, nlflags = NLM_F_CREATE; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + addr = nl_cli_addr_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_FAMILY = 257, + ARG_LABEL = 258, + ARG_PEER, + ARG_SCOPE, + ARG_BROADCAST, + ARG_REPLACE, + ARG_PREFERRED, + ARG_VALID, + }; + static struct option long_opts[] = { + { "replace", 0, 0, ARG_REPLACE }, + { "quiet", 0, 0, 'q' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "local", 1, 0, 'a' }, + { "dev", 1, 0, 'd' }, + { "family", 1, 0, ARG_FAMILY }, + { "label", 1, 0, ARG_LABEL }, + { "peer", 1, 0, ARG_PEER }, + { "scope", 1, 0, ARG_SCOPE }, + { "broadcast", 1, 0, ARG_BROADCAST }, + { "preferred", 1, 0, ARG_PREFERRED }, + { "valid", 1, 0, ARG_VALID }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "qhva:d:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case '?': exit(NLE_INVAL); + case ARG_REPLACE: nlflags |= NLM_F_REPLACE; break; + case 'q': quiet = 1; break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'a': nl_cli_addr_parse_local(addr, optarg); break; + case 'd': nl_cli_addr_parse_dev(addr, link_cache, optarg); break; + case ARG_FAMILY: nl_cli_addr_parse_family(addr, optarg); break; + case ARG_LABEL: nl_cli_addr_parse_label(addr, optarg); break; + case ARG_PEER: nl_cli_addr_parse_peer(addr, optarg); break; + case ARG_SCOPE: nl_cli_addr_parse_scope(addr, optarg); break; + case ARG_BROADCAST: nl_cli_addr_parse_broadcast(addr, optarg); break; + case ARG_PREFERRED: nl_cli_addr_parse_preferred(addr, optarg); break; + case ARG_VALID: nl_cli_addr_parse_valid(addr, optarg); break; + } + } + + if ((err = rtnl_addr_add(sock, addr, nlflags)) < 0) + nl_cli_fatal(err, "Unable to add address: %s", + nl_geterror(err)); + + if (!quiet) { + printf("Added "); + nl_object_dump(OBJ_CAST(addr), &dp); + } + + return 0; +} diff --git a/libnl/src/nl-addr-delete.c b/libnl/src/nl-addr-delete.c new file mode 100644 index 0000000..58dd439 --- /dev/null +++ b/libnl/src/nl-addr-delete.c @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +static struct nl_sock *sock; +static int interactive = 0, default_yes = 0, quiet = 0; +static int deleted = 0; + +static void print_usage(void) +{ + printf( +"Usage: nl-addr-delete [OPTION]... [ADDRESS]\n" +"\n" +"Options\n" +" -i, --interactive Run interactively.\n" +" --yes Set default answer to yes.\n" +" -q, --quiet Do not print informal notifications.\n" +" -h, --help Show this help.\n" +" -v, --version Show versioning information.\n" +"\n" +"Address Options\n" +" -a, --local=ADDR Local address.\n" +" -d, --dev=DEV Associated network device.\n" +" --family=FAMILY Family of local address.\n" +" --label=STRING Address label (IPv4).\n" +" --peer=ADDR Peer address (IPv4).\n" +" --scope=SCOPE Address scope (IPv4).\n" +" --broadcast=ADDR Broadcast address of network (IPv4).\n" +" --valid-lifetime=TS Valid lifetime before route expires (IPv6).\n" +" --preferred=TIME Preferred lifetime (IPv6).\n" +" --valid=TIME Valid lifetime (IPv6).\n" + ); + + exit(0); +} + +static void delete_cb(struct nl_object *obj, void *arg) +{ + struct rtnl_addr *addr = nl_object_priv(obj); + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + int err; + + if (interactive && !nl_cli_confirm(obj, ¶ms, default_yes)) + return; + + if ((err = rtnl_addr_delete(sock, addr, 0)) < 0) + nl_cli_fatal(err, "Unable to delete address: %s\n", + nl_geterror(err)); + + if (!quiet) { + printf("Deleted "); + nl_object_dump(obj, ¶ms); + } + + deleted++; +} + +int main(int argc, char *argv[]) +{ + struct rtnl_addr *addr; + struct nl_cache *link_cache, *addr_cache; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + addr_cache = nl_cli_addr_alloc_cache(sock); + addr = nl_cli_addr_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_FAMILY = 257, + ARG_LABEL = 258, + ARG_YES, + ARG_PEER, + ARG_SCOPE, + ARG_BROADCAST, + ARG_PREFERRED, + ARG_VALID, + }; + static struct option long_opts[] = { + { "interactive", 0, 0, 'i' }, + { "yes", 0, 0, ARG_YES }, + { "quiet", 0, 0, 'q' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "local", 1, 0, 'a' }, + { "dev", 1, 0, 'd' }, + { "family", 1, 0, ARG_FAMILY }, + { "label", 1, 0, ARG_LABEL }, + { "peer", 1, 0, ARG_PEER }, + { "scope", 1, 0, ARG_SCOPE }, + { "broadcast", 1, 0, ARG_BROADCAST }, + { "preferred", 1, 0, ARG_PREFERRED }, + { "valid", 1, 0, ARG_VALID }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "iqhva:d:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'i': interactive = 1; break; + case ARG_YES: default_yes = 1; break; + case 'q': quiet = 1; break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'a': nl_cli_addr_parse_local(addr, optarg); break; + case 'd': nl_cli_addr_parse_dev(addr, link_cache, optarg); break; + case ARG_FAMILY: nl_cli_addr_parse_family(addr, optarg); break; + case ARG_LABEL: nl_cli_addr_parse_label(addr, optarg); break; + case ARG_PEER: nl_cli_addr_parse_peer(addr, optarg); break; + case ARG_SCOPE: nl_cli_addr_parse_scope(addr, optarg); break; + case ARG_BROADCAST: nl_cli_addr_parse_broadcast(addr, optarg); break; + case ARG_PREFERRED: nl_cli_addr_parse_preferred(addr, optarg); break; + case ARG_VALID: nl_cli_addr_parse_valid(addr, optarg); break; + } + } + + nl_cache_foreach_filter(addr_cache, OBJ_CAST(addr), delete_cb, NULL); + + if (!quiet) + printf("Deleted %d addresses\n", deleted); + + return 0; +} diff --git a/libnl/src/nl-addr-list.c b/libnl/src/nl-addr-list.c new file mode 100644 index 0000000..00df1aa --- /dev/null +++ b/libnl/src/nl-addr-list.c @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +static void print_usage(void) +{ + printf( +"Usage: nl-addr-list [OPTION]... [ADDRESS]\n" +"\n" +"Options\n" +" --details Show details on multiple lines.\n" +" --env Print address details in sh env variable syntax.\n" +" --prefix=STRING Prefix each printed line.\n" +" -h, --help Show this help.\n" +" -v, --version Show versioning information.\n" +"\n" +"Address Selection\n" +" -a, --local=ADDR Local address.\n" +" -d, --dev=DEV Associated network device.\n" +" --family=FAMILY Family of local address.\n" +" --label=STRING Address label (IPv4).\n" +" --peer=ADDR Peer address (IPv4).\n" +" --scope=SCOPE Address scope (IPv4).\n" +" --broadcast=ADDR Broadcast address of network (IPv4).\n" +" --valid-lifetime=TS Valid lifetime before route expires (IPv6).\n" +" --preferred=TIME Preferred lifetime (IPv6).\n" +" --valid=TIME Valid lifetime (IPv6).\n" + ); + exit(0); +} + +static char *prefix; + +static void print_prefix(struct nl_dump_params *p, int line) +{ + if (prefix) + nl_dump(p, "%s", prefix); +} + +static void env_dump(struct nl_object *obj, void *arg) +{ + struct nl_dump_params *p = arg; + struct rtnl_addr *addr = (struct rtnl_addr *) obj; + struct nl_cache *link_cache; + struct nl_addr *a; + static int index = 0; + char buf[128], pfx[32], *s; + + snprintf(pfx, sizeof(pfx), "ADDR%d", index++); + + nl_dump_line(p, "%s_FAMILY=%s\n", pfx, + nl_af2str(rtnl_addr_get_family(addr), buf, sizeof(buf))); + + nl_dump_line(p, "%s_LOCAL=%s\n", pfx, + nl_addr2str(rtnl_addr_get_local(addr), buf, sizeof(buf))); + + nl_dump_line(p, "%s_IFINDEX=%u\n", pfx, rtnl_addr_get_ifindex(addr)); + link_cache = nl_cache_mngt_require_safe("route/link"); + if (link_cache) + nl_dump_line(p, "%s_IFNAME=%s\n", pfx, + rtnl_link_i2name(link_cache, + rtnl_addr_get_ifindex(addr), + buf, sizeof(buf))); + + if ((a = rtnl_addr_get_peer(addr))) + nl_dump_line(p, "%s_PEER=%s\n", pfx, + nl_addr2str(a, buf, sizeof(buf))); + + if ((a = rtnl_addr_get_broadcast(addr))) + nl_dump_line(p, "%s_BROADCAST=%s\n", pfx, + nl_addr2str(a, buf, sizeof(buf))); + + nl_dump_line(p, "%s_SCOPE=%s\n", pfx, + rtnl_scope2str(rtnl_addr_get_scope(addr), + buf, sizeof(buf))); + + if ((s = rtnl_addr_get_label(addr))) + nl_dump_line(p, "%s_LABEL=%s\n", pfx, s); + + rtnl_addr_flags2str(rtnl_addr_get_flags(addr), buf, sizeof(buf)); + if (buf[0]) + nl_dump_line(p, "%s_FLAGS=%s\n", pfx, buf); + + nl_dump_line(p, "%s_CACHEINFO_VALID=%u\n", pfx, + rtnl_addr_get_valid_lifetime(addr)); + + if (link_cache) + nl_cache_put(link_cache); + +#if 0 + if (addr->ce_mask & ADDR_ATTR_CACHEINFO) { + struct rtnl_addr_cacheinfo *ci = &addr->a_cacheinfo; + + nl_dump_line(p, "ADDR_CACHEINFO_PREFERRED=%u\n", + ci->aci_prefered); + + nl_dump_line(p, "ADDR_CACHEINFO_CREATED=%u\n", ci->aci_cstamp); + nl_dump_line(p, "ADDR_CACHEINFO_LASTUPDATE=%u\n", + ci->aci_tstamp); + } +#endif +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct rtnl_addr *addr; + struct nl_cache *link_cache, *addr_cache; + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_nl_cb = print_prefix, + .dp_fd = stdout, + }; + int dump_env = 0; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + addr_cache = nl_cli_addr_alloc_cache(sock); + addr = nl_cli_addr_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_FAMILY = 257, + ARG_LABEL = 258, + ARG_PEER, + ARG_SCOPE, + ARG_BROADCAST, + ARG_DETAILS, + ARG_ENV, + ARG_PREFIX, + ARG_PREFERRED, + ARG_VALID, + }; + static struct option long_opts[] = { + { "details", 0, 0, ARG_DETAILS }, + { "env", 0, 0, ARG_ENV }, + { "prefix", 1, 0, ARG_PREFIX }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "local", 1, 0, 'a' }, + { "dev", 1, 0, 'd' }, + { "family", 1, 0, ARG_FAMILY }, + { "label", 1, 0, ARG_LABEL }, + { "peer", 1, 0, ARG_PEER }, + { "scope", 1, 0, ARG_SCOPE }, + { "broadcast", 1, 0, ARG_BROADCAST }, + { "preferred", 1, 0, ARG_PREFERRED }, + { "valid", 1, 0, ARG_VALID }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "46hva:d:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case '?': exit(NLE_INVAL); + case '4': rtnl_addr_set_family(addr, AF_INET); break; + case '6': rtnl_addr_set_family(addr, AF_INET6); break; + case ARG_DETAILS: params.dp_type = NL_DUMP_DETAILS; break; + case ARG_ENV: dump_env = 1; break; + case ARG_PREFIX: prefix = strdup(optarg); break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'a': nl_cli_addr_parse_local(addr, optarg); break; + case 'd': nl_cli_addr_parse_dev(addr, link_cache, optarg); break; + case ARG_FAMILY: nl_cli_addr_parse_family(addr, optarg); break; + case ARG_LABEL: nl_cli_addr_parse_label(addr, optarg); break; + case ARG_PEER: nl_cli_addr_parse_peer(addr, optarg); break; + case ARG_SCOPE: nl_cli_addr_parse_scope(addr, optarg); break; + case ARG_BROADCAST: nl_cli_addr_parse_broadcast(addr, optarg); break; + case ARG_PREFERRED: nl_cli_addr_parse_preferred(addr, optarg); break; + case ARG_VALID: nl_cli_addr_parse_valid(addr, optarg); break; + } + } + + if (dump_env) + nl_cache_foreach_filter(addr_cache, OBJ_CAST(addr), env_dump, + ¶ms); + else + nl_cache_dump_filter(addr_cache, ¶ms, OBJ_CAST(addr)); + + return 0; +} diff --git a/libnl/src/nl-class-add.c b/libnl/src/nl-class-add.c new file mode 100644 index 0000000..ac37daa --- /dev/null +++ b/libnl/src/nl-class-add.c @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include + +#include "nl-priv-dynamic-route/nl-priv-dynamic-route.h" + +static int quiet = 0; + +static void print_usage(void) +{ + printf( +"Usage: nl-class-add [OPTIONS]... class [CONFIGURATION]...\n" +"\n" +"OPTIONS\n" +" -q, --quiet Do not print informal notifications.\n" +" -h, --help Show this help text.\n" +" -v, --version Show versioning information.\n" +" --update Update class if it exists.\n" +" --update-only Only update class, never create it.\n" +" -d, --dev=DEV Network device the class should be attached to.\n" +" -i, --id=ID ID of new class (default: auto-generated)\n" +" -p, --parent=ID ID of parent { root | ingress | class-ID }\n" +" --mtu=SIZE Overwrite MTU (default: MTU of network device)\n" +" --mpu=SIZE Minimum packet size on the link (default: 0).\n" +" --overhead=SIZE Overhead in bytes per packet (default: 0).\n" +" --linktype=TYPE Overwrite linktype (default: type of network device)\n" +"\n" +"CONFIGURATION\n" +" -h, --help Show help text of class specific options.\n" +"\n" +"EXAMPLE\n" +" $ nl-class-add --dev=eth1 --parent=root htb --rate=100mbit\n" +"\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct rtnl_class *class; + struct rtnl_tc *tc; + struct nl_cache *link_cache; + struct nl_dump_params dp = { + .dp_type = NL_DUMP_DETAILS, + .dp_fd = stdout, + }; + struct nl_cli_tc_module *tm; + struct rtnl_tc_ops *ops; + int err, flags = NLM_F_CREATE | NLM_F_EXCL; + char *kind, *id = NULL; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + + link_cache = nl_cli_link_alloc_cache(sock); + + class = nl_cli_class_alloc(); + tc = (struct rtnl_tc *) class; + + for (;;) { + int c, optidx = 0; + enum { + ARG_UPDATE = 257, + ARG_UPDATE_ONLY = 258, + ARG_MTU, + ARG_MPU, + ARG_OVERHEAD, + ARG_LINKTYPE, + }; + static struct option long_opts[] = { + { "quiet", 0, 0, 'q' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "dev", 1, 0, 'd' }, + { "parent", 1, 0, 'p' }, + { "id", 1, 0, 'i' }, + { "update", 0, 0, ARG_UPDATE }, + { "update-only", 0, 0, ARG_UPDATE_ONLY }, + { "mtu", 1, 0, ARG_MTU }, + { "mpu", 1, 0, ARG_MPU }, + { "overhead", 1, 0, ARG_OVERHEAD }, + { "linktype", 1, 0, ARG_LINKTYPE }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "+qhvd:p:i:", + long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'q': quiet = 1; break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'd': nl_cli_tc_parse_dev(tc, link_cache, optarg); break; + case 'p': nl_cli_tc_parse_parent(tc, optarg); break; + case 'i': id = strdup(optarg); break; + case ARG_UPDATE: flags = NLM_F_CREATE; break; + case ARG_UPDATE_ONLY: flags = 0; break; + case ARG_MTU: nl_cli_tc_parse_mtu(tc, optarg); break; + case ARG_MPU: nl_cli_tc_parse_mpu(tc, optarg); break; + case ARG_OVERHEAD: nl_cli_tc_parse_overhead(tc, optarg); break; + case ARG_LINKTYPE: nl_cli_tc_parse_linktype(tc, optarg); break; + } + } + + if (optind >= argc) + print_usage(); + + if (!rtnl_tc_get_ifindex(tc)) + nl_cli_fatal(EINVAL, "You must specify a network device (--dev=XXX)"); + + if (!rtnl_tc_get_parent(tc)) + nl_cli_fatal(EINVAL, "You must specify a parent (--parent=XXX)"); + + if (id) { + nl_cli_tc_parse_handle(tc, id, 1); + free(id); + } + + kind = argv[optind++]; + rtnl_tc_set_kind(tc, kind); + + if (!(ops = rtnl_tc_get_ops(tc))) + nl_cli_fatal(ENOENT, "Unknown class \"%s\"", kind); + + if (!(tm = nl_cli_tc_lookup(ops))) + nl_cli_fatal(ENOTSUP, "class type \"%s\" not supported.", kind); + + tm->tm_parse_argv(tc, argc, argv); + + if (!quiet) { + printf("Adding "); + nl_object_dump(OBJ_CAST(class), &dp); + } + + if ((err = rtnl_class_add(sock, class, flags)) < 0) + nl_cli_fatal(EINVAL, "Unable to add class: %s", nl_geterror(err)); + + return 0; +} diff --git a/libnl/src/nl-class-delete.c b/libnl/src/nl-class-delete.c new file mode 100644 index 0000000..88dc59e --- /dev/null +++ b/libnl/src/nl-class-delete.c @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +static int quiet = 0, default_yes = 0, deleted = 0, interactive = 0; +static struct nl_sock *sock; + +static void print_usage(void) +{ + printf( + "Usage: nl-class-delete [OPTION]... [class]\n" + "\n" + "OPTIONS\n" + " --interactive Run interactively.\n" + " --yes Set default answer to yes.\n" + " -q, --quiet Do not print informal notifications.\n" + " -h, --help Show this help text and exit.\n" + " -v, --version Show versioning information and exit.\n" + "\n" + " -d, --dev=DEV Device the class is attached to.\n" + " -p, --parent=ID Identifier of parent qdisc/class.\n" + " -i, --id=ID Identifier\n" + " -k, --kind=NAME Kind of class (e.g. pfifo_fast)\n" + "\n" + "EXAMPLE\n" + " # Delete all classes on eth0 attached to parent 1:\n" + " $ nl-class-delete --dev eth0 --parent 1:\n" + "\n" + ); + + exit(0); +} + +static void delete_cb(struct nl_object *obj, void *arg) +{ + struct rtnl_class *class = nl_object_priv(obj); + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + int err; + + if (interactive && !nl_cli_confirm(obj, ¶ms, default_yes)) + return; + + if ((err = rtnl_class_delete(sock, class)) < 0) + nl_cli_fatal(err, "Unable to delete class: %s\n", nl_geterror(err)); + + if (!quiet) { + printf("Deleted "); + nl_object_dump(obj, ¶ms); + } + + deleted++; +} + +int main(int argc, char *argv[]) +{ + struct rtnl_class *class; + struct rtnl_tc *tc; + struct nl_cache *link_cache, *class_cache; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + class = nl_cli_class_alloc(); + tc = (struct rtnl_tc *) class; + + for (;;) { + int c, optidx = 0; + enum { + ARG_YES = 257, + ARG_INTERACTIVE = 258, + }; + static struct option long_opts[] = { + { "interactive", 0, 0, ARG_INTERACTIVE }, + { "yes", 0, 0, ARG_YES }, + { "quiet", 0, 0, 'q' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "dev", 1, 0, 'd' }, + { "parent", 1, 0, 'p' }, + { "id", 1, 0, 'i' }, + { "kind", 1, 0, 'k' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "qhvd:p:i:k:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case '?': nl_cli_fatal(EINVAL, "Invalid options"); + case ARG_INTERACTIVE: interactive = 1; break; + case ARG_YES: default_yes = 1; break; + case 'q': quiet = 1; break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'd': nl_cli_tc_parse_dev(tc, link_cache, optarg); break; + case 'p': nl_cli_tc_parse_parent(tc, optarg); break; + case 'i': nl_cli_tc_parse_handle(tc, optarg, 0); break; + case 'k': nl_cli_tc_parse_kind(tc, optarg); break; + } + } + + if (!rtnl_tc_get_ifindex(tc)) + nl_cli_fatal(EINVAL, "You must specify a network device (--dev=XXX)"); + + class_cache = nl_cli_class_alloc_cache(sock, rtnl_tc_get_ifindex(tc)); + + nl_cache_foreach_filter(class_cache, OBJ_CAST(class), delete_cb, NULL); + + if (!quiet) + printf("Deleted %d classs\n", deleted); + + return 0; +} diff --git a/libnl/src/nl-class-list.c b/libnl/src/nl-class-list.c new file mode 100644 index 0000000..8b5cd6c --- /dev/null +++ b/libnl/src/nl-class-list.c @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include + +static struct nl_sock *sock; + +static struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, +}; + +static void print_usage(void) +{ + printf( + "Usage: nl-class-list [OPTION]...\n" + "\n" + "OPTIONS\n" + " --details Show details\n" + " --stats Show statistics\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + "\n" + " -d, --dev=DEV Device the class is attached to. (default: all)\n" + " -p, --parent=ID Identifier of parent class.\n" + " -i, --id=ID Identifier.\n" + " -k, --kind=NAME Kind of class (e.g. pfifo_fast)\n" + "\n" + "EXAMPLE\n" + " # Display statistics of all classes on eth0\n" + " $ nl-class-list --stats --dev=eth0\n" + "\n" + ); + exit(0); +} + +static void __dump_class(int ifindex, struct rtnl_class *filter) +{ + struct nl_cache *cache; + + cache = nl_cli_class_alloc_cache(sock, ifindex); + nl_cache_dump_filter(cache, ¶ms, OBJ_CAST(filter)); +} + +static void dump_class(struct nl_object *obj, void *arg) +{ + struct rtnl_link *link = nl_object_priv(obj); + + __dump_class(rtnl_link_get_ifindex(link), arg); +} + +int main(int argc, char *argv[]) +{ + struct rtnl_class *class; + struct rtnl_tc *tc; + struct nl_cache *link_cache; + int ifindex; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + class = nl_cli_class_alloc(); + tc = (struct rtnl_tc *) class; + + params.dp_fd = stdout; + + for (;;) { + int c, optidx = 0; + enum { + ARG_DETAILS = 257, + ARG_STATS = 258, + }; + static struct option long_opts[] = { + { "details", 0, 0, ARG_DETAILS }, + { "stats", 0, 0, ARG_STATS }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "dev", 1, 0, 'd' }, + { "parent", 1, 0, 'p' }, + { "id", 1, 0, 'i' }, + { "kind", 1, 0, 'k' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "hvd:p:i:k:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case ARG_DETAILS: params.dp_type = NL_DUMP_DETAILS; break; + case ARG_STATS: params.dp_type = NL_DUMP_STATS; break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'd': nl_cli_tc_parse_dev(tc, link_cache, optarg); break; + case 'p': nl_cli_tc_parse_parent(tc, optarg); break; + case 'i': nl_cli_tc_parse_handle(tc, optarg, 0); break; + case 'k': nl_cli_tc_parse_kind(tc, optarg); break; + } + } + + if ((ifindex = rtnl_tc_get_ifindex(tc))) + __dump_class(ifindex, class); + else + nl_cache_foreach(link_cache, dump_class, class); + + return 0; +} diff --git a/libnl/src/nl-classid-lookup.c b/libnl/src/nl-classid-lookup.c new file mode 100644 index 0000000..397fcb4 --- /dev/null +++ b/libnl/src/nl-classid-lookup.c @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include + +static void print_usage(void) +{ + printf( +"Usage: nl-classid-lookup [OPTIONS]... NAME\n" +"\n" +"OPTIONS\n" +" -h, --help Show this help text.\n" +" -v, --version Show versioning information.\n" +" -r, --reverse Do a reverse lookup, i.e. classid to name.\n" +" --raw Print the raw classid, not pretty printed.\n" +"\n" +"EXAMPLE\n" +" $ nl-classid-lookup low_latency\n" +" $ nl-classid-lookup -r 1:12\n" +"\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + uint32_t classid; + char *name; + int err, reverse = 0, raw = 0; + + for (;;) { + int c, optidx = 0; + enum { + ARG_RAW = 257, + }; + static struct option long_opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "reverse", 0, 0, 'r' }, + { "raw", 0, 0, ARG_RAW }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "hvr", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'r': reverse = 1; break; + case ARG_RAW: raw = 1; break; + } + } + + if (optind >= argc) + print_usage(); + + name = argv[optind++]; + + /* + * We use rtnl_tc_str2handle() even while doing a reverse lookup. This + * allows for name -> name lookups. This is intentional, it does not + * do any harm and avoids duplicating a lot of code. + */ + if ((err = rtnl_tc_str2handle(name, &classid)) < 0) + nl_cli_fatal(err, "Unable to lookup classid \"%s\": %s", + name, nl_geterror(err)); + + if (reverse) { + char buf[64]; + printf("%s\n", rtnl_tc_handle2str(classid, buf, sizeof(buf))); + } else if (raw) + printf("%#x\n", classid); + else + printf("%x:%x\n", TC_H_MAJ(classid) >> 16, TC_H_MIN(classid)); + + return 0; +} diff --git a/libnl/src/nl-cls-add.c b/libnl/src/nl-cls-add.c new file mode 100644 index 0000000..b583872 --- /dev/null +++ b/libnl/src/nl-cls-add.c @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include + +#include "nl-priv-dynamic-route/nl-priv-dynamic-route.h" + +static int quiet = 0; + +static void print_usage(void) +{ + printf( +"Usage: nl-cls-add [OPTIONS]... classifier [CONFIGURATION]...\n" +"\n" +"OPTIONS\n" +" -q, --quiet Do not print informal notifications.\n" +" -h, --help Show this help text.\n" +" -v, --version Show versioning information.\n" +" --update Update classifier if it exists.\n" +" --update-only Only update classifier, never create it.\n" +" -d, --dev=DEV Network device the classifier should be attached to.\n" +" -i, --id=ID ID of new classifier (default: auto-generated)\n" +" -p, --parent=ID ID of parent { root | ingress | class-ID }\n" +" --proto=PROTO Protocol to match (default: all)\n" +" --prio=PRIO Priority (default: 0)\n" +" --mtu=SIZE Overwrite MTU (default: MTU of network device)\n" +" --mpu=SIZE Minimum packet size on the link (default: 0).\n" +" --overhead=SIZE Overhead in bytes per packet (default: 0).\n" +" --linktype=TYPE Overwrite linktype (default: type of network device)\n" +"\n" +"CONFIGURATION\n" +" -h, --help Show help text of classifier specific options.\n" +"\n" +"EXAMPLE\n" +" $ nl-cls-add --dev=eth1 --parent=q_root basic --target c_www\n" +"\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct rtnl_cls *cls; + struct rtnl_tc *tc; + struct nl_cache *link_cache; + struct nl_dump_params dp = { + .dp_type = NL_DUMP_DETAILS, + .dp_fd = stdout, + }; + struct nl_cli_tc_module *tm; + struct rtnl_tc_ops *ops; + int err, flags = NLM_F_CREATE | NLM_F_EXCL; + char *kind, *id = NULL; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + + link_cache = nl_cli_link_alloc_cache(sock); + + cls = nl_cli_cls_alloc(); + tc = (struct rtnl_tc *) cls; + + for (;;) { + int c, optidx = 0; + enum { + ARG_UPDATE = 257, + ARG_UPDATE_ONLY = 258, + ARG_MTU, + ARG_MPU, + ARG_OVERHEAD, + ARG_LINKTYPE, + ARG_PROTO, + ARG_PRIO, + }; + static struct option long_opts[] = { + { "quiet", 0, 0, 'q' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "dev", 1, 0, 'd' }, + { "parent", 1, 0, 'p' }, + { "id", 1, 0, 'i' }, + { "proto", 1, 0, ARG_PROTO }, + { "prio", 1, 0, ARG_PRIO }, + { "update", 0, 0, ARG_UPDATE }, + { "update-only", 0, 0, ARG_UPDATE_ONLY }, + { "mtu", 1, 0, ARG_MTU }, + { "mpu", 1, 0, ARG_MPU }, + { "overhead", 1, 0, ARG_OVERHEAD }, + { "linktype", 1, 0, ARG_LINKTYPE }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "+qhvd:p:i:", + long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'q': quiet = 1; break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'd': nl_cli_tc_parse_dev(tc, link_cache, optarg); break; + case 'p': nl_cli_tc_parse_parent(tc, optarg); break; + case 'i': free(id); id = strdup(optarg); break; + case ARG_UPDATE: flags = NLM_F_CREATE; break; + case ARG_UPDATE_ONLY: flags = 0; break; + case ARG_MTU: nl_cli_tc_parse_mtu(tc, optarg); break; + case ARG_MPU: nl_cli_tc_parse_mpu(tc, optarg); break; + case ARG_OVERHEAD: nl_cli_tc_parse_overhead(tc, optarg); break; + case ARG_LINKTYPE: nl_cli_tc_parse_linktype(tc, optarg); break; + case ARG_PROTO: nl_cli_cls_parse_proto(cls, optarg); break; + case ARG_PRIO: + rtnl_cls_set_prio(cls, nl_cli_parse_u32(optarg)); + break; + } + } + + if (optind >= argc) + print_usage(); + + if (!rtnl_tc_get_ifindex(tc)) + nl_cli_fatal(EINVAL, "You must specify a network device (--dev=XXX)"); + + if (!rtnl_tc_get_parent(tc)) + nl_cli_fatal(EINVAL, "You must specify a parent (--parent=XXX)"); + + if (id) { + nl_cli_tc_parse_handle(tc, id, 1); + free(id); + } + + kind = argv[optind++]; + rtnl_tc_set_kind(tc, kind); + + if (!(ops = rtnl_tc_get_ops(tc))) + nl_cli_fatal(ENOENT, "Unknown classifier \"%s\".", kind); + + if (!(tm = nl_cli_tc_lookup(ops))) + nl_cli_fatal(ENOTSUP, "Classifier type \"%s\" not supported.", kind); + + tm->tm_parse_argv(tc, argc, argv); + + if (!quiet) { + printf("Adding "); + nl_object_dump(OBJ_CAST(cls), &dp); + } + + if ((err = rtnl_cls_add(sock, cls, flags)) < 0) + nl_cli_fatal(EINVAL, "Unable to add classifier: %s", nl_geterror(err)); + + return 0; +} diff --git a/libnl/src/nl-cls-delete.c b/libnl/src/nl-cls-delete.c new file mode 100644 index 0000000..ca8e370 --- /dev/null +++ b/libnl/src/nl-cls-delete.c @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2010 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +static int quiet = 0, default_yes = 0, deleted = 0, interactive = 0; +static struct nl_sock *sock; + +static void print_usage(void) +{ + printf( +"Usage: nl-cls-delete [OPTION]... [class]\n" +"\n" +"OPTIONS\n" +" --interactive Run interactively.\n" +" --yes Set default answer to yes.\n" +" -q, --quiet Do not print informal notifications.\n" +" -h, --help Show this help text and exit.\n" +" -v, --version Show versioning information and exit.\n" +"\n" +" -d, --dev=DEV Device the classifer is attached to.\n" +" -p, --parent=ID Identifier of parent qdisc/class.\n" +" -i, --id=ID Identifier\n" +" -k, --kind=NAME Kind of classifier (e.g. basic, u32, fw)\n" +" --proto=PROTO Protocol to match (default: all)\n" +" --prio=PRIO Priority (default: 0)\n" +"\n" +"EXAMPLE\n" +" # Delete all classifiers on eth0 attached to parent q_root:\n" +" $ nl-cls-delete --dev eth0 --parent q_root:\n" +"\n" + ); + + exit(0); +} + +static void delete_cb(struct nl_object *obj, void *arg) +{ + struct rtnl_cls *cls = nl_object_priv(obj); + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + int err; + + if (interactive && !nl_cli_confirm(obj, ¶ms, default_yes)) + return; + + if ((err = rtnl_cls_delete(sock, cls, 0)) < 0) + nl_cli_fatal(err, "Unable to delete classifier: %s\n", + nl_geterror(err)); + + if (!quiet) { + printf("Deleted "); + nl_object_dump(obj, ¶ms); + } + + deleted++; +} + +static void __delete_link(int ifindex, struct rtnl_cls *filter) +{ + struct nl_cache *cache; + uint32_t parent = rtnl_tc_get_parent((struct rtnl_tc *) filter); + + cache = nl_cli_cls_alloc_cache(sock, ifindex, parent); + nl_cache_foreach_filter(cache, OBJ_CAST(filter), delete_cb, NULL); + nl_cache_free(cache); +} + +static void delete_link(struct nl_object *obj, void *arg) +{ + struct rtnl_link *link = nl_object_priv(obj); + + __delete_link(rtnl_link_get_ifindex(link), arg); +} + +int main(int argc, char *argv[]) +{ + struct rtnl_cls *cls; + struct rtnl_tc *tc; + struct nl_cache *link_cache; + int ifindex; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + cls = nl_cli_cls_alloc(); + tc = (struct rtnl_tc *) cls; + + for (;;) { + int c, optidx = 0; + enum { + ARG_YES = 257, + ARG_INTERACTIVE = 258, + ARG_PROTO, + ARG_PRIO, + }; + static struct option long_opts[] = { + { "interactive", 0, 0, ARG_INTERACTIVE }, + { "yes", 0, 0, ARG_YES }, + { "quiet", 0, 0, 'q' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "dev", 1, 0, 'd' }, + { "parent", 1, 0, 'p' }, + { "id", 1, 0, 'i' }, + { "kind", 1, 0, 'k' }, + { "proto", 1, 0, ARG_PROTO }, + { "prio", 1, 0, ARG_PRIO }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "qhvd:p:i:k:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case '?': nl_cli_fatal(EINVAL, "Invalid options"); + case ARG_INTERACTIVE: interactive = 1; break; + case ARG_YES: default_yes = 1; break; + case 'q': quiet = 1; break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'd': nl_cli_tc_parse_dev(tc, link_cache, optarg); break; + case 'p': nl_cli_tc_parse_parent(tc, optarg); break; + case 'i': nl_cli_tc_parse_handle(tc, optarg, 0); break; + case 'k': nl_cli_tc_parse_kind(tc, optarg); break; + case ARG_PROTO: nl_cli_cls_parse_proto(cls, optarg); break; + case ARG_PRIO: + rtnl_cls_set_prio(cls, nl_cli_parse_u32(optarg)); + break; + } + } + + if ((ifindex = rtnl_tc_get_ifindex(tc))) + __delete_link(ifindex, cls); + else + nl_cache_foreach(link_cache, delete_link, cls); + + if (!quiet) + printf("Deleted %d classs\n", deleted); + + return 0; +} diff --git a/libnl/src/nl-cls-list.c b/libnl/src/nl-cls-list.c new file mode 100644 index 0000000..5a4e638 --- /dev/null +++ b/libnl/src/nl-cls-list.c @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2010 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include + +static struct nl_sock *sock; + +static struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, +}; + +static void print_usage(void) +{ + printf( +"Usage: nl-cls-list [OPTION]...\n" +"\n" +"OPTIONS\n" +" --details Show details\n" +" --stats Show statistics\n" +" -h, --help Show this help\n" +" -v, --version Show versioning information\n" +"\n" +" -d, --dev=DEV Device the classifier is attached to. (default: all)\n" +" -p, --parent=ID Identifier of parent class.\n" +" -i, --id=ID Identifier.\n" +" -k, --kind=NAME Kind of classifier (e.g. basic, u32, fw)\n" +" --proto=PROTO Protocol to match (default: all)\n" +" --prio=PRIO Priority (default: 0)\n" +"\n" +"EXAMPLE\n" +" # Display statistics of all classes on eth0\n" +" $ nl-cls-list --stats --dev=eth0\n" +"\n" + ); + exit(0); +} + +static void __dump_link(int ifindex, struct rtnl_cls *filter) +{ + struct nl_cache *cache; + uint32_t parent = rtnl_tc_get_parent((struct rtnl_tc *) filter); + + cache = nl_cli_cls_alloc_cache(sock, ifindex, parent); + nl_cache_dump_filter(cache, ¶ms, OBJ_CAST(filter)); + nl_cache_free(cache); +} + +static void dump_link(struct nl_object *obj, void *arg) +{ + struct rtnl_link *link = nl_object_priv(obj); + + __dump_link(rtnl_link_get_ifindex(link), arg); +} + +int main(int argc, char *argv[]) +{ + struct rtnl_cls *cls; + struct rtnl_tc *tc; + struct nl_cache *link_cache; + int ifindex; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + cls = nl_cli_cls_alloc(); + tc = (struct rtnl_tc *) cls; + + params.dp_fd = stdout; + + for (;;) { + int c, optidx = 0; + enum { + ARG_DETAILS = 257, + ARG_STATS = 258, + ARG_PROTO, + ARG_PRIO, + }; + static struct option long_opts[] = { + { "details", 0, 0, ARG_DETAILS }, + { "stats", 0, 0, ARG_STATS }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "dev", 1, 0, 'd' }, + { "parent", 1, 0, 'p' }, + { "id", 1, 0, 'i' }, + { "kind", 1, 0, 'k' }, + { "proto", 1, 0, ARG_PROTO }, + { "prio", 1, 0, ARG_PRIO }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "hvd:p:i:k:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case ARG_DETAILS: params.dp_type = NL_DUMP_DETAILS; break; + case ARG_STATS: params.dp_type = NL_DUMP_STATS; break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'd': nl_cli_tc_parse_dev(tc, link_cache, optarg); break; + case 'p': nl_cli_tc_parse_parent(tc, optarg); break; + case 'i': nl_cli_tc_parse_handle(tc, optarg, 0); break; + case 'k': nl_cli_tc_parse_kind(tc, optarg); break; + case ARG_PROTO: nl_cli_cls_parse_proto(cls, optarg); break; + case ARG_PRIO: + rtnl_cls_set_prio(cls, nl_cli_parse_u32(optarg)); + break; + } + } + + if ((ifindex = rtnl_tc_get_ifindex(tc))) + __dump_link(ifindex, cls); + else + nl_cache_foreach(link_cache, dump_link, cls); + + return 0; +} diff --git a/libnl/src/nl-fib-lookup.c b/libnl/src/nl-fib-lookup.c new file mode 100644 index 0000000..a1470c7 --- /dev/null +++ b/libnl/src/nl-fib-lookup.c @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include + +static void print_usage(void) +{ + printf( + "Usage: nl-fib-lookup [options] \n" + "Options:\n" + " -t, --table Table id\n" + " -f, --fwmark Firewall mark\n" + " -s, --scope Routing scope\n" + " -T, --tos Type of Service\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *nlh; + struct nl_cache *result; + struct flnl_request *request; + struct nl_addr *addr; + struct nl_dump_params params = { + .dp_fd = stdout, + .dp_type = NL_DUMP_DETAILS, + }; + int table = RT_TABLE_UNSPEC, scope = RT_SCOPE_UNIVERSE; + int tos = 0, err = 1; + uint64_t fwmark = 0; + + while (1) { + static struct option long_opts[] = { + {"table", 1, 0, 't'}, + {"fwmark", 1, 0, 'f'}, + {"scope", 1, 0, 's'}, + {"tos", 1, 0, 'T'}, + {"help", 0, 0, 'h'}, + {0, 0, 0, 0}, + }; + int c, idx = 0; + + c = getopt_long(argc, argv, "t:f:s:T:h", long_opts, &idx); + if (c == -1) + break; + + switch (c) { + case 't': + table = strtoul(optarg, NULL, 0); + break; + case 'f': + fwmark = strtoul(optarg, NULL, 0); + break; + case 's': + scope = strtoul(optarg, NULL, 0); + break; + case 'T': + tos = strtoul(optarg, NULL, 0); + break; + default: + print_usage(); + } + } + + if (optind >= argc) + print_usage(); + + nlh = nl_cli_alloc_socket(); + + if ((err = nl_addr_parse(argv[optind], AF_INET, &addr)) < 0) + nl_cli_fatal(err, "Unable to parse address \"%s\": %s\n", + argv[optind], nl_geterror(err)); + + result = flnl_result_alloc_cache(); + if (!result) + nl_cli_fatal(ENOMEM, "Unable to allocate cache"); + + request = flnl_request_alloc(); + if (!request) + nl_cli_fatal(ENOMEM, "Unable to allocate request"); + + flnl_request_set_table(request, table); + flnl_request_set_fwmark(request, fwmark); + flnl_request_set_scope(request, scope); + flnl_request_set_tos(request, tos); + + err = flnl_request_set_addr(request, addr); + nl_addr_put(addr); + if (err < 0) + nl_cli_fatal(err, "Unable to send request: %s", nl_geterror(err)); + + nl_cli_connect(nlh, NETLINK_FIB_LOOKUP); + + err = flnl_lookup(nlh, request, result); + if (err < 0) + nl_cli_fatal(err, "Unable to lookup: %s\n", nl_geterror(err)); + + nl_cache_dump(result, ¶ms); + + return 0; +} diff --git a/libnl/src/nl-link-enslave.c b/libnl/src/nl-link-enslave.c new file mode 100644 index 0000000..5c6dc02 --- /dev/null +++ b/libnl/src/nl-link-enslave.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2011 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nl_cache *link_cache; + struct rtnl_link *master, *slave; + int err; + + if (argc < 3) { + fprintf(stderr, "Usage: nl-link-enslave master slave\n"); + return 1; + } + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + + if (!(master = rtnl_link_get_by_name(link_cache, argv[1]))) { + fprintf(stderr, "Unknown link: %s\n", argv[1]); + return 1; + } + + if (!(slave = rtnl_link_get_by_name(link_cache, argv[2]))) { + fprintf(stderr, "Unknown link: %s\n", argv[2]); + return 1; + } + + if ((err = rtnl_link_bond_enslave(sock, master, slave)) < 0) { + fprintf(stderr, "Unable to enslave %s to %s: %s\n", + argv[2], argv[1], nl_geterror(err)); + return 1; + } + + return 0; +} + diff --git a/libnl/src/nl-link-ifindex2name.c b/libnl/src/nl-link-ifindex2name.c new file mode 100644 index 0000000..200c9f2 --- /dev/null +++ b/libnl/src/nl-link-ifindex2name.c @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include + +static void print_usage(void) +{ + printf("Usage: nl-link-ifindex2name \n"); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nl_cache *link_cache; + char name[IFNAMSIZ]; + uint32_t ifindex; + + if (argc < 2) + print_usage(); + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + + ifindex = nl_cli_parse_u32(argv[1]); + + if (!rtnl_link_i2name(link_cache, ifindex, name, sizeof(name))) + nl_cli_fatal(ENOENT, "Interface index %d does not exist", + ifindex); + + printf("%s\n", name); + + return 0; +} diff --git a/libnl/src/nl-link-list.c b/libnl/src/nl-link-list.c new file mode 100644 index 0000000..aec96cb --- /dev/null +++ b/libnl/src/nl-link-list.c @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2010 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include + +static void print_usage(void) +{ + printf( +"Usage: nl-link-list [OPTIONS]... \n" +"\n" +"OPTIONS\n" +" --details Show detailed information of each link\n" +" --stats Show statistics, implies --details\n" +" -h, --help Show this help text.\n" +" -v, --version Show versioning information.\n" +"\n" +" -n, --name=NAME Name of link\n" +" -i, --index Interface index (unique identifier)\n" +" --family=NAME Link address family\n" +" --mtu=NUM MTU value\n" +" --txqlen=NUM TX queue length\n" +" --weight=NUM Weight\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nl_cache *link_cache; + struct rtnl_link *link; + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link = nl_cli_link_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_FAMILY = 257, + ARG_MTU = 258, + ARG_TXQLEN, + ARG_WEIGHT, + ARG_DETAILS, + ARG_STATS, + }; + static struct option long_opts[] = { + { "details", 0, 0, ARG_DETAILS }, + { "stats", 0, 0, ARG_STATS }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "name", 1, 0, 'n' }, + { "index", 1, 0, 'i' }, + { "family", 1, 0, ARG_FAMILY }, + { "mtu", 1, 0, ARG_MTU }, + { "txqlen", 1, 0, ARG_TXQLEN }, + { "weight", 1, 0, ARG_WEIGHT }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "hvn:i:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case ARG_DETAILS: params.dp_type = NL_DUMP_DETAILS; break; + case ARG_STATS: params.dp_type = NL_DUMP_STATS; break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'n': nl_cli_link_parse_name(link, optarg); break; + case 'i': nl_cli_link_parse_ifindex(link, optarg); break; + case ARG_FAMILY: nl_cli_link_parse_family(link, optarg); break; + case ARG_MTU: nl_cli_link_parse_mtu(link, optarg); break; + case ARG_TXQLEN: nl_cli_link_parse_txqlen(link, optarg); break; + case ARG_WEIGHT: nl_cli_link_parse_weight(link, optarg); break; + } + } + + link_cache = nl_cli_link_alloc_cache_family(sock, + rtnl_link_get_family(link)); + + nl_cache_dump_filter(link_cache, ¶ms, OBJ_CAST(link)); + + return 0; +} diff --git a/libnl/src/nl-link-name2ifindex.c b/libnl/src/nl-link-name2ifindex.c new file mode 100644 index 0000000..e35ee27 --- /dev/null +++ b/libnl/src/nl-link-name2ifindex.c @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include + +static void print_usage(void) +{ + printf("Usage: nl-link-name2ifindex \n"); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nl_cache *link_cache; + uint32_t ifindex; + + if (argc < 2) + print_usage(); + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + + if (!(ifindex = rtnl_link_name2i(link_cache, argv[1]))) + nl_cli_fatal(ENOENT, "Interface \"%s\" does not exist", + argv[1]); + + printf("%u\n", ifindex); + + return 0; +} diff --git a/libnl/src/nl-link-release.c b/libnl/src/nl-link-release.c new file mode 100644 index 0000000..9d04413 --- /dev/null +++ b/libnl/src/nl-link-release.c @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2011 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nl_cache *link_cache; + struct rtnl_link *slave; + int err; + + if (argc < 2) { + fprintf(stderr, "Usage: nl-link-release slave\n"); + return 1; + } + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + + if (!(slave = rtnl_link_get_by_name(link_cache, argv[1]))) { + fprintf(stderr, "Unknown link: %s\n", argv[1]); + return 1; + } + + if ((err = rtnl_link_bond_release(sock, slave)) < 0) { + fprintf(stderr, "Unable to release slave %s: %s\n", + argv[1], nl_geterror(err)); + return 1; + } + + return 0; +} + diff --git a/libnl/src/nl-link-set.c b/libnl/src/nl-link-set.c new file mode 100644 index 0000000..c301542 --- /dev/null +++ b/libnl/src/nl-link-set.c @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2010 Thomas Graf + */ + +#include "nl-default.h" + +#include +#include + +#include +#include + +static struct nl_sock *sock; +static int quiet = 0; + +#if 0 + " changes := [link LINK]\n" + " [master MASTER] [qdisc QDISC] [addr ADDR] [broadcast BRD]\n" + " [{ up | down }] [{ arp | noarp }] [{ promisc | nopromisc }]\n" + " [{ dynamic | nodynamic }] [{ multicast | nomulticast }]\n" + " [{ trailers | notrailers }] [{ allmulticast | noallmulticast }]\n"); +#endif + +static void print_usage(void) +{ + printf( + "Usage: nl-link-set [OPTION]... [LINK]\n" + "\n" + "Options\n" + " -q, --quiet Do not print informal notifications\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + "\n" + "Selecting the Link\n" + " -n, --name=NAME link name\n" + " -i, --index interface index\n" + "Change Options\n" + " --rename=NAME rename interface\n" + " --mtu=NUM MTU value\n" + " --txqlen=NUM TX queue length\n" + " --weight=NUM weight\n" + " --ifalias=NAME alias name (SNMP IfAlias)\n" + " --state=up/down set interface up/down\n" + ); + exit(0); +} + +static void set_cb(struct nl_object *obj, void *arg) +{ + struct rtnl_link *link = nl_object_priv(obj); + struct rtnl_link *change = arg; + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + int err; + + if ((err = rtnl_link_change(sock, link, change, 0)) < 0) + nl_cli_fatal(err, "Unable to change link: %s", + nl_geterror(err)); + + if (!quiet) { + printf("Changed "); + nl_object_dump(OBJ_CAST(link), ¶ms); + } +} + +int main(int argc, char *argv[]) +{ + struct nl_cache *link_cache; + struct rtnl_link *link, *change; + int ok = 0; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + link = nl_cli_link_alloc(); + change = nl_cli_link_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_RENAME = 257, + ARG_MTU = 258, + ARG_TXQLEN, + ARG_WEIGHT, + ARG_IFALIAS, + ARG_STATE, + }; + static struct option long_opts[] = { + { "quiet", 0, 0, 'q' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "name", 1, 0, 'n' }, + { "index", 1, 0, 'i' }, + { "rename", 1, 0, ARG_RENAME }, + { "mtu", 1, 0, ARG_MTU }, + { "txqlen", 1, 0, ARG_TXQLEN }, + { "weight", 1, 0, ARG_WEIGHT }, + { "ifalias", 1, 0, ARG_IFALIAS }, + { "state", 1, 0, ARG_STATE }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "qhvn:i:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'q': quiet = 1; break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'n': ok++; nl_cli_link_parse_name(link, optarg); break; + case 'i': ok++; nl_cli_link_parse_ifindex(link, optarg); break; + case ARG_RENAME: nl_cli_link_parse_name(change, optarg); break; + case ARG_MTU: nl_cli_link_parse_mtu(change, optarg); break; + case ARG_TXQLEN: nl_cli_link_parse_txqlen(change, optarg); break; + case ARG_WEIGHT: nl_cli_link_parse_weight(change, optarg); break; + case ARG_IFALIAS: nl_cli_link_parse_ifalias(change, optarg); break; + case ARG_STATE: + if(!strcmp(optarg, "up")) + rtnl_link_set_flags(change, IFF_UP); + else if(!strcmp(optarg, "down")) + rtnl_link_unset_flags(change, IFF_UP); + break; + } + } + + if (!ok) + print_usage(); + + nl_cache_foreach_filter(link_cache, OBJ_CAST(link), set_cb, change); + + return 0; +} diff --git a/libnl/src/nl-link-stats.c b/libnl/src/nl-link-stats.c new file mode 100644 index 0000000..07e5e1c --- /dev/null +++ b/libnl/src/nl-link-stats.c @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include + +static void print_usage(void) +{ + printf( + "Usage: nl-link-stats [OPTION]... [LINK] [ListOfStats]\n" + "\n" + "Options\n" + " -l, --list List available statistic names\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + "\n" + "Link Options\n" + " -n, --name=NAME link name\n" + " -i, --index=NUM interface index\n" + ); + exit(0); +} + +static void list_stat_names(void) +{ + char buf[64]; + int i; + + for (i = 0; i <= RTNL_LINK_STATS_MAX; i++) + printf("%s\n", rtnl_link_stat2str(i, buf, sizeof(buf))); + + exit(0); +} + +static int gargc; + +static void dump_stat(struct rtnl_link *link, int id) +{ + uint64_t st = rtnl_link_get_stat(link, id); + char buf[64]; + + printf("%s.%s %" PRIu64 "\n", rtnl_link_get_name(link), + rtnl_link_stat2str(id, buf, sizeof(buf)), st); +} + +static void dump_stats(struct nl_object *obj, void *arg) +{ + struct rtnl_link *link = (struct rtnl_link *) obj; + char **argv = arg; + + if (optind >= gargc) { + int i; + + for (i = 0; i <= RTNL_LINK_STATS_MAX; i++) + dump_stat(link, i); + } else { + while (optind < gargc) { + int id = rtnl_link_str2stat(argv[optind]); + + if (id < 0) + fprintf(stderr, "Warning: Unknown statistic " + "\"%s\"\n", argv[optind]); + else + dump_stat(link, id); + + optind++; + } + } +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nl_cache *link_cache; + struct rtnl_link *link; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + link = nl_cli_link_alloc(); + + for (;;) { + int c, optidx = 0; + static struct option long_opts[] = { + { "list", 0, 0, 'l' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "name", 1, 0, 'n' }, + { "index", 1, 0, 'i' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "lhvn:i:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'l': list_stat_names(); break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'n': nl_cli_link_parse_name(link, optarg); break; + case 'i': nl_cli_link_parse_ifindex(link, optarg); break; + } + } + + gargc = argc; + nl_cache_foreach_filter(link_cache, OBJ_CAST(link), dump_stats, argv); + + return 0; +} + diff --git a/libnl/src/nl-list-caches.c b/libnl/src/nl-list-caches.c new file mode 100644 index 0000000..4c2564a --- /dev/null +++ b/libnl/src/nl-list-caches.c @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include "nl-priv-dynamic-core/object-api.h" +#include "nl-priv-dynamic-core/cache-api.h" + +static void print_usage(void) +{ + fprintf(stderr, "Usage: nl-list-caches\n"); + exit(1); +} + +static char *id_attr_list(struct nl_object_ops *ops, char *buf, size_t len) +{ + if (ops->oo_attrs2str != NULL) + return ops->oo_attrs2str(ops->oo_id_attrs, buf, len); + else { + memset(buf, 0, len); + return buf; + } +} + +static void print(struct nl_cache_ops *ops, void *arg) +{ + char buf[64]; + + printf("%s:\n" \ + " hdrsize: %d bytes\n" \ + " protocol: %s\n" \ + " request-update: %s\n" \ + " msg-parser: %s\n", + ops->co_name, ops->co_hdrsize, + nl_nlfamily2str(ops->co_protocol, buf, sizeof(buf)), + ops->co_request_update ? "yes" : "no", + ops->co_msg_parser ? "yes" : "no"); + + if (ops->co_obj_ops) { + struct nl_object_ops *obj_ops = ops->co_obj_ops; + const char *dump_names[NL_DUMP_MAX+1] = { + "brief", + "detailed", + "stats", + }; + int i; + + printf(" cacheable object:\n" \ + " name: %s:\n" \ + " size: %zu bytes\n" \ + " constructor: %s\n" \ + " free-data: %s\n" \ + " clone: %s\n" \ + " compare: %s\n" \ + " id attributes: %s\n" \ + " dump: ", + obj_ops->oo_name, obj_ops->oo_size, + obj_ops->oo_constructor ? "yes" : "no", + obj_ops->oo_free_data ? "yes" : "no", + obj_ops->oo_clone ? "yes" : "no", + obj_ops->oo_compare ? "yes" : "no", + id_attr_list(obj_ops, buf, sizeof(buf))); + + for (i = 0; i <= NL_DUMP_MAX; i++) + if (obj_ops->oo_dump[i]) + printf("%s%s", + i == 0 ? "" : ", ", + dump_names[i]); + + printf("\n"); + } + + if (ops->co_genl) { + struct genl_ops *genl_ops = ops->co_genl; + + printf(" genl:\n" \ + " name: %s\n" \ + " user-hdr: %d\n" \ + " id: %d\n", + genl_ops->o_name, genl_ops->o_hdrsize, genl_ops->o_id); + + if (genl_ops->o_ncmds) { + int i; + + printf(" cmds:\n"); + + for (i = 0; i < genl_ops->o_ncmds; i++) { + struct genl_cmd *cmd = &genl_ops->o_cmds[i]; + + printf(" %s:\n" + " id: %d\n" \ + " maxattr: %d\n" \ + " msg-parser: %s\n" \ + " attr-policy: %s\n", + cmd->c_name, cmd->c_id, cmd->c_maxattr, + cmd->c_msg_parser ? "yes" : "no", + cmd->c_attr_policy ? "yes" : "no"); + } + } + } +} + +int main(int argc, char *argv[]) +{ + if (argc > 1 && !strcasecmp(argv[1], "-h")) + print_usage(); + + nl_cache_ops_foreach(print, NULL); + + return 0; +} diff --git a/libnl/src/nl-list-sockets.c b/libnl/src/nl-list-sockets.c new file mode 100644 index 0000000..bb27df0 --- /dev/null +++ b/libnl/src/nl-list-sockets.c @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#define PROC_NETLINK "/proc/net/netlink" + +int main(int argc, char *argv[]) +{ + FILE *fd; + char buf[2048], p[64]; + + fd = fopen(PROC_NETLINK, "re"); + if (fd == NULL) { + perror("fopen"); + return -1; + } + + printf("Address Family PID Groups rmem " + "wmem CB refcnt\n"); + + while (fgets(buf, sizeof(buf), fd)) { + unsigned long sk, cb; + int ret, proto, pid, rmem, wmem, refcnt; + unsigned int groups; + + ret = sscanf(buf, "%lx %d %d %08x %d %d %lx %d\n", + &sk, &proto, &pid, &groups, &rmem, &wmem, + &cb, &refcnt); + if (ret != 8) + continue; + + printf("0x%016lx %-16s %-6d %08x %-6d %-6d 0x%08lx %d\n", + sk, nl_nlfamily2str(proto, p, sizeof(p)), pid, + groups, rmem, wmem, cb, refcnt); + } + + fclose(fd); + + return 0; +} diff --git a/libnl/src/nl-monitor.c b/libnl/src/nl-monitor.c new file mode 100644 index 0000000..60a02d2 --- /dev/null +++ b/libnl/src/nl-monitor.c @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +static const struct { + enum rtnetlink_groups gr_id; + const char* gr_name; +} known_groups[] = { + { RTNLGRP_LINK, "link" }, + { RTNLGRP_NOTIFY, "notify" }, + { RTNLGRP_NEIGH, "neigh" }, + { RTNLGRP_TC, "tc" }, + { RTNLGRP_IPV4_IFADDR, "ipv4-ifaddr" }, + { RTNLGRP_IPV4_MROUTE, "ipv4-mroute" }, + { RTNLGRP_IPV4_ROUTE, "ipv4-route" }, + { RTNLGRP_IPV6_IFADDR, "ipv6-ifaddr" }, + { RTNLGRP_IPV6_MROUTE, "ipv6-mroute" }, + { RTNLGRP_IPV6_ROUTE, "ipv6-route" }, + { RTNLGRP_IPV6_IFINFO, "ipv6-ifinfo" }, + { RTNLGRP_DECnet_IFADDR, "decnet-ifaddr" }, + { RTNLGRP_DECnet_ROUTE, "decnet-route" }, + { RTNLGRP_IPV6_PREFIX, "ipv6-prefix" }, + { RTNLGRP_IPV4_NETCONF, "ipv4-netconf" }, + { RTNLGRP_IPV6_NETCONF, "ipv6-netconf" }, + { RTNLGRP_MPLS_NETCONF, "mpls-netconf" }, + { RTNLGRP_MDB, "mdb" }, + { RTNLGRP_NONE, NULL } +}; + +static void obj_input(struct nl_object *obj, void *arg) +{ + nl_object_dump(obj, arg); +} + +static int event_input(struct nl_msg *msg, void *arg) +{ + if (nl_msg_parse(msg, &obj_input, arg) < 0) + fprintf(stderr, "<> Unknown message type\n"); + + /* Exit nl_recvmsgs_def() and return to the main select() */ + return NL_STOP; +} + +static void print_usage(void) +{ + int i; + + printf( + "Usage: nl-monitor [OPTION] []\n" + "\n" + "Options\n" + " -d, --debug=LEVEL Set libnl debug level { 0 - 7 }\n" + " -f, --format=TYPE Output format { brief | details | stats }\n" + " -h, --help Show this help.\n" + "\n" + ); + printf("Known groups:"); + for (i = 0; known_groups[i].gr_id != RTNLGRP_NONE; i++) + printf(" %s", known_groups[i].gr_name); + printf("\n"); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_dump_params dp = { + .dp_type = NL_DUMP_STATS, + .dp_fd = stdout, + .dp_dump_msgtype = 1, + }; + + struct nl_sock *sock; + int err = 1; + int i, idx; + + sock = nl_cli_alloc_socket(); + nl_socket_disable_seq_check(sock); + nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, event_input, &dp); + + for (;;) { + int c, optidx = 0; + static struct option long_opts[] = { + { "debug", 1, 0, 'd' }, + { "format", 1, 0, 'f' }, + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "d:f:h", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'd': + nl_debug = atoi(optarg); + break; + case 'f': + dp.dp_type = nl_cli_parse_dumptype(optarg); + break; + default: + print_usage(); + break; + } + } + + nl_cli_connect(sock, NETLINK_ROUTE); + + for (idx = optind; argc > idx; idx++) { + for (i = 0; known_groups[i].gr_id != RTNLGRP_NONE; i++) { + if (!strcmp(argv[idx], known_groups[i].gr_name)) { + + if ((err = nl_socket_add_membership(sock, known_groups[i].gr_id)) < 0) { + nl_cli_fatal(err, "%s: %s\n", argv[idx], + nl_geterror(err)); + } + + break; + } + } + if (known_groups[i].gr_id == RTNLGRP_NONE) + fprintf(stderr, "Warning: Unknown group: %s\n", argv[idx]); + } + + nl_cli_link_alloc_cache(sock); + + while (1) { + fd_set rfds; + int fd, retval; + + fd = nl_socket_get_fd(sock); + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + /* wait for an incoming message on the netlink socket */ + retval = select(fd+1, &rfds, NULL, NULL, NULL); + + if (retval) { + /* FD_ISSET(fd, &rfds) will be true */ + nl_recvmsgs_default(sock); + } + } + + return 0; +} diff --git a/libnl/src/nl-neigh-add.c b/libnl/src/nl-neigh-add.c new file mode 100644 index 0000000..d4d926d --- /dev/null +++ b/libnl/src/nl-neigh-add.c @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +static int quiet = 0; + +static void print_usage(void) +{ + printf( + "Usage: nl-neigh-add [OPTION]... NEIGHBOUR\n" + "\n" + "Options\n" + " --update-only Do not create neighbour, updates exclusively\n" + " --create-only Do not update neighbour if it exists already.\n" + " -q, --quiet Do not print informal notifications\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + "\n" + "Neighbour Options\n" + " -a, --addr=ADDR Destination address of neighbour\n" + " -l, --lladdr=ADDR Link layer address of neighbour\n" + " -d, --dev=DEV Device the neighbour is connected to\n" + " --state=STATE Neighbour state, (default = permanent)\n" + "\n" + "Example\n" + " nl-neigh-add --create-only --addr=10.0.0.1 --dev=eth0 \\\n" + " --lladdr=AA:BB:CC:DD:EE:FF\n" + ); + + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct rtnl_neigh *neigh; + struct nl_cache *link_cache; + struct nl_dump_params dp = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + int err, ok = 0, nlflags = NLM_F_REPLACE | NLM_F_CREATE; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + neigh = nl_cli_neigh_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_UPDATE_ONLY = 257, + ARG_CREATE_ONLY = 258, + ARG_STATE, + }; + static struct option long_opts[] = { + { "update-only", 0, 0, ARG_UPDATE_ONLY }, + { "create-only", 0, 0, ARG_CREATE_ONLY }, + { "quiet", 0, 0, 'q' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "addr", 1, 0, 'a' }, + { "lladdr", 1, 0, 'l' }, + { "dev", 1, 0, 'd' }, + { "state", 1, 0, ARG_STATE }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "qhva:l:d:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case ARG_UPDATE_ONLY: nlflags &= ~NLM_F_CREATE; break; + case ARG_CREATE_ONLY: nlflags |= NLM_F_EXCL; break; + case 'q': quiet = 1; break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'a': ok++; nl_cli_neigh_parse_dst(neigh, optarg); break; + case 'l': nl_cli_neigh_parse_lladdr(neigh, optarg); break; + case 'd': nl_cli_neigh_parse_dev(neigh, link_cache, optarg); break; + case ARG_STATE: nl_cli_neigh_parse_state(neigh, optarg); break; + } + } + + if (!ok) + print_usage(); + + if ((err = rtnl_neigh_add(sock, neigh, nlflags)) < 0) + nl_cli_fatal(err, "Unable to add neighbour: %s", + nl_geterror(err)); + + if (!quiet) { + printf("Added "); + nl_object_dump(OBJ_CAST(neigh), &dp); + } + + return 0; +} diff --git a/libnl/src/nl-neigh-delete.c b/libnl/src/nl-neigh-delete.c new file mode 100644 index 0000000..29effcb --- /dev/null +++ b/libnl/src/nl-neigh-delete.c @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +static int quiet = 0, default_yes = 0, deleted = 0, interactive = 0; +static struct nl_sock *sock; + +static void print_usage(void) +{ + printf( + "Usage: nl-neigh-delete [OPTION]... [NEIGHBOUR]\n" + "\n" + "Options\n" + " -i, --interactive Run interactively\n" + " --yes Set default answer to yes\n" + " -q, --quiet Do not print informal notifications\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + "\n" + "Neighbour Options\n" + " -a, --addr=ADDR Destination address of neighbour\n" + " -l, --lladdr=ADDR Link layer address of neighbour\n" + " -d, --dev=DEV Device the neighbour is connected to\n" + " --family=FAMILY Destination address family\n" + " --state=STATE Neighbour state, (default = permanent)\n" + ); + + exit(0); +} + +static void delete_cb(struct nl_object *obj, void *arg) +{ + struct rtnl_neigh *neigh = nl_object_priv(obj); + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + int err; + + if (interactive && !nl_cli_confirm(obj, ¶ms, default_yes)) + return; + + if ((err = rtnl_neigh_delete(sock, neigh, 0)) < 0) + nl_cli_fatal(err, "Unable to delete neighbour: %s\n", + nl_geterror(err)); + + if (!quiet) { + printf("Deleted "); + nl_object_dump(obj, ¶ms); + } + + deleted++; +} + +int main(int argc, char *argv[]) +{ + struct rtnl_neigh *neigh; + struct nl_cache *link_cache, *neigh_cache; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + neigh_cache = nl_cli_neigh_alloc_cache(sock); + neigh = nl_cli_neigh_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_FAMILY = 257, + ARG_STATE = 258, + ARG_YES, + }; + static struct option long_opts[] = { + { "interactive", 0, 0, 'i' }, + { "yes", 0, 0, ARG_YES }, + { "quiet", 0, 0, 'q' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "addr", 1, 0, 'a' }, + { "lladdr", 1, 0, 'l' }, + { "dev", 1, 0, 'd' }, + { "family", 1, 0, ARG_FAMILY }, + { "state", 1, 0, ARG_STATE }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "qhva:l:d:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'i': interactive = 1; break; + case ARG_YES: default_yes = 1; break; + case 'q': quiet = 1; break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'a': nl_cli_neigh_parse_dst(neigh, optarg); break; + case 'l': nl_cli_neigh_parse_lladdr(neigh, optarg); break; + case 'd': nl_cli_neigh_parse_dev(neigh, link_cache, optarg); break; + case ARG_FAMILY: nl_cli_neigh_parse_family(neigh, optarg); break; + case ARG_STATE: nl_cli_neigh_parse_state(neigh, optarg); break; + } + } + + nl_cache_foreach_filter(neigh_cache, OBJ_CAST(neigh), delete_cb, NULL); + + if (!quiet) + printf("Deleted %d neighbours\n", deleted); + + return 0; +} diff --git a/libnl/src/nl-neigh-list.c b/libnl/src/nl-neigh-list.c new file mode 100644 index 0000000..80fce0b --- /dev/null +++ b/libnl/src/nl-neigh-list.c @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +static void print_usage(void) +{ + printf( + "Usage: nl-neigh-list [OPTION]... [NEIGHBOUR]\n" + "\n" + "Options\n" + " -f, --format=TYPE Output format { brief | details | stats }\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + "\n" + "Neighbour Options\n" + " -a, --addr=ADDR Destination address of neighbour\n" + " -l, --lladdr=ADDR Link layer address of neighbour\n" + " -d, --dev=DEV Device the neighbour is connected to\n" + " --family=FAMILY Destination address family\n" + " --state=STATE Neighbour state, (default = permanent)\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct rtnl_neigh *neigh; + struct nl_cache *link_cache, *neigh_cache; + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache_flags(sock, NL_CACHE_AF_ITER); + neigh_cache = nl_cli_neigh_alloc_cache(sock); + neigh = nl_cli_neigh_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_FAMILY = 257, + ARG_STATE = 258, + }; + static struct option long_opts[] = { + { "format", 1, 0, 'f' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "addr", 1, 0, 'a' }, + { "lladdr", 1, 0, 'l' }, + { "dev", 1, 0, 'd' }, + { "family", 1, 0, ARG_FAMILY }, + { "state", 1, 0, ARG_STATE }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "f:hva:l:d:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'a': nl_cli_neigh_parse_dst(neigh, optarg); break; + case 'l': nl_cli_neigh_parse_lladdr(neigh, optarg); break; + case 'd': nl_cli_neigh_parse_dev(neigh, link_cache, optarg); break; + case ARG_FAMILY: nl_cli_neigh_parse_family(neigh, optarg); break; + case ARG_STATE: nl_cli_neigh_parse_state(neigh, optarg); break; + } + } + + nl_cache_dump_filter(neigh_cache, ¶ms, OBJ_CAST(neigh)); + + rtnl_neigh_put(neigh); + nl_cache_put(neigh_cache); + nl_cache_put(link_cache); + nl_socket_free(sock); + + return 0; +} diff --git a/libnl/src/nl-neightbl-list.c b/libnl/src/nl-neightbl-list.c new file mode 100644 index 0000000..596b6ee --- /dev/null +++ b/libnl/src/nl-neightbl-list.c @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include + +static void print_usage(void) +{ + printf( + "Usage: nl-neightbl-list [OPTION]...\n" + "\n" + "Options\n" + " -f, --format=TYPE Output format { brief | details | stats }\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nl_cache *neightbl_cache; + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + nl_cli_link_alloc_cache(sock); + neightbl_cache = nl_cli_alloc_cache(sock, "neighbour table", + rtnl_neightbl_alloc_cache); + + for (;;) { + int c, optidx = 0; + static struct option long_opts[] = { + { "format", 1, 0, 'f' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "f:hv", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + } + } + + nl_cache_dump(neightbl_cache, ¶ms); + + return 0; +} diff --git a/libnl/src/nl-nh-list.c b/libnl/src/nl-nh-list.c new file mode 100644 index 0000000..18fc5f4 --- /dev/null +++ b/libnl/src/nl-nh-list.c @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2022 Stanislav Zaikin + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +static void print_usage(void) +{ + printf("Usage: nl-nh-list [OPTIONS]... \n" + "\n" + "OPTIONS\n" + " --details Show detailed information of each link\n" + " -h, --help Show this help text.\n" + " -v, --version Show versioning information.\n" + "\n" + " -n, --name=NAME Name of link\n" + " -i, --index Interface index (unique identifier)\n" + " --family=NAME Link address family\n"); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nl_cache *link_cache; + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + + for (;;) { + int c, optidx = 0; + enum { + ARG_FAMILY = 257, + ARG_DETAILS, + }; + static struct option long_opts[] = { { "details", 0, 0, + ARG_DETAILS }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "name", 1, 0, 'n' }, + { 0, 0, 0, 0 } }; + + c = getopt_long(argc, argv, "hvn:i:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case ARG_DETAILS: + params.dp_type = NL_DUMP_DETAILS; + break; + case 'h': + print_usage(); + break; + case 'v': + nl_cli_print_version(); + break; + } + } + + link_cache = nl_cli_nh_alloc_cache(sock); + + nl_cache_dump(link_cache, ¶ms); + + return 0; +} diff --git a/libnl/src/nl-pktloc-lookup.c b/libnl/src/nl-pktloc-lookup.c new file mode 100644 index 0000000..90502a3 --- /dev/null +++ b/libnl/src/nl-pktloc-lookup.c @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include + +static void print_usage(void) +{ +printf( +"Usage: nl-pktloc-lookup [OPTIONS] \n" +"\n" +"OPTIONS\n" +" -h, --help Show this help text.\n" +" -v, --version Show versioning information.\n" +" -l, --list List all packet location definitions.\n" +" --u32=VALUE Print in iproute2's u32 selector style\n" +"\n" +"\n" +"EXAMPLE\n" +" $ nl-pktloc-lookup ip.dst\n" +" $ nl-pktloc-lookup --list\n" +"\n" +); + exit(0); +} + +static const char *align_txt[] = { + [TCF_EM_ALIGN_U8] = "u8", + [TCF_EM_ALIGN_U16] = "u16", + [TCF_EM_ALIGN_U32] = "u32" +}; + +static uint32_t align_mask[] = { + [TCF_EM_ALIGN_U8] = 0xff, + [TCF_EM_ALIGN_U16] = 0xffff, + [TCF_EM_ALIGN_U32] = 0xffffffff, +}; + +static const char *layer_txt[] = { + [TCF_LAYER_LINK] = "eth", + [TCF_LAYER_NETWORK] = "ip", + [TCF_LAYER_TRANSPORT] = "tcp" +}; + +static const char *get_align_txt(struct rtnl_pktloc *loc, char buf[static 16]) +{ + if (loc->align < _NL_N_ELEMENTS(align_txt)) + return align_txt[loc->align]; + + snprintf(buf, 16, "%u", loc->align); + return buf; +} + +static void dump_u32_style(struct rtnl_pktloc *loc, uint32_t value) +{ + char buf[16]; + + if (loc->align > 4) + nl_cli_fatal(EINVAL, "u32 only supports alignments u8|u16|u32."); + + if (loc->layer == TCF_LAYER_LINK) + nl_cli_fatal(EINVAL, "u32 does not support link " + "layer locations."); + + if (loc->shift > 0) + nl_cli_fatal(EINVAL, "u32 does not support shifting."); + + printf("%s %x %x at %s%u\n", get_align_txt(loc, buf), value, + loc->mask ? loc->mask : + (loc->align < _NL_N_ELEMENTS(align_mask) ? + align_mask[loc->align] : + 0), + loc->layer == TCF_LAYER_TRANSPORT ? "nexthdr+" : "", + loc->offset); +} + +static void dump_loc(struct rtnl_pktloc *loc) +{ + char buf[16]; + + printf("%s = %s at %s+%u & %#x >> %u\n", loc->name, + get_align_txt(loc, buf), + loc->layer < _NL_N_ELEMENTS(layer_txt) ? layer_txt[loc->layer] : + "???", + loc->offset, loc->mask, loc->shift); +} + +static void list_cb(struct rtnl_pktloc *loc, void *arg) +{ + char buf[16]; + + printf("%-26s %-5s %3s+%-4u %#-10x %-8u %u\n", loc->name, + get_align_txt(loc, buf), + loc->layer < _NL_N_ELEMENTS(layer_txt) ? layer_txt[loc->layer] : + "???", + loc->offset, loc->mask, loc->shift, loc->refcnt); +} + +static void do_list(void) +{ + printf( +"name align offset mask shift refcnt\n"); + printf("---------------------------------------------------------\n"); + + rtnl_pktloc_foreach(&list_cb, NULL); +} + +int main(int argc, char *argv[]) +{ + struct rtnl_pktloc *loc; + int err, ustyle = 0; + uint32_t uvalue = 0; + + for (;;) { + int c, optidx = 0; + enum { + ARG_U32 = 257, + }; + static struct option long_opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "list", 0, 0, 'l' }, + { "u32", 1, 0, ARG_U32 }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "hvl", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'l': do_list(); exit(0); + case ARG_U32: + ustyle = 1; + uvalue = nl_cli_parse_u32(optarg); + break; + } + } + + if (optind >= argc) + print_usage(); + + if ((err = rtnl_pktloc_lookup(argv[optind++], &loc)) < 0) + nl_cli_fatal(err, "Unable to lookup packet location: %s", + nl_geterror(err)); + + if (ustyle) + dump_u32_style(loc, uvalue); + else + dump_loc(loc); + + return 0; +} diff --git a/libnl/src/nl-qdisc-add.c b/libnl/src/nl-qdisc-add.c new file mode 100644 index 0000000..40b9546 --- /dev/null +++ b/libnl/src/nl-qdisc-add.c @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include + +#include "nl-priv-dynamic-route/nl-priv-dynamic-route.h" + +static int quiet = 0; + +static void print_usage(void) +{ + printf( +"Usage: nl-qdisc-add [OPTIONS]... QDISC [CONFIGURATION]...\n" +"\n" +"OPTIONS\n" +" -q, --quiet Do not print informal notifications.\n" +" -h, --help Show this help text.\n" +" -v, --version Show versioning information.\n" +" --update Update qdisc if it exists.\n" +" --replace Replace or update qdisc if it exists.\n" +" --update-only Only update qdisc, never create it.\n" +" --replace-only Only replace or update qdisc, never create it.\n" +" -d, --dev=DEV Network device the qdisc should be attached to.\n" +" -i, --id=ID ID of new qdisc (default: auto-generated)r\n" +" -p, --parent=ID ID of parent { root | ingress | QDISC-ID }\n" +"\n" +"CONFIGURATION\n" +" -h, --help Show help text of qdisc specific options.\n" +"\n" +"EXAMPLE\n" +" $ nl-qdisc-add --dev=eth1 --parent=root htb --rate=100mbit\n" +"\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct rtnl_qdisc *qdisc; + struct rtnl_tc *tc; + struct nl_cache *link_cache; + struct nl_dump_params dp = { + .dp_type = NL_DUMP_DETAILS, + .dp_fd = stdout, + }; + struct nl_cli_tc_module *tm; + struct rtnl_tc_ops *ops; + int err, flags = NLM_F_CREATE | NLM_F_EXCL; + char *kind, *id = NULL; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + + link_cache = nl_cli_link_alloc_cache(sock); + + qdisc = nl_cli_qdisc_alloc(); + tc = (struct rtnl_tc *) qdisc; + + for (;;) { + int c, optidx = 0; + enum { + ARG_REPLACE = 257, + ARG_UPDATE = 258, + ARG_REPLACE_ONLY, + ARG_UPDATE_ONLY, + }; + static struct option long_opts[] = { + { "quiet", 0, 0, 'q' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "dev", 1, 0, 'd' }, + { "parent", 1, 0, 'p' }, + { "id", 1, 0, 'i' }, + { "replace", 0, 0, ARG_REPLACE }, + { "update", 0, 0, ARG_UPDATE }, + { "replace-only", 0, 0, ARG_REPLACE_ONLY }, + { "update-only", 0, 0, ARG_UPDATE_ONLY }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "+qhvd:p:i:", + long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'q': quiet = 1; break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'd': nl_cli_tc_parse_dev(tc, link_cache, optarg); break; + case 'p': nl_cli_tc_parse_parent(tc, optarg); break; + case 'i': id = strdup(optarg); break; + case ARG_UPDATE: flags = NLM_F_CREATE; break; + case ARG_REPLACE: flags = NLM_F_CREATE | NLM_F_REPLACE; break; + case ARG_UPDATE_ONLY: flags = 0; break; + case ARG_REPLACE_ONLY: flags = NLM_F_REPLACE; break; + } + } + + if (optind >= argc) + print_usage(); + + if (!rtnl_tc_get_ifindex(tc)) + nl_cli_fatal(EINVAL, "You must specify a network device (--dev=XXX)"); + + if (!rtnl_tc_get_parent(tc)) + nl_cli_fatal(EINVAL, "You must specify a parent"); + + if (id) { + nl_cli_tc_parse_handle(tc, id, 1); + free(id); + } + + kind = argv[optind++]; + rtnl_tc_set_kind(tc, kind); + + if (!(ops = rtnl_tc_get_ops(tc))) + nl_cli_fatal(ENOENT, "Unknown qdisc \"%s\"", kind); + + if (!(tm = nl_cli_tc_lookup(ops))) + nl_cli_fatal(ENOTSUP, "Qdisc type \"%s\" not supported.", kind); + + tm->tm_parse_argv(tc, argc, argv); + + if (!quiet) { + printf("Adding "); + nl_object_dump(OBJ_CAST(qdisc), &dp); + } + + if ((err = rtnl_qdisc_add(sock, qdisc, flags)) < 0) + nl_cli_fatal(EINVAL, "Unable to add qdisc: %s", nl_geterror(err)); + + return 0; +} diff --git a/libnl/src/nl-qdisc-delete.c b/libnl/src/nl-qdisc-delete.c new file mode 100644 index 0000000..8c89372 --- /dev/null +++ b/libnl/src/nl-qdisc-delete.c @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2010 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include + +static int quiet = 0, default_yes = 0, deleted = 0, interactive = 0; +static struct nl_sock *sock; + +static void print_usage(void) +{ + printf( + "Usage: nl-qdisc-delete [OPTION]... [QDISC]\n" + "\n" + "OPTIONS\n" + " --interactive Run interactively.\n" + " --yes Set default answer to yes.\n" + " -q, --quiet Do not print informal notifications.\n" + " -h, --help Show this help text and exit.\n" + " -v, --version Show versioning information and exit.\n" + "\n" + " -d, --dev=DEV Device the qdisc is attached to.\n" + " -p, --parent=ID Identifier of parent qdisc/class.\n" + " -i, --id=ID Identifier\n" + " -k, --kind=NAME Kind of qdisc (e.g. pfifo_fast)\n" + ); + + exit(0); +} + +static void delete_cb(struct nl_object *obj, void *arg) +{ + struct rtnl_qdisc *qdisc = nl_object_priv(obj); + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + int err; + + /* Ignore default qdiscs, unable to delete */ + if (rtnl_tc_get_handle((struct rtnl_tc *) qdisc) == 0) + return; + + if (interactive && !nl_cli_confirm(obj, ¶ms, default_yes)) + return; + + if ((err = rtnl_qdisc_delete(sock, qdisc)) < 0) + nl_cli_fatal(err, "Unable to delete qdisc: %s\n", nl_geterror(err)); + + if (!quiet) { + printf("Deleted "); + nl_object_dump(obj, ¶ms); + } + + deleted++; +} + +int main(int argc, char *argv[]) +{ + struct rtnl_qdisc *qdisc; + struct rtnl_tc *tc; + struct nl_cache *link_cache, *qdisc_cache; + int nfilter = 0; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + qdisc_cache = nl_cli_qdisc_alloc_cache(sock); + qdisc = nl_cli_qdisc_alloc(); + tc = (struct rtnl_tc *) qdisc; + + for (;;) { + int c, optidx = 0; + enum { + ARG_YES = 257, + ARG_INTERACTIVE = 258, + }; + static struct option long_opts[] = { + { "interactive", 0, 0, ARG_INTERACTIVE }, + { "yes", 0, 0, ARG_YES }, + { "quiet", 0, 0, 'q' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "dev", 1, 0, 'd' }, + { "parent", 1, 0, 'p' }, + { "id", 1, 0, 'i' }, + { "kind", 1, 0, 'k' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "qhvd:p:i:k:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case '?': nl_cli_fatal(EINVAL, "Invalid options"); + case ARG_INTERACTIVE: interactive = 1; break; + case ARG_YES: default_yes = 1; break; + case 'q': quiet = 1; break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'd': + nfilter++; + nl_cli_tc_parse_dev(tc, link_cache, optarg); + break; + case 'p': + nfilter++; + nl_cli_tc_parse_parent(tc, optarg); + break; + case 'i': + nfilter++; + nl_cli_tc_parse_handle(tc, optarg, 0); + break; + case 'k': + nfilter++; + nl_cli_tc_parse_kind(tc, optarg); + break; + } + } + + if (nfilter == 0 && !interactive && !default_yes) { + nl_cli_fatal(EINVAL, + "You are attempting to delete all qdiscs on all devices.\n" + "If you want to proceed, run nl-qdisc-delete --yes.\n" + "Aborting..."); + } + + nl_cache_foreach_filter(qdisc_cache, OBJ_CAST(qdisc), delete_cb, NULL); + + if (!quiet) + printf("Deleted %d qdiscs\n", deleted); + + return 0; +} diff --git a/libnl/src/nl-qdisc-list.c b/libnl/src/nl-qdisc-list.c new file mode 100644 index 0000000..f41cca2 --- /dev/null +++ b/libnl/src/nl-qdisc-list.c @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2010 Thomas Graf + */ + +#include "nl-default.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define NUM_INDENT 4 + +static struct nl_sock *sock; +static int recursive = 0; +static struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, +}; + +static void print_usage(void) +{ + printf( + "Usage: nl-qdisc-list [OPTION]... [QDISC]\n" + "\n" + "OPTIONS\n" + " --details Show details\n" + " --stats Show statistics\n" + " -r, --recursive Show recursive tree\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + "\n" + " -d, --dev=DEV Device the qdisc is attached to. (default: all)\n" + " -p, --parent=ID Identifier of parent qdisc.\n" + " -i, --id=ID Identifier.\n" + " -k, --kind=NAME Kind of qdisc (e.g. pfifo_fast)\n" + "\n" + "EXAMPLE\n" + " # Display statistics of all qdiscs attached to eth0\n" + " $ nl-qdisc-list --details --dev=eth0\n" + "\n" + ); + exit(0); +} + +static void list_classes(int ifindex, uint32_t parent); +static void list_qdiscs(int ifindex, uint32_t parent); + +static void list_class(struct nl_object *obj, void *arg) +{ + struct rtnl_tc *tc = nl_object_priv(obj); + nl_object_dump(obj, ¶ms); + + list_classes(rtnl_tc_get_ifindex(tc), rtnl_tc_get_handle(tc)); + list_qdiscs(rtnl_tc_get_ifindex(tc), rtnl_tc_get_handle(tc)); +} + +static void list_classes(int ifindex, uint32_t parent) +{ + struct nl_cache *class_cache; + struct rtnl_class *filter = nl_cli_class_alloc(); + + class_cache = nl_cli_class_alloc_cache(sock, ifindex); + + rtnl_tc_set_parent((struct rtnl_tc *) filter, parent); + params.dp_prefix += NUM_INDENT; + nl_cache_foreach_filter(class_cache, OBJ_CAST(filter), list_class, NULL); + params.dp_prefix -= NUM_INDENT; + + rtnl_class_put(filter); + nl_cache_free(class_cache); +} + +static void list_cls(int ifindex, uint32_t parent) +{ + struct nl_cache *cls_cache; + + cls_cache = nl_cli_cls_alloc_cache(sock, ifindex, parent); + + params.dp_prefix += NUM_INDENT; + nl_cache_dump(cls_cache, ¶ms); + params.dp_prefix -= NUM_INDENT; + + nl_cache_free(cls_cache); +} + +static void list_qdisc(struct nl_object *obj, void *arg) +{ + struct rtnl_qdisc *qdisc = nl_object_priv(obj); + struct rtnl_tc *tc = (struct rtnl_tc *) qdisc; + + nl_object_dump(obj, ¶ms); + + list_cls(rtnl_tc_get_ifindex(tc), rtnl_tc_get_handle(tc)); + + if (rtnl_tc_get_parent(tc) == TC_H_ROOT) { + list_cls(rtnl_tc_get_ifindex(tc), TC_H_ROOT); + list_classes(rtnl_tc_get_ifindex(tc), TC_H_ROOT); + } + + list_classes(rtnl_tc_get_ifindex(tc), rtnl_tc_get_handle(tc)); +} + +static void list_qdiscs(int ifindex, uint32_t parent) +{ + struct nl_cache *qdisc_cache; + struct rtnl_qdisc *filter = nl_cli_qdisc_alloc(); + + qdisc_cache = nl_cli_qdisc_alloc_cache(sock); + + rtnl_tc_set_ifindex((struct rtnl_tc *) filter, ifindex); + rtnl_tc_set_parent((struct rtnl_tc *) filter, parent); + params.dp_prefix += NUM_INDENT; + nl_cache_foreach_filter(qdisc_cache, OBJ_CAST(filter), list_qdisc, NULL); + params.dp_prefix -= NUM_INDENT; + + rtnl_qdisc_put(filter); + nl_cache_free(qdisc_cache); +} + +int main(int argc, char *argv[]) +{ + struct rtnl_qdisc *qdisc; + struct rtnl_tc *tc; + struct nl_cache *link_cache, *qdisc_cache; + + params.dp_fd = stdout; + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + qdisc_cache = nl_cli_qdisc_alloc_cache(sock); + qdisc = nl_cli_qdisc_alloc(); + tc = (struct rtnl_tc *) qdisc; + + for (;;) { + int c, optidx = 0; + enum { + ARG_DETAILS = 257, + ARG_STATS = 258, + }; + static struct option long_opts[] = { + { "details", 0, 0, ARG_DETAILS }, + { "stats", 0, 0, ARG_STATS }, + { "recursive", 0, 0, 'r' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "dev", 1, 0, 'd' }, + { "parent", 1, 0, 'p' }, + { "id", 1, 0, 'i' }, + { "kind", 1, 0, 'k' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "rhvd:p:i:k:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case ARG_DETAILS: params.dp_type = NL_DUMP_DETAILS; break; + case ARG_STATS: params.dp_type = NL_DUMP_STATS; break; + case 'r': recursive = 1; break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'd': nl_cli_tc_parse_dev(tc, link_cache, optarg); break; + case 'p': nl_cli_tc_parse_parent(tc, optarg); break; + case 'i': nl_cli_tc_parse_handle(tc, optarg, 0); break; + case 'k': nl_cli_tc_parse_kind(tc, optarg); break; + } + } + + if (recursive) + nl_cache_foreach_filter(qdisc_cache, OBJ_CAST(qdisc), list_qdisc, NULL); + else + nl_cache_dump_filter(qdisc_cache, ¶ms, OBJ_CAST(qdisc)); + + return 0; +} diff --git a/libnl/src/nl-route-add.c b/libnl/src/nl-route-add.c new file mode 100644 index 0000000..04fca9f --- /dev/null +++ b/libnl/src/nl-route-add.c @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +static int quiet = 0; +static struct nl_cache *link_cache, *route_cache; + +static void print_usage(void) +{ + printf( + "Usage: nl-route-add [OPTION]... [ROUTE]\n" + "\n" + "Options\n" + " -q, --quiet Do not print informal notifications\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + "\n" + "Route Options\n" + " -d, --dst=ADDR destination prefix, e.g. 10.10.0.0/16\n" + " -n, --nexthop=NH nexthop configuration:\n" + " dev=DEV route via device\n" + " weight=WEIGHT weight of nexthop\n" + " flags=FLAGS\n" + " via=GATEWAY route via other node\n" + " realms=REALMS\n" + " e.g. dev=eth0,via=192.168.1.12\n" + " -t, --table=TABLE Routing table\n" + " --family=FAMILY Address family\n" + " --src=ADDR Source prefix\n" + " --iif=DEV Incomming interface\n" + " --pref-src=ADDR Preferred source address\n" + " --metrics=OPTS Metrics configurations\n" + " --priority=NUM Priotity\n" + " --scope=SCOPE Scope\n" + " --protocol=PROTO Protocol\n" + " --type=TYPE { unicast | local | broadcast | multicast }\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct rtnl_route *route; + struct nl_dump_params dp = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + int err = 1; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + route_cache = nl_cli_route_alloc_cache(sock, 0); + route = nl_cli_route_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_FAMILY = 257, + ARG_SRC = 258, + ARG_IIF, + ARG_PREF_SRC, + ARG_METRICS, + ARG_PRIORITY, + ARG_SCOPE, + ARG_PROTOCOL, + ARG_TYPE, + }; + static struct option long_opts[] = { + { "quiet", 0, 0, 'q' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "dst", 1, 0, 'd' }, + { "nexthop", 1, 0, 'n' }, + { "table", 1, 0, 't' }, + { "family", 1, 0, ARG_FAMILY }, + { "src", 1, 0, ARG_SRC }, + { "iif", 1, 0, ARG_IIF }, + { "pref-src", 1, 0, ARG_PREF_SRC }, + { "metrics", 1, 0, ARG_METRICS }, + { "priority", 1, 0, ARG_PRIORITY }, + { "scope", 1, 0, ARG_SCOPE }, + { "protocol", 1, 0, ARG_PROTOCOL }, + { "type", 1, 0, ARG_TYPE }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "qhvd:n:t:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'q': quiet = 1; break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'd': nl_cli_route_parse_dst(route, optarg); break; + case 'n': nl_cli_route_parse_nexthop(route, optarg, link_cache); break; + case 't': nl_cli_route_parse_table(route, optarg); break; + case ARG_FAMILY: nl_cli_route_parse_family(route, optarg); break; + case ARG_SRC: nl_cli_route_parse_src(route, optarg); break; + case ARG_IIF: nl_cli_route_parse_iif(route, optarg, link_cache); break; + case ARG_PREF_SRC: nl_cli_route_parse_pref_src(route, optarg); break; + case ARG_METRICS: nl_cli_route_parse_metric(route, optarg); break; + case ARG_PRIORITY: nl_cli_route_parse_prio(route, optarg); break; + case ARG_SCOPE: nl_cli_route_parse_scope(route, optarg); break; + case ARG_PROTOCOL: nl_cli_route_parse_protocol(route, optarg); break; + case ARG_TYPE: nl_cli_route_parse_type(route, optarg); break; + } + } + + if ((err = rtnl_route_add(sock, route, NLM_F_EXCL)) < 0) + nl_cli_fatal(err, "Unable to add route: %s", nl_geterror(err)); + + if (!quiet) { + printf("Added "); + nl_object_dump(OBJ_CAST(route), &dp); + } + + return 0; +} diff --git a/libnl/src/nl-route-delete.c b/libnl/src/nl-route-delete.c new file mode 100644 index 0000000..17d50b5 --- /dev/null +++ b/libnl/src/nl-route-delete.c @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +static int interactive = 0, default_yes = 0, quiet = 0; +static int deleted = 0; +static struct nl_sock *sock; + +static void print_version(void) +{ + fprintf(stderr, "%s\n", LIBNL_STRING); + exit(0); +} + +static void print_usage(void) +{ + printf( + "Usage: nl-route-delete [OPTION]... [ROUTE]\n" + "\n" + "Options\n" + " -i, --interactive Run interactively\n" + " --yes Set default answer to yes\n" + " -q, --quiet Do not print informal notifications\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + "\n" + "Route Options\n" + " -d, --dst=ADDR destination prefix, e.g. 10.10.0.0/16\n" + " -n, --nexthop=NH nexthop configuration:\n" + " dev=DEV route via device\n" + " weight=WEIGHT weight of nexthop\n" + " flags=FLAGS\n" + " via=GATEWAY route via other node\n" + " realms=REALMS\n" + " e.g. dev=eth0,via=192.168.1.12\n" + " -t, --table=TABLE Routing table\n" + " --family=FAMILY Address family\n" + " --src=ADDR Source prefix\n" + " --iif=DEV Incomming interface\n" + " --pref-src=ADDR Preferred source address\n" + " --metrics=OPTS Metrics configurations\n" + " --priority=NUM Priotity\n" + " --scope=SCOPE Scope\n" + " --protocol=PROTO Protocol\n" + " --type=TYPE { unicast | local | broadcast | multicast }\n" + ); + exit(0); +} + +static void delete_cb(struct nl_object *obj, void *arg) +{ + struct rtnl_route *route = (struct rtnl_route *) obj; + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + int err; + + if (interactive && !nl_cli_confirm(obj, ¶ms, default_yes)) + return; + + if ((err = rtnl_route_delete(sock, route, 0)) < 0) + nl_cli_fatal(err, "Unable to delete route: %s", nl_geterror(err)); + + if (!quiet) { + printf("Deleted "); + nl_object_dump(obj, ¶ms); + } + + deleted++; +} + +int main(int argc, char *argv[]) +{ + struct nl_cache *link_cache, *route_cache; + struct rtnl_route *route; + int nf = 0; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + route_cache = nl_cli_route_alloc_cache(sock, 0); + route = nl_cli_route_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_FAMILY = 257, + ARG_SRC = 258, + ARG_IIF, + ARG_PREF_SRC, + ARG_METRICS, + ARG_PRIORITY, + ARG_SCOPE, + ARG_PROTOCOL, + ARG_TYPE, + ARG_YES, + }; + static struct option long_opts[] = { + { "interactive", 0, 0, 'i' }, + { "yes", 0, 0, ARG_YES }, + { "quiet", 0, 0, 'q' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "dst", 1, 0, 'd' }, + { "nexthop", 1, 0, 'n' }, + { "table", 1, 0, 't' }, + { "family", 1, 0, ARG_FAMILY }, + { "src", 1, 0, ARG_SRC }, + { "iif", 1, 0, ARG_IIF }, + { "pref-src", 1, 0, ARG_PREF_SRC }, + { "metrics", 1, 0, ARG_METRICS }, + { "priority", 1, 0, ARG_PRIORITY }, + { "scope", 1, 0, ARG_SCOPE }, + { "protocol", 1, 0, ARG_PROTOCOL }, + { "type", 1, 0, ARG_TYPE }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "iqhvd:n:t:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'i': interactive = 1; break; + case ARG_YES: default_yes = 1; break; + case 'q': quiet = 1; break; + case 'h': print_usage(); break; + case 'v': print_version(); break; + case 'd': nf++; nl_cli_route_parse_dst(route, optarg); break; + case 'n': nf++; nl_cli_route_parse_nexthop(route, optarg, link_cache); break; + case 't': nf++; nl_cli_route_parse_table(route, optarg); break; + case ARG_FAMILY: nf++; nl_cli_route_parse_family(route, optarg); break; + case ARG_SRC: nf++; nl_cli_route_parse_src(route, optarg); break; + case ARG_IIF: nf++; nl_cli_route_parse_iif(route, optarg, link_cache); break; + case ARG_PREF_SRC: nf++; nl_cli_route_parse_pref_src(route, optarg); break; + case ARG_METRICS: nf++; nl_cli_route_parse_metric(route, optarg); break; + case ARG_PRIORITY: nf++; nl_cli_route_parse_prio(route, optarg); break; + case ARG_SCOPE: nf++; nl_cli_route_parse_scope(route, optarg); break; + case ARG_PROTOCOL: nf++; nl_cli_route_parse_protocol(route, optarg); break; + case ARG_TYPE: nf++; nl_cli_route_parse_type(route, optarg); break; + } + } + + if (nf == 0 && !interactive && !default_yes) { + fprintf(stderr, "You attempted to delete all routes in " + "non-interactive mode, aborting.\n"); + exit(0); + } + + nl_cache_foreach_filter(route_cache, OBJ_CAST(route), delete_cb, NULL); + + if (!quiet) + printf("Deleted %d routes\n", deleted); + + return 0; +} diff --git a/libnl/src/nl-route-get.c b/libnl/src/nl-route-get.c new file mode 100644 index 0000000..ce845b2 --- /dev/null +++ b/libnl/src/nl-route-get.c @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +static void print_usage(void) +{ + printf("Usage: nl-route-get \n"); + exit(1); +} + +static void parse_cb(struct nl_object *obj, void *arg) +{ + //struct rtnl_route *route = (struct rtnl_route *) obj; + struct nl_dump_params params = { + .dp_fd = stdout, + .dp_type = NL_DUMP_DETAILS, + }; + + nl_object_dump(obj, ¶ms); +} + +static int cb(struct nl_msg *msg, void *arg) +{ + int err; + + if ((err = nl_msg_parse(msg, &parse_cb, NULL)) < 0) + nl_cli_fatal(err, "Unable to parse object: %s", nl_geterror(err)); + + return 0; +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nl_addr *dst; + int err = 1; + + if (argc < 2 || !strcmp(argv[1], "-h")) + print_usage(); + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + nl_cli_link_alloc_cache(sock); + nl_cli_route_alloc_cache(sock, 0); + + dst = nl_cli_addr_parse(argv[1], AF_INET); + + { + struct nl_msg *m; + struct rtmsg rmsg = { + .rtm_family = nl_addr_get_family(dst), + .rtm_dst_len = nl_addr_get_prefixlen(dst), + }; + + m = nlmsg_alloc_simple(RTM_GETROUTE, 0); + if (!m) + nl_cli_fatal(ENOMEM, "out of memory"); + if (nlmsg_append(m, &rmsg, sizeof(rmsg), NLMSG_ALIGNTO) < 0) + nl_cli_fatal(ENOMEM, "out of memory"); + if (nla_put_addr(m, RTA_DST, dst) < 0) + nl_cli_fatal(ENOMEM, "out of memory"); + + err = nl_send_auto_complete(sock, m); + nlmsg_free(m); + if (err < 0) + nl_cli_fatal(err, "%s", nl_geterror(err)); + + nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, cb, NULL); + + if (nl_recvmsgs_default(sock) < 0) + nl_cli_fatal(err, "%s", nl_geterror(err)); + } + + return 0; +} diff --git a/libnl/src/nl-route-list.c b/libnl/src/nl-route-list.c new file mode 100644 index 0000000..284e240 --- /dev/null +++ b/libnl/src/nl-route-list.c @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +static void print_usage(void) +{ + printf( + "Usage: nl-route-list [OPTION]... [ROUTE]\n" + "\n" + "Options\n" + " -c, --cache List the contents of the route cache\n" + " -f, --format=TYPE Output format { brief | details | stats }\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + "\n" + "Route Options\n" + " -d, --dst=ADDR destination prefix, e.g. 10.10.0.0/16\n" + " -n, --nexthop=NH nexthop configuration:\n" + " dev=DEV route via device\n" + " weight=WEIGHT weight of nexthop\n" + " flags=FLAGS\n" + " via=GATEWAY route via other node\n" + " realms=REALMS\n" + " e.g. dev=eth0,via=192.168.1.12\n" + " -t, --table=TABLE Routing table\n" + " --family=FAMILY Address family\n" + " --src=ADDR Source prefix\n" + " --iif=DEV Incomming interface\n" + " --pref-src=ADDR Preferred source address\n" + " --metrics=OPTS Metrics configurations\n" + " --priority=NUM Priotity\n" + " --scope=SCOPE Scope\n" + " --protocol=PROTO Protocol\n" + " --type=TYPE { unicast | local | broadcast | multicast }\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nl_cache *link_cache, *route_cache; + struct rtnl_route *route; + struct nl_dump_params params = { + .dp_fd = stdout, + .dp_type = NL_DUMP_LINE, + }; + int print_cache = 0; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + route = nl_cli_route_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_FAMILY = 257, + ARG_SRC = 258, + ARG_IIF, + ARG_PREF_SRC, + ARG_METRICS, + ARG_PRIORITY, + ARG_SCOPE, + ARG_PROTOCOL, + ARG_TYPE, + }; + static struct option long_opts[] = { + { "cache", 0, 0, 'c' }, + { "format", 1, 0, 'f' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "dst", 1, 0, 'd' }, + { "nexthop", 1, 0, 'n' }, + { "table", 1, 0, 't' }, + { "family", 1, 0, ARG_FAMILY }, + { "src", 1, 0, ARG_SRC }, + { "iif", 1, 0, ARG_IIF }, + { "pref-src", 1, 0, ARG_PREF_SRC }, + { "metrics", 1, 0, ARG_METRICS }, + { "priority", 1, 0, ARG_PRIORITY }, + { "scope", 1, 0, ARG_SCOPE }, + { "protocol", 1, 0, ARG_PROTOCOL }, + { "type", 1, 0, ARG_TYPE }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "cf:hvd:n:t:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'c': print_cache = 1; break; + case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'd': nl_cli_route_parse_dst(route, optarg); break; + case 'n': nl_cli_route_parse_nexthop(route, optarg, link_cache); break; + case 't': nl_cli_route_parse_table(route, optarg); break; + case ARG_FAMILY: nl_cli_route_parse_family(route, optarg); break; + case ARG_SRC: nl_cli_route_parse_src(route, optarg); break; + case ARG_IIF: nl_cli_route_parse_iif(route, optarg, link_cache); break; + case ARG_PREF_SRC: nl_cli_route_parse_pref_src(route, optarg); break; + case ARG_METRICS: nl_cli_route_parse_metric(route, optarg); break; + case ARG_PRIORITY: nl_cli_route_parse_prio(route, optarg); break; + case ARG_SCOPE: nl_cli_route_parse_scope(route, optarg); break; + case ARG_PROTOCOL: nl_cli_route_parse_protocol(route, optarg); break; + case ARG_TYPE: nl_cli_route_parse_type(route, optarg); break; + } + } + + route_cache = nl_cli_route_alloc_cache(sock, + print_cache ? ROUTE_CACHE_CONTENT : 0); + + nl_cache_dump_filter(route_cache, ¶ms, OBJ_CAST(route)); + + return 0; +} diff --git a/libnl/src/nl-rule-list.c b/libnl/src/nl-rule-list.c new file mode 100644 index 0000000..b31c575 --- /dev/null +++ b/libnl/src/nl-rule-list.c @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +static void print_usage(void) +{ + printf( + "Usage: nl-rule-list [OPTION]... [ROUTE]\n" + "\n" + "Options\n" + " -c, --cache List the contents of the route cache\n" + " -f, --format=TYPE Output format { brief | details | stats }\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + "\n" + "Rule Options\n" + " --family Address family\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct rtnl_rule *rule; + struct nl_cache *rule_cache; + struct nl_dump_params params = { + .dp_fd = stdout, + .dp_type = NL_DUMP_LINE, + }; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + nl_cli_link_alloc_cache(sock); + rule_cache = nl_cli_rule_alloc_cache(sock); + rule = nl_cli_rule_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_FAMILY = 257, + }; + static struct option long_opts[] = { + { "format", 1, 0, 'f' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "family", 1, 0, ARG_FAMILY }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "f:hv", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case ARG_FAMILY: nl_cli_rule_parse_family(rule, optarg); break; + } + } + + nl_cache_dump_filter(rule_cache, ¶ms, OBJ_CAST(rule)); + + return 0; +} diff --git a/libnl/src/nl-tctree-list.c b/libnl/src/nl-tctree-list.c new file mode 100644 index 0000000..63dcc15 --- /dev/null +++ b/libnl/src/nl-tctree-list.c @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include +#include + +#include +#include +#include +#include + +static struct nl_sock *sock; +static struct nl_cache *qdisc_cache, *class_cache; +static struct nl_dump_params params = { + .dp_type = NL_DUMP_DETAILS, +}; + +static int ifindex; +static void print_qdisc(struct nl_object *, void *); +static void print_tc_childs(struct rtnl_tc *, void *); + +static void print_usage(void) +{ + printf( + "Usage: nl-tctree-list [OPTION]...\n" + "\n" + "Options\n" + " -f, --format=TYPE Output format { brief | details | stats }\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + ); + exit(0); +} + +static void print_class(struct nl_object *obj, void *arg) +{ + struct rtnl_qdisc *leaf; + struct rtnl_class *class = (struct rtnl_class *) obj; + struct nl_cache *cls_cache; + uint32_t parent = rtnl_tc_get_handle((struct rtnl_tc *) class); + + params.dp_prefix = (int)(long) arg; + nl_object_dump(obj, ¶ms); + + leaf = rtnl_class_leaf_qdisc(class, qdisc_cache); + if (leaf) + print_qdisc((struct nl_object *) leaf, (char *) arg + 2); + + print_tc_childs(TC_CAST(class), (char *) arg + 2); + + if (rtnl_cls_alloc_cache(sock, ifindex, parent, &cls_cache) < 0) + return; + + params.dp_prefix = (int)(long) arg + 2; + nl_cache_dump(cls_cache, ¶ms); + nl_cache_free(cls_cache); +} + +static void print_tc_childs(struct rtnl_tc *tc, void *arg) +{ + struct rtnl_class *filter; + + filter = nl_cli_class_alloc(); + + rtnl_tc_set_parent(TC_CAST(filter), rtnl_tc_get_handle(tc)); + rtnl_tc_set_ifindex(TC_CAST(filter), rtnl_tc_get_ifindex(tc)); + + nl_cache_foreach_filter(class_cache, OBJ_CAST(filter), &print_class, arg); + + rtnl_class_put(filter); +} + +static void print_qdisc(struct nl_object *obj, void *arg) +{ + struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) obj; + struct nl_cache *cls_cache; + uint32_t parent = rtnl_tc_get_handle((struct rtnl_tc *) qdisc); + + params.dp_prefix = (int)(long) arg; + nl_object_dump(obj, ¶ms); + + print_tc_childs(TC_CAST(qdisc), (char *) arg + 2); + + if (rtnl_cls_alloc_cache(sock, ifindex, parent, &cls_cache) < 0) + return; + + params.dp_prefix = (int)(long) arg + 2; + nl_cache_dump(cls_cache, ¶ms); + nl_cache_free(cls_cache); +} + +static void print_link(struct nl_object *obj, void *arg) +{ + struct rtnl_link *link = (struct rtnl_link *) obj; + struct rtnl_qdisc *qdisc; + + ifindex = rtnl_link_get_ifindex(link); + params.dp_prefix = 0; + nl_object_dump(obj, ¶ms); + + if (rtnl_class_alloc_cache(sock, ifindex, &class_cache) < 0) + return; + + qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, TC_H_ROOT); + if (qdisc) { + print_qdisc((struct nl_object *) qdisc, (void *) 2); + rtnl_qdisc_put(qdisc); + } + + qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, 0); + if (qdisc) { + print_qdisc((struct nl_object *) qdisc, (void *) 2); + rtnl_qdisc_put(qdisc); + } + + qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, TC_H_INGRESS); + if (qdisc) { + print_qdisc((struct nl_object *) qdisc, (void *) 2); + rtnl_qdisc_put(qdisc); + } + + nl_cache_free(class_cache); +} + +int main(int argc, char *argv[]) +{ + struct nl_cache *link_cache; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + qdisc_cache = nl_cli_qdisc_alloc_cache(sock); + + params.dp_fd = stdout; + + for (;;) { + int c, optidx = 0; + static struct option long_opts[] = { + { "format", 1, 0, 'f' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "f:hv", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + } + } + + nl_cache_foreach(link_cache, &print_link, NULL); + + return 0; +} diff --git a/libnl/src/nl-util-addr.c b/libnl/src/nl-util-addr.c new file mode 100644 index 0000000..fc8ebdd --- /dev/null +++ b/libnl/src/nl-util-addr.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2009 Thomas Graf + */ + +#include "nl-default.h" + +#include + +int main(int argc, char *argv[]) +{ + int err; + char host[256]; + struct nl_addr *a; + + if (argc < 2) { + fprintf(stderr, "Usage: nl-util-addr
\n"); + return -1; + } + + a = nl_cli_addr_parse(argv[1], AF_UNSPEC); + err = nl_addr_resolve(a, host, sizeof(host)); + if (err != 0) + nl_cli_fatal(err, "Unable to resolve address \"%s\": %s", + argv[1], nl_geterror(err)); + + printf("%s\n", host); + + return 0; +} diff --git a/libnl/tests/check-all.c b/libnl/tests/check-all.c new file mode 100644 index 0000000..e8a0e80 --- /dev/null +++ b/libnl/tests/check-all.c @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Thomas Graf + */ + +#include "nl-default.h" + +#include + +#include "cksuite-all.h" + +static Suite *main_suite(void) +{ + Suite *suite = suite_create("main"); + + return suite; +} + +int main(int argc, char *argv[]) +{ + SRunner *runner; + int nfailed; + + runner = srunner_create(main_suite()); + + srunner_add_suite(runner, make_nl_addr_suite()); + srunner_add_suite(runner, make_nl_attr_suite()); + srunner_add_suite(runner, make_nl_ematch_tree_clone_suite()); + srunner_add_suite(runner, make_nl_netns_suite()); + + srunner_run_all(runner, CK_ENV); + + nfailed = srunner_ntests_failed(runner); + srunner_free(runner); + + return nfailed != 0; +} diff --git a/libnl/tests/check-direct.c b/libnl/tests/check-direct.c new file mode 100644 index 0000000..33b7742 --- /dev/null +++ b/libnl/tests/check-direct.c @@ -0,0 +1,207 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +#include +#include + +#include +#include + +#include "nl-priv-static-route/nl-priv-static-route.h" +#include "nl-aux-core/nl-core.h" + +#define CASES 5 +#define MAX_ATTR 7 + +START_TEST(static_checks) +{ + int i, j; + char strbuf[100]; + + _NL_STATIC_ASSERT(RTNL_LINK_RX_PACKETS == 0); + assert(_nltst_map_stat_id_from_IPSTATS_MIB_v2[0] == + RTNL_LINK_RX_PACKETS); + for (i = 1; i < __IPSTATS_MIB_MAX; i++) { + assert(_nltst_map_stat_id_from_IPSTATS_MIB_v2[i] > 0); + assert(_nltst_map_stat_id_from_IPSTATS_MIB_v2[i] < + __RTNL_LINK_STATS_MAX); + for (j = 1; j < i; j++) + assert(_nltst_map_stat_id_from_IPSTATS_MIB_v2[i] != + _nltst_map_stat_id_from_IPSTATS_MIB_v2[j]); + } + + for (i = 0; i <= RTNL_LINK_STATS_MAX + 1; i++) { + const char *s; + + s = rtnl_link_stat2str(i, strbuf, sizeof(strbuf)); + assert(s); + assert(s == strbuf); + assert(strlen(s) < sizeof(strbuf)); + if (strncmp(s, "0x", 2) == 0) { + assert(i == RTNL_LINK_STATS_MAX + 1); + ck_assert_int_eq(strtoll(&s[2], NULL, 16), i); + } else + ck_assert_int_le(i, RTNL_LINK_STATS_MAX); + ck_assert_int_eq(i, rtnl_link_str2stat(s)); + } + + ck_assert_int_eq(nl_str2ip_proto(""), -NLE_OBJ_NOTFOUND); + ck_assert_int_eq(nl_str2ip_proto("5"), 5); + ck_assert_int_eq(nl_str2ip_proto(" 13 "), -NLE_OBJ_NOTFOUND); + ck_assert_int_eq(nl_str2ip_proto("13"), 13); + ck_assert_int_eq(nl_str2ip_proto("0x13"), 0x13); + ck_assert_int_eq(nl_str2ip_proto("0342"), 0342); + ck_assert_int_eq(nl_str2ip_proto("2147483647"), 2147483647); + ck_assert_int_eq(nl_str2ip_proto("2147483648"), -NLE_OBJ_NOTFOUND); +} +END_TEST + +static void set_bitmap_range(u_int32_t start, u_int32_t end, + struct rtnl_link_bridge_vlan *vlan_info, + int untagged) +{ + for (u_int32_t i = start; i <= end; i++) { + vlan_info->vlan_bitmap[i / 32] |= (((uint32_t)1) << (i % 32)); + if (untagged) { + vlan_info->untagged_bitmap[i / 32] |= + (((uint32_t)1) << (i % 32)); + } + } +} + +START_TEST(vlan_attribute_check) +{ + struct nlmsghdr *nlh; + struct nlattr *a; + int attr_count, rem; + struct bridge_vlan_info *vlan_attr; + struct rtnl_link_bridge_vlan vlan_info[CASES]; + struct bridge_vlan_info expected_attr[CASES][MAX_ATTR]; + + for (int i = 0; i < CASES; i++) { + memset(&vlan_info[i], 0, sizeof(struct rtnl_link_bridge_vlan)); + memset(&expected_attr[i], 0, + sizeof(struct bridge_vlan_info) * MAX_ATTR); + } + + // Case 1 setting pvid untagged. + vlan_info[0].pvid = 1; + set_bitmap_range(1, 1, &vlan_info[0], 1); + expected_attr[0][0].vid = 1; + expected_attr[0][0].flags = BRIDGE_VLAN_INFO_PVID | + BRIDGE_VLAN_INFO_UNTAGGED; + + // Case 2 setting vid range. + vlan_info[1].pvid = 0; + set_bitmap_range(1, 4094, &vlan_info[1], 0); + expected_attr[1][0].vid = 1; + expected_attr[1][0].flags = BRIDGE_VLAN_INFO_RANGE_BEGIN; + expected_attr[1][1].vid = 4094; + expected_attr[1][1].flags = BRIDGE_VLAN_INFO_RANGE_END; + + // Case 3 interweaving pvid with vid range. + vlan_info[2].pvid = 7; + set_bitmap_range(1, 27, &vlan_info[2], 0); + set_bitmap_range(7, 7, &vlan_info[2], 1); + expected_attr[2][0].vid = 1; + expected_attr[2][0].flags = BRIDGE_VLAN_INFO_RANGE_BEGIN; + expected_attr[2][1].vid = 6; + expected_attr[2][1].flags = BRIDGE_VLAN_INFO_RANGE_END; + expected_attr[2][2].vid = 8; + expected_attr[2][2].flags = BRIDGE_VLAN_INFO_RANGE_BEGIN; + expected_attr[2][3].vid = 27; + expected_attr[2][3].flags = BRIDGE_VLAN_INFO_RANGE_END; + expected_attr[2][4].vid = 7; + expected_attr[2][4].flags = BRIDGE_VLAN_INFO_PVID | + BRIDGE_VLAN_INFO_UNTAGGED; + + // Case 4 interweaving untagged and tagged vid ranges. + vlan_info[3].pvid = 1; + set_bitmap_range(1, 1, &vlan_info[3], 1); + set_bitmap_range(1, 25, &vlan_info[3], 0); + set_bitmap_range(26, 50, &vlan_info[3], 1); + set_bitmap_range(51, 75, &vlan_info[3], 0); + expected_attr[3][0].vid = 2; + expected_attr[3][0].flags = BRIDGE_VLAN_INFO_RANGE_BEGIN; + expected_attr[3][1].vid = 25; + expected_attr[3][1].flags = BRIDGE_VLAN_INFO_RANGE_END; + expected_attr[3][2].vid = 26; + expected_attr[3][2].flags = BRIDGE_VLAN_INFO_RANGE_BEGIN | + BRIDGE_VLAN_INFO_UNTAGGED; + expected_attr[3][3].vid = 50; + expected_attr[3][3].flags = BRIDGE_VLAN_INFO_RANGE_END | + BRIDGE_VLAN_INFO_UNTAGGED; + expected_attr[3][4].vid = 51; + expected_attr[3][4].flags = BRIDGE_VLAN_INFO_RANGE_BEGIN; + expected_attr[3][5].vid = 75; + expected_attr[3][5].flags = BRIDGE_VLAN_INFO_RANGE_END; + expected_attr[3][6].vid = 1; + expected_attr[3][6].flags = BRIDGE_VLAN_INFO_PVID | + BRIDGE_VLAN_INFO_UNTAGGED; + + // Case 5 individual vid. + vlan_info[4].pvid = 0; + set_bitmap_range(5, 5, &vlan_info[4], 0); + set_bitmap_range(3067, 3067, &vlan_info[4], 1); + expected_attr[4][0].vid = 5; + expected_attr[4][0].flags = 0; + expected_attr[4][1].vid = 3067; + expected_attr[4][1].flags = BRIDGE_VLAN_INFO_UNTAGGED; + + for (int i = 0; i < CASES; i++) { + _nl_auto_nl_msg struct nl_msg *msg = nlmsg_alloc(); + attr_count = 0; + ck_assert_msg(msg, "Unable to allocate netlink message"); + ck_assert_int_eq(0, + _nl_bridge_fill_vlan_info(msg, &vlan_info[i])); + + nlh = nlmsg_hdr(msg); + + nlmsg_for_each_attr(a, nlh, 0, rem) { + ck_assert_msg(expected_attr[i][attr_count].vid != 0, + "Attribute number %d unexpected", + attr_count); + ck_assert_msg( + nla_type(a) == IFLA_BRIDGE_VLAN_INFO, + "Expected attribute IFLA_BRIDGE_VLAN_INFO %d", + IFLA_BRIDGE_VLAN_INFO); + vlan_attr = (struct bridge_vlan_info *)nla_data(a); + ck_assert_int_eq(vlan_attr->vid, + expected_attr[i][attr_count].vid); + ck_assert_int_eq(vlan_attr->flags, + expected_attr[i][attr_count].flags); + attr_count++; + } + } +} +END_TEST + +static Suite *make_suite(void) +{ + Suite *suite = suite_create("Direct"); + TCase *tc = tcase_create("Core"); + + tcase_add_test(tc, static_checks); + tcase_add_test(tc, vlan_attribute_check); + suite_add_tcase(suite, tc); + return suite; +} + +int main(int argc, char *argv[]) +{ + SRunner *runner; + int nfailed; + + runner = srunner_create(suite_create("main")); + + srunner_add_suite(runner, make_suite()); + + srunner_run_all(runner, CK_ENV); + + nfailed = srunner_ntests_failed(runner); + srunner_free(runner); + return nfailed != 0; +} diff --git a/libnl/tests/cksuite-all-addr.c b/libnl/tests/cksuite-all-addr.c new file mode 100644 index 0000000..cc53333 --- /dev/null +++ b/libnl/tests/cksuite-all-addr.c @@ -0,0 +1,237 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Thomas Graf + */ + +#include "nl-default.h" + +#include +#include +#include + +#include "cksuite-all.h" + +START_TEST(addr_alloc) +{ + struct nl_addr *addr; + + addr = nl_addr_alloc(16); + ck_assert_msg(addr, "Allocation should not return NULL"); + + ck_assert_msg(nl_addr_iszero(addr) != 0, + "New empty address should be all zeros"); + + ck_assert_msg(nl_addr_get_family(addr) == AF_UNSPEC, + "New empty address should have family AF_UNSPEC"); + + ck_assert_msg(nl_addr_get_prefixlen(addr) == 0, + "New empty address should have prefix length 0"); + + ck_assert_msg(!nl_addr_shared(addr), + "New empty address should not be shared"); + + ck_assert_msg(nl_addr_get(addr) == addr, + "nl_addr_get() should return pointer to address"); + + ck_assert_msg(nl_addr_shared(addr) != 0, + "Address should be shared after call to nl_addr_get()"); + + nl_addr_put(addr); + + ck_assert_msg( + !nl_addr_shared(addr), + "Address should not be shared after call to nl_addr_put()"); + + ck_assert_msg(nl_addr_fill_sockaddr(addr, NULL, 0) != 0, + "Socket address filling should fail for empty address"); + + nl_addr_put(addr); +} +END_TEST + +START_TEST(addr_binary_addr) +{ + struct nl_addr *addr, *addr2; + char baddr[4] = { 0x1, 0x2, 0x3, 0x4 }; + char baddr2[6] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 }; + + addr = nl_addr_alloc(4); + ck_assert_msg(addr != NULL, "Allocation should not return NULL"); + + ck_assert_msg(nl_addr_set_binary_addr(addr, baddr, 4) >= 0, + "Valid binary address should be settable"); + + ck_assert_msg( + nl_addr_get_prefixlen(addr) == 0, + "Prefix length should be unchanged after nl_addr_set_binary_addr()"); + + ck_assert_msg(nl_addr_get_len(addr) == 4, "Address length should be 4"); + + ck_assert_msg( + nl_addr_set_binary_addr(addr, baddr2, 6) != 0, + "Should not be able to set binary address exceeding maximum length"); + + ck_assert_msg(nl_addr_get_len(addr) == 4, + "Address length should still be 4"); + + ck_assert_msg( + nl_addr_guess_family(addr) == AF_INET, + "Binary address of length 4 should be guessed as AF_INET"); + + ck_assert_msg(memcmp(baddr, nl_addr_get_binary_addr(addr), 4) == 0, + "Binary address mismatches"); + + addr2 = nl_addr_build(AF_UNSPEC, baddr, 4); + ck_assert_msg(addr2 != NULL, "Building of address should not fail"); + + nl_addr_set_prefixlen(addr, 32); + ck_assert_msg( + nl_addr_get_prefixlen(addr) == 32, + "Prefix length should be successful changed after nl_addr_set_prefixlen()"); + + ck_assert_msg(!nl_addr_cmp(addr, addr2), + "Addresses built from same binary address should match"); + + nl_addr_put(addr); + nl_addr_put(addr2); +} +END_TEST + +START_TEST(addr_parse4) +{ + struct nl_addr *addr4, *clone; + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + char *addr_str = "10.0.0.1/16"; + char buf[128]; + + ck_assert_msg(nl_addr_parse(addr_str, AF_INET6, &addr4) != 0, + "Should not be able to parse IPv4 address in IPv6 mode"); + + ck_assert_msg(nl_addr_parse(addr_str, AF_UNSPEC, &addr4) == 0, + "Should be able to parse \"%s\"", addr_str); + + ck_assert_msg(nl_addr_get_family(addr4) == AF_INET, + "Address family should be AF_INET"); + + ck_assert_msg(nl_addr_get_prefixlen(addr4) == 16, + "Prefix length should be 16"); + + ck_assert_msg(!nl_addr_iszero(addr4), + "Address should not be all zeroes"); + + clone = nl_addr_clone(addr4); + ck_assert_msg(clone != NULL, "Cloned address should not be NULL"); + + ck_assert_msg(nl_addr_cmp(addr4, clone) == 0, + "Cloned address should not mismatch original"); + + ck_assert_msg(nl_addr_fill_sockaddr(addr4, (struct sockaddr *)&sin, + &len) == 0, + "Should be able to fill socketaddr"); + + ck_assert_msg( + !strcmp(nl_addr2str(addr4, buf, sizeof(buf)), addr_str), + "Address translated back to string does not match original"); + + nl_addr_put(addr4); + nl_addr_put(clone); +} +END_TEST + +START_TEST(addr_parse6) +{ + struct nl_addr *addr6, *clone; + struct sockaddr_in6 sin; + socklen_t len = sizeof(sin); + char *addr_str = "2001:1:2::3/64"; + char buf[128]; + + ck_assert_msg(nl_addr_parse(addr_str, AF_INET, &addr6) != 0, + "Should not be able to parse IPv6 address in IPv4 mode"); + + ck_assert_msg(nl_addr_parse(addr_str, AF_UNSPEC, &addr6) == 0, + "Should be able to parse \"%s\"", addr_str); + + ck_assert_msg(nl_addr_get_family(addr6) == AF_INET6, + "Address family should be AF_INET6"); + + ck_assert_msg(nl_addr_get_prefixlen(addr6) == 64, + "Prefix length should be 64"); + + ck_assert_msg(!nl_addr_iszero(addr6), + "Address should not be all zeroes"); + + clone = nl_addr_clone(addr6); + ck_assert_msg(clone != NULL, "Cloned address should not be NULL"); + + ck_assert_msg(nl_addr_cmp(addr6, clone) == 0, + "Cloned address should not mismatch original"); + + ck_assert_msg(nl_addr_fill_sockaddr(addr6, (struct sockaddr *)&sin, + &len) == 0, + "Should be able to fill socketaddr"); + + ck_assert_msg( + !strcmp(nl_addr2str(addr6, buf, sizeof(buf)), addr_str), + "Address translated back to string does not match original"); + + _nl_clear_pointer(&addr6, nl_addr_put); + + ck_assert(nl_addr_parse("default", AF_INET6, &addr6) == 0); + ck_assert_int_eq(nl_addr_get_len(addr6), 16); + ck_assert_int_eq(nl_addr_get_prefixlen(addr6), 0); + ck_assert_mem_eq(nl_addr_get_binary_addr(addr6), ((uint8_t[16]){ 0 }), + 16); + + nl_addr_put(addr6); + nl_addr_put(clone); +} +END_TEST + +START_TEST(addr_info) +{ + struct nl_addr *addr; + char *addr_str = "127.0.0.1"; + struct addrinfo *result; + + ck_assert_msg(nl_addr_parse(addr_str, AF_UNSPEC, &addr) == 0, + "Parsing of valid address should not fail"); + + ck_assert_msg(nl_addr_info(addr, &result) == 0, + "getaddrinfo() on loopback address should work"); + + freeaddrinfo(result); + nl_addr_put(addr); +} +END_TEST + +START_TEST(addr_flags2str) +{ + int ifa_flags = IFA_F_TENTATIVE | IFA_F_DADFAILED; + int ifa_flags2; + char buf[128]; + + rtnl_addr_flags2str(ifa_flags, buf, sizeof(buf)); + ck_assert_str_eq(buf, "dadfailed,tentative"); + + ifa_flags2 = rtnl_addr_str2flags(buf); + ck_assert_int_eq(ifa_flags2, ifa_flags); +} +END_TEST + +Suite *make_nl_addr_suite(void) +{ + Suite *suite = suite_create("Abstract addresses"); + TCase *tc = tcase_create("Core"); + + tcase_add_test(tc, addr_alloc); + tcase_add_test(tc, addr_binary_addr); + tcase_add_test(tc, addr_parse4); + tcase_add_test(tc, addr_parse6); + tcase_add_test(tc, addr_info); + tcase_add_test(tc, addr_flags2str); + suite_add_tcase(suite, tc); + + return suite; +} diff --git a/libnl/tests/cksuite-all-attr.c b/libnl/tests/cksuite-all-attr.c new file mode 100644 index 0000000..86e3c6f --- /dev/null +++ b/libnl/tests/cksuite-all-attr.c @@ -0,0 +1,214 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Thomas Graf + */ + +#include "nl-default.h" + +#include +#include + +#include +#include +#include + +#include "cksuite-all.h" +#include "nl-aux-route/nl-route.h" + +START_TEST(attr_size) +{ + ck_assert_msg(nla_attr_size(0) == NLA_HDRLEN, + "Length of empty attribute should match header size"); + ck_assert_msg(nla_attr_size(1) == NLA_HDRLEN + 1, + "Length of 1 bytes payload should be NLA_HDRLEN + 1"); + ck_assert_msg(nla_attr_size(2) == NLA_HDRLEN + 2, + "Length of 2 bytes payload should be NLA_HDRLEN + 2"); + ck_assert_msg(nla_attr_size(3) == NLA_HDRLEN + 3, + "Length of 3 bytes payload should be NLA_HDRLEN + 3"); + ck_assert_msg(nla_attr_size(4) == NLA_HDRLEN + 4, + "Length of 4 bytes payload should be NLA_HDRLEN + 4"); + + ck_assert_msg(nla_total_size(1) == NLA_HDRLEN + 4, + "Total size of 1 bytes payload should result in 8 bytes"); + ck_assert_msg(nla_total_size(2) == NLA_HDRLEN + 4, + "Total size of 2 bytes payload should result in 8 bytes"); + ck_assert_msg(nla_total_size(3) == NLA_HDRLEN + 4, + "Total size of 3 bytes payload should result in 8 bytes"); + ck_assert_msg(nla_total_size(4) == NLA_HDRLEN + 4, + "Total size of 4 bytes payload should result in 8 bytes"); + + ck_assert_msg(nla_padlen(1) == 3, + "2 bytes of payload should result in 3 padding bytes"); + ck_assert_msg(nla_padlen(2) == 2, + "2 bytes of payload should result in 2 padding bytes"); + ck_assert_msg(nla_padlen(3) == 1, + "3 bytes of payload should result in 1 padding bytes"); + ck_assert_msg(nla_padlen(4) == 0, + "4 bytes of payload should result in 0 padding bytes"); + ck_assert_msg(nla_padlen(5) == 3, + "5 bytes of payload should result in 3 padding bytes"); +} +END_TEST + +START_TEST(msg_construct) +{ + struct nl_msg *msg; + struct nlmsghdr *nlh; + struct nlattr *a; + int i, rem; + + msg = nlmsg_alloc(); + ck_assert_msg(msg, "Unable to allocate netlink message"); + + for (i = 1; i < 256; i++) { + ck_assert_msg(nla_put_u32(msg, i, i + 1) == 0, + "Unable to add attribute %d", i); + } + + nlh = nlmsg_hdr(msg); + i = 1; + nlmsg_for_each_attr(a, nlh, 0, rem) { + ck_assert_msg(nla_type(a) == i, "Expected attribute %d", i); + i++; + ck_assert_msg(nla_get_u32(a) == (unsigned)i, + "Expected attribute value %d", i); + } + + nlmsg_free(msg); +} +END_TEST + +START_TEST(clone_cls_u32) +{ + _nl_auto_rtnl_link struct rtnl_link *link = NULL; + _nl_auto_rtnl_cls struct rtnl_cls *cls = NULL; + _nl_auto_rtnl_cls struct rtnl_cls *cls2 = NULL; + int r; + const uint32_t direction = 16; + + link = rtnl_link_alloc(); + ck_assert(link); + + rtnl_link_set_ifindex(link, 5); + + cls = rtnl_cls_alloc(); + ck_assert(cls); + + rtnl_tc_set_link(TC_CAST(cls), link); + + r = rtnl_tc_set_kind(TC_CAST(cls), "u32"); + ck_assert(r == 0); + + rtnl_cls_set_prio(cls, 1); + rtnl_cls_set_protocol(cls, ETH_P_IP); + + rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(1, 0)); + + rtnl_u32_set_hashtable(cls, 5); + + rtnl_u32_add_key_uint32(cls, 0x0a000914, 0xffffffff, direction, 0); + + rtnl_u32_set_hashmask(cls, 0xff000000, direction); + + rtnl_u32_add_mark(cls, 55, 66); + + rtnl_u32_set_link(cls, 44); + + cls2 = (struct rtnl_cls *)nl_object_clone((struct nl_object *)cls); + ck_assert(cls2); +} +END_TEST + +/*****************************************************************************/ + +START_TEST(test_nltst_strtok) +{ +#define _assert_strtok(str, ...) \ + do { \ + const char *const _expected[] = { NULL, ##__VA_ARGS__, NULL }; \ + _nltst_auto_strfreev char **_tokens = NULL; \ + \ + _tokens = _nltst_strtokv(str); \ + _nltst_assert_strv_equal(_tokens, &_expected[1]); \ + } while (0) + + _assert_strtok(""); + _assert_strtok(" \n"); + _assert_strtok("a", "a"); + _assert_strtok(" a ", "a"); + _assert_strtok(" a\\ b", "a\\ ", "b"); + _assert_strtok(" a\\ b cc\\d", "a\\ ", "b", "cc\\d"); + _assert_strtok(" a\\ b\\ cc\\d", "a\\ ", "b\\ ", "cc\\d"); +} +END_TEST + +/*****************************************************************************/ + +START_TEST(test_nltst_select_route) +{ + /* This is a unit test for testing the unit-test helper function + * _nltst_select_route_parse(). */ + +#define _check(str, exp_addr_family, exp_addr_pattern, exp_plen) \ + do { \ + const char *_str = (str); \ + const int _exp_addr_family = (exp_addr_family); \ + const char *const _exp_addr_pattern = (exp_addr_pattern); \ + const int _exp_plen = (exp_plen); \ + _nltst_auto_clear_select_route NLTstSelectRoute \ + _select_route = { 0 }; \ + _nltst_auto_clear_select_route NLTstSelectRoute \ + _select_route2 = { 0 }; \ + _nl_auto_free char *_str2 = NULL; \ + \ + _nltst_select_route_parse(_str, &_select_route); \ + ck_assert_int_eq(_exp_addr_family, _select_route.addr_family); \ + if (_nltst_inet_valid(AF_UNSPEC, _exp_addr_pattern)) { \ + ck_assert_str_eq(_exp_addr_pattern, \ + _select_route.addr); \ + ck_assert_ptr_null(_select_route.addr_pattern); \ + } else { \ + ck_assert_str_eq(_exp_addr_pattern, \ + _select_route.addr_pattern); \ + ck_assert_ptr_null(_select_route.addr); \ + } \ + ck_assert_int_eq(_exp_plen, _select_route.plen); \ + \ + _nltst_assert_select_route(&_select_route); \ + \ + _str2 = _nltst_select_route_to_string(&_select_route); \ + ck_assert_ptr_nonnull(_str2); \ + \ + _nltst_select_route_parse(_str2, &_select_route2); \ + \ + ck_assert(_nltst_select_route_equal(&_select_route, \ + &_select_route2)); \ + } while (0) + + _check("0.0.0.0", AF_INET, "0.0.0.0", -1); + _check("4 0.0.0.0/0", AF_INET, "0.0.0.0", 0); + _check(" 6\n 0:0::/0", AF_INET6, "::", 0); + _check(" \n 0:0::/100", AF_INET6, "::", 100); + _check("6 0:0::*/0 ", AF_INET6, "0:0::*", 0); + _check("6 0:0::*/128 ", AF_INET6, "0:0::*", 128); + _check("6 0:0::* ", AF_INET6, "0:0::*", -1); + +#undef _check +} + +/*****************************************************************************/ + +Suite *make_nl_attr_suite(void) +{ + Suite *suite = suite_create("Netlink attributes"); + TCase *tc = tcase_create("Core"); + + tcase_add_test(tc, attr_size); + tcase_add_test(tc, msg_construct); + tcase_add_test(tc, clone_cls_u32); + tcase_add_test(tc, test_nltst_strtok); + tcase_add_test(tc, test_nltst_select_route); + suite_add_tcase(suite, tc); + + return suite; +} diff --git a/libnl/tests/cksuite-all-ematch-tree-clone.c b/libnl/tests/cksuite-all-ematch-tree-clone.c new file mode 100644 index 0000000..c48a9dd --- /dev/null +++ b/libnl/tests/cksuite-all-ematch-tree-clone.c @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include +#include +#include + +#include + +#include + +#include "cksuite-all.h" +#include "nl-aux-route/nl-route.h" +#include "nl-priv-dynamic-route/nl-priv-dynamic-route.h" + +#define MAX_DEPTH 6 +#define MAX_CHILDREN 5 + +static int current_depth = 0; +static int id = 1; +static long long array_size = 0; + +static long long my_pow(long long x, long long y) +{ + int ret = x; + + if (y == 0) + return 1; + + if (y < 0 || x == 0) + return 0; + + while (--y) { + ret *= x; + } + + return ret; +} + +static int build_children(struct nl_list_head *parent) +{ + int i, num = 0; + struct rtnl_ematch *child = NULL; + + if (!parent) + return 0; + + if (++current_depth > MAX_DEPTH) { + --current_depth; + return 0; + } + + num = _nltst_rand_u32() % ((unsigned)(MAX_CHILDREN + 1)); + for (i = 0; i < num; ++i) { + child = rtnl_ematch_alloc(); + if (!child) { + printf("Mem alloc error\n"); + exit(1); + } + build_children(&child->e_childs); + child->e_id = id++; + nl_list_add_tail(&child->e_list, parent); + } + + --current_depth; + return 0; +} + +static void build_src_cgroup(struct rtnl_ematch_tree *tree) +{ + build_children(&tree->et_list); +} + +static void dump_ematch_list(struct nl_list_head *head, int *result, int *index) +{ + struct rtnl_ematch *pos = NULL; + + nl_list_for_each_entry(pos, head, e_list) { + if (!nl_list_empty(&pos->e_childs)) + dump_ematch_list(&pos->e_childs, result, index); + result[*index] = pos->e_id; + (*index)++; + } +} + +static void dump_ematch_tree(struct rtnl_ematch_tree *tree, int *result, + int *index) +{ + if (!tree) + return; + + dump_ematch_list(&tree->et_list, result, index); +} + +static int compare(int *r1, int *r2, int len) +{ + int i = 0; + for (i = 0; i < len; ++i) { + if (r1[i] != r2[i]) + return -1; + } + return 0; +} + +START_TEST(ematch_tree_clone) +{ + _nl_auto_rtnl_ematch_tree struct rtnl_ematch_tree *src = NULL; + _nl_auto_rtnl_ematch_tree struct rtnl_ematch_tree *dst = NULL; + _nl_auto_free int *src_result = NULL; + _nl_auto_free int *dst_result = NULL; + int i = 0; + int j = 0; + + array_size = (MAX_DEPTH * my_pow(MAX_CHILDREN, MAX_DEPTH)) / 2; + src_result = calloc(4, array_size); + dst_result = calloc(4, array_size); + + src = rtnl_ematch_tree_alloc(2); + + build_src_cgroup(src); + dump_ematch_tree(src, src_result, &i); + + dst = rtnl_ematch_tree_clone(src); + dump_ematch_tree(dst, dst_result, &j); + + ck_assert(dst); + ck_assert(i == j); + ck_assert(!compare(src_result, dst_result, i)); +} +END_TEST + +Suite *make_nl_ematch_tree_clone_suite(void) +{ + Suite *suite = suite_create("Clone ematch tree"); + TCase *tc = tcase_create("Core"); + + tcase_add_test(tc, ematch_tree_clone); + suite_add_tcase(suite, tc); + + return suite; +} diff --git a/libnl/tests/cksuite-all-netns.c b/libnl/tests/cksuite-all-netns.c new file mode 100644 index 0000000..1948c3e --- /dev/null +++ b/libnl/tests/cksuite-all-netns.c @@ -0,0 +1,348 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Author: Susant Sahani + * Copyright (c) 2018 Red Hat, Inc. + */ + +#include "nl-default.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cksuite-all.h" + +/*****************************************************************************/ + +static void _nltst_delete_link2(const char *ifname) +{ + _nltst_delete_link(NULL, ifname); +} +#define _nltst_auto_delete_link _nl_auto(_nltst_auto_delete_link_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(const char *, _nltst_auto_delete_link_fcn, + _nltst_delete_link2); + +/*****************************************************************************/ + +START_TEST(cache_and_clone) +{ + _nl_auto_nl_socket struct nl_sock *sk = NULL; + _nl_auto_nl_cache struct nl_cache *link_cache = NULL; + _nl_auto_free struct nl_object **links_all = NULL; + static const struct { + const char *ifname; + const char *kind; + bool add; + } links[] = { + { + .ifname = "xbr0", + .kind = "bridge", + .add = true, + }, + { + .ifname = "xdummy0", + .kind = "dummy", + .add = true, + }, + { + .ifname = "xbond0", + .kind = "bond", + .add = true, + }, + { + .ifname = "lo", + .kind = NULL, + .add = false, + }, + }; + size_t i; + int r; + + for (i = 0; i < _NL_N_ELEMENTS(links); i++) { + if (links[i].add) + _nltst_add_link(NULL, links[i].ifname, links[i].kind, + NULL); + } + + sk = _nltst_socket(NETLINK_ROUTE); + + r = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache); + ck_assert_int_eq(r, 0); + + r = nl_cache_refill(sk, link_cache); + ck_assert_int_eq(r, 0); + + for (i = 0; i < _NL_N_ELEMENTS(links); i++) { + _nl_auto_rtnl_link struct rtnl_link *link_clone = NULL; + struct rtnl_link *link; + + link = _nltst_cache_get_link(link_cache, links[i].ifname); + ck_assert_ptr_nonnull(link); + + ck_assert_str_eq(rtnl_link_get_name(link), links[i].ifname); + + if (_nl_streq(links[i].ifname, "lo")) + ck_assert_int_eq(rtnl_link_get_ifindex(link), 1); + else + ck_assert_int_gt(rtnl_link_get_ifindex(link), 1); + + link_clone = (struct rtnl_link *)nl_object_clone( + (struct nl_object *)link); + ck_assert(link_clone); + + _nltst_object_identical(link, link_clone); + } + + links_all = _nltst_cache_get_all(link_cache, NULL); + for (i = 0; links_all[i]; i++) { + struct rtnl_link *link = (struct rtnl_link *)links_all[i]; + _nl_auto_rtnl_link struct rtnl_link *link_clone = NULL; + + link_clone = (struct rtnl_link *)nl_object_clone( + (struct nl_object *)link); + ck_assert(link_clone); + + _nltst_object_identical(link, link_clone); + } +} +END_TEST + +/*****************************************************************************/ + +START_TEST(test_create_iface) +{ + const int TEST_IDX = _i; + _nl_auto_nl_socket struct nl_sock *sk = _nltst_socket(NETLINK_ROUTE); + _nl_auto_rtnl_link struct rtnl_link *link = NULL; + _nl_auto_rtnl_link struct rtnl_link *link2 = NULL; + _nl_auto_rtnl_link struct rtnl_link *peer = NULL; + _nltst_auto_delete_link const char *IFNAME_DUMMY = NULL; + _nltst_auto_delete_link const char *IFNAME = "ifname"; + int ifindex_dummy; + uint32_t u32; + int r; + + switch (TEST_IDX) { + case 0: + link = _nltst_assert(rtnl_link_bridge_alloc()); + rtnl_link_set_name(link, IFNAME); + break; + case 1: + link = _nltst_assert(rtnl_link_vxlan_alloc()); + rtnl_link_set_name(link, IFNAME); + _nltst_assert_retcode(rtnl_link_vxlan_set_id(link, 128)); + break; + case 2: + link = _nltst_assert(rtnl_link_alloc()); + rtnl_link_set_type(link, "ifb"); + rtnl_link_set_name(link, IFNAME); + break; + case 3: + link = _nltst_assert(rtnl_link_ipgre_alloc()); + rtnl_link_set_name(link, IFNAME); + _nltst_assert_retcode(rtnl_link_ipgre_set_local( + link, _nltst_inet4("192.168.254.12"))); + _nltst_assert_retcode(rtnl_link_ipgre_set_remote( + link, _nltst_inet4("192.168.254.13"))); + _nltst_assert_retcode(rtnl_link_ipgre_set_ttl(link, 64)); + break; + case 4: + link = _nltst_assert(rtnl_link_ip6_tnl_alloc()); + rtnl_link_set_name(link, IFNAME); + _nltst_assert_retcode(rtnl_link_ip6_tnl_set_local( + link, _nltst_inet6p("2607:f0d0:1002:51::4"))); + _nltst_assert_retcode(rtnl_link_ip6_tnl_set_remote( + link, _nltst_inet6p("2607:f0d0:1002:52::5"))); + break; + case 5: + link = _nltst_assert(rtnl_link_ipgretap_alloc()); + rtnl_link_set_name(link, IFNAME); + _nltst_assert_retcode(rtnl_link_ipgre_set_local( + link, _nltst_inet4("10.211.55.10"))); + _nltst_assert_retcode(rtnl_link_ipgre_set_remote( + link, _nltst_inet4("10.133.6.33"))); + _nltst_assert_retcode(rtnl_link_ipgre_set_ttl(link, 64)); + break; + case 6: + link = _nltst_assert(rtnl_link_ipvti_alloc()); + rtnl_link_set_name(link, IFNAME); + _nltst_assert_retcode(rtnl_link_ipvti_set_local( + link, _nltst_inet4("192.168.254.12"))); + _nltst_assert_retcode(rtnl_link_ipvti_set_remote( + link, _nltst_inet4("192.168.254.13"))); + break; + case 7: + link = _nltst_assert(rtnl_link_sit_alloc()); + rtnl_link_set_name(link, IFNAME); + _nltst_assert_retcode(rtnl_link_sit_set_local( + link, _nltst_inet4("192.168.254.12"))); + _nltst_assert_retcode(rtnl_link_sit_set_remote( + link, _nltst_inet4("192.168.254.13"))); + _nltst_assert_retcode(rtnl_link_sit_set_ttl(link, 64)); + break; + case 8: + link = _nltst_assert(rtnl_link_ipip_alloc()); + rtnl_link_set_name(link, IFNAME); + _nltst_assert_retcode(rtnl_link_ipip_set_local( + link, _nltst_inet4("192.168.254.12"))); + _nltst_assert_retcode(rtnl_link_ipip_set_remote( + link, _nltst_inet4("192.168.254.13"))); + _nltst_assert_retcode(rtnl_link_ipip_set_ttl(link, 64)); + break; + case 9: + link = _nltst_assert(rtnl_link_bond_alloc()); + rtnl_link_set_name(link, IFNAME); + break; + case 10: + IFNAME_DUMMY = "ci-dummy"; + _nltst_add_link(sk, IFNAME_DUMMY, "dummy", &ifindex_dummy); + + link = _nltst_assert(rtnl_link_macvtap_alloc()); + rtnl_link_set_link(link, ifindex_dummy); + rtnl_link_set_name(link, IFNAME); + _nltst_assert_retcode(rtnl_link_macvtap_set_mode( + link, rtnl_link_macvtap_str2mode("bridge"))); + break; + case 11: + IFNAME_DUMMY = "ci-dummy"; + _nltst_add_link(sk, IFNAME_DUMMY, "dummy", &ifindex_dummy); + + link = _nltst_assert(rtnl_link_macvlan_alloc()); + rtnl_link_set_link(link, ifindex_dummy); + rtnl_link_set_name(link, IFNAME); + break; + case 12: + IFNAME_DUMMY = "ci-dummy"; + _nltst_add_link(sk, IFNAME_DUMMY, "dummy", &ifindex_dummy); + + link = _nltst_assert(rtnl_link_vlan_alloc()); + rtnl_link_set_link(link, ifindex_dummy); + rtnl_link_set_name(link, IFNAME); + _nltst_assert_retcode(rtnl_link_vlan_set_id(link, 10)); + break; + case 13: + IFNAME_DUMMY = "ci-dummy"; + _nltst_add_link(sk, IFNAME_DUMMY, "dummy", &ifindex_dummy); + + link = _nltst_assert(rtnl_link_macsec_alloc()); + rtnl_link_set_link(link, ifindex_dummy); + rtnl_link_set_name(link, IFNAME); + _nltst_assert_retcode(rtnl_link_macsec_set_port(link, 10)); + _nltst_assert_retcode(rtnl_link_macsec_set_encrypt(link, 1)); + _nltst_assert_retcode( + rtnl_link_macsec_set_replay_protect(link, 1)); + _nltst_assert_retcode(rtnl_link_macsec_set_window(link, 200)); + break; + case 14: + IFNAME_DUMMY = "ci-dummy"; + _nltst_add_link(sk, IFNAME_DUMMY, "dummy", &ifindex_dummy); + + link = _nltst_assert(rtnl_link_ipvlan_alloc()); + rtnl_link_set_link(link, ifindex_dummy); + _nltst_assert_retcode(rtnl_link_ipvlan_set_mode( + link, rtnl_link_ipvlan_str2mode("l2"))); + rtnl_link_set_name(link, IFNAME); + break; + case 15: + link = _nltst_assert(rtnl_link_vrf_alloc()); + rtnl_link_set_name(link, IFNAME); + _nltst_assert_retcode(rtnl_link_vrf_set_tableid(link, 10)); + break; + case 16: { + link = _nltst_assert(rtnl_link_veth_alloc()); + rtnl_link_set_name(link, IFNAME); + peer = _nltst_assert(rtnl_link_veth_get_peer(link)); + rtnl_link_set_name(peer, "ci-veth-peer"); + } break; + default: + ck_assert_msg(0, "unexpected TEST_IDX=%d", _i); + break; + } + + r = rtnl_link_add(sk, link, NLM_F_CREATE); + if (r == -NLE_OPNOTSUPP) { + /* Hm, no kernel module? Skip the test. */ + _nltst_assert_link_not_exists(IFNAME); + IFNAME = NULL; + return; + } + _nltst_assert_retcode(r); + + _nltst_assert_link_exists(IFNAME); + + switch (TEST_IDX) { + case 15: + _nltst_get_link(sk, IFNAME, NULL, &link2); + _nltst_assert_retcode(rtnl_link_vrf_get_tableid(link2, &u32)); + ck_assert_int_eq(u32, 10); + break; + case 16: + _nltst_assert_link_exists("ci-veth-peer"); + if (_nltst_rand_bool()) + IFNAME = "ci-veth-peer"; + break; + } +} +END_TEST + +/*****************************************************************************/ + +static void _route_init(int addr_family, struct nl_sock **sk, + struct nl_cache **cache) +{ + ck_assert(sk && !*sk); + ck_assert(cache && !*cache); + + *sk = _nltst_socket(NETLINK_ROUTE); + *cache = _nltst_rtnl_route_alloc_cache(*sk, addr_family); +} + +START_TEST(route_1) +{ + _nl_auto_nl_socket struct nl_sock *sk = NULL; + _nl_auto_nl_cache struct nl_cache *cache = NULL; + + if (_nltst_skip_no_iproute2("route_1")) + return; + + _nltst_add_link(NULL, "v1", "dummy", NULL); + _nltst_system("ip -d link set v1 up"); + + _route_init(AF_INET6, &sk, &cache); + + _nltst_assert_route_cache(cache, "6 fe80::*/128", "fe80::/64", + "ff00::/8"); +} +END_TEST + +/*****************************************************************************/ + +Suite *make_nl_netns_suite(void) +{ + Suite *suite = suite_create("netns"); + TCase *tc = tcase_create("Core"); + + tcase_add_checked_fixture(tc, nltst_netns_fixture_setup, + nltst_netns_fixture_teardown); + tcase_add_test(tc, cache_and_clone); + tcase_add_loop_test(tc, test_create_iface, 0, 17); + tcase_add_test(tc, route_1); + suite_add_tcase(suite, tc); + + return suite; +} diff --git a/libnl/tests/cksuite-all.h b/libnl/tests/cksuite-all.h new file mode 100644 index 0000000..26e9b5c --- /dev/null +++ b/libnl/tests/cksuite-all.h @@ -0,0 +1,13 @@ +#ifndef __LIBNL3_TESTS_CHECK_ALL_H__ +#define __LIBNL3_TESTS_CHECK_ALL_H__ + +#include + +#include "nl-test-util.h" + +Suite *make_nl_attr_suite(void); +Suite *make_nl_addr_suite(void); +Suite *make_nl_ematch_tree_clone_suite(void); +Suite *make_nl_netns_suite(void); + +#endif /* __LIBNL3_TESTS_CHECK_ALL_H__ */ diff --git a/libnl/tests/nl-test-util.c b/libnl/tests/nl-test-util.c new file mode 100644 index 0000000..dc8dc5a --- /dev/null +++ b/libnl/tests/nl-test-util.c @@ -0,0 +1,1181 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include "nl-test-util.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "lib/route/nl-route.h" + +#include "nl-aux-route/nl-route.h" + +/*****************************************************************************/ + +void _nltst_get_urandom(void *ptr, size_t len) +{ + int fd; + ssize_t nread; + + ck_assert_int_gt(len, 0); + ck_assert_ptr_nonnull(ptr); + + fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOCTTY); + _nltst_assert_errno(fd >= 0); + + nread = read(fd, ptr, len); + _nltst_assert_errno(nread >= 0 && ((size_t)nread) == len); + + _nltst_close(fd); +} + +uint32_t _nltst_rand_u32(void) +{ + _nl_thread_local static unsigned short entropy[3]; + _nl_thread_local static bool has_entropy = false; + + if (!has_entropy) { + unsigned long long seed; + uint64_t seed64; + const char *s; + char *s_end; + + memset(entropy, 0, sizeof(entropy)); + s = getenv("NLTST_SEED_RAND"); + if (!s) + seed = 0; + else if (s[0] != '\0') { + errno = 0; + seed = strtoull(s, &s_end, 10); + if (errno != 0 || s_end[0] != '\0') { + ck_assert_msg( + 0, + "invalid NLTST_SEED_RAND=\"%s\". Must be an integer to seed the random numbers", + s); + } + } else + _nltst_get_urandom(&seed, sizeof(seed)); + + seed64 = seed; + printf("runs with NLTST_SEED_RAND=%" PRIu64 "\n", seed64); + + entropy[0] = (seed64 >> 0) ^ (seed64 >> 48); + entropy[1] = (seed64 >> 16) ^ (seed64 >> 0); + entropy[2] = (seed64 >> 32) ^ (seed64 >> 16); + has_entropy = true; + } + + _NL_STATIC_ASSERT(sizeof(long) >= sizeof(uint32_t)); + return jrand48(entropy); +} + +/*****************************************************************************/ + +#define _CANARY 539339 + +struct nltst_netns { + int canary; +}; + +/*****************************************************************************/ + +#define _assert_nltst_netns(nsdata) \ + do { \ + const struct nltst_netns *_nsdata = (nsdata); \ + \ + ck_assert_ptr_nonnull(_nsdata); \ + ck_assert_int_eq(_nsdata->canary, _CANARY); \ + } while (0) + +static struct { + struct nltst_netns *nsdata; +} _netns_fixture_global; + +void nltst_netns_fixture_setup(void) +{ + ck_assert(!_netns_fixture_global.nsdata); + + _netns_fixture_global.nsdata = nltst_netns_enter(); + _assert_nltst_netns(_netns_fixture_global.nsdata); +} + +void nltst_netns_fixture_teardown(void) +{ + _assert_nltst_netns(_netns_fixture_global.nsdata); + _nl_clear_pointer(&_netns_fixture_global.nsdata, nltst_netns_leave); +} + +/*****************************************************************************/ + +static void unshare_user(void) +{ + const uid_t uid = geteuid(); + const gid_t gid = getegid(); + FILE *f; + int r; + + /* Become a root in new user NS. */ + r = unshare(CLONE_NEWUSER); + _nltst_assert_errno(r == 0); + + /* Since Linux 3.19 we have to disable setgroups() in order to map users. + * Just proceed if the file is not there. */ + f = fopen("/proc/self/setgroups", "we"); + if (f) { + r = fprintf(f, "deny"); + _nltst_assert_errno(r > 0); + _nltst_fclose(f); + } + + /* Map current UID to root in NS to be created. */ + f = fopen("/proc/self/uid_map", "we"); + if (!f) { + if (errno == EACCES) { + /* Accessing uid_map might fail due to sandboxing. + * We ignore that error and proceed with the test. It will probably + * still work. */ + return; + } + _nltst_assert_errno(f); + } + r = fprintf(f, "0 %d 1", uid); + _nltst_assert_errno(r > 0); + _nltst_fclose(f); + + /* Map current GID to root in NS to be created. */ + f = fopen("/proc/self/gid_map", "we"); + _nltst_assert_errno(f); + r = fprintf(f, "0 %d 1", gid); + _nltst_assert_errno(r > 0); + _nltst_fclose(f); +} + +struct nltst_netns *nltst_netns_enter(void) +{ + struct nltst_netns *nsdata; + int r; + + nsdata = calloc(1, sizeof(struct nltst_netns)); + ck_assert(nsdata); + + nsdata->canary = _CANARY; + + unshare_user(); + + r = unshare(CLONE_NEWNET | CLONE_NEWNS); + _nltst_assert_errno(r == 0); + + /* We need a read-only /sys so that the platform knows there's no udev. */ + mount(NULL, "/sys", "sysfs", MS_SLAVE, NULL); + r = mount("sys", "/sys", "sysfs", MS_RDONLY, NULL); + _nltst_assert_errno(r == 0); + + return nsdata; +} + +void nltst_netns_leave(struct nltst_netns *nsdata) +{ + ck_assert(nsdata); + ck_assert_int_eq(nsdata->canary, _CANARY); + + /* nltst_netns_leave() was supposed to enter the original namespaces again + * and undo enter. + * + * However, I could get it to work (setns() always fails with EPERM) + * and valgrind on current Ubuntu seems not to support setns() call. + * + * So, do nothing. It's not really a problem, because the next test + * either should unshare yet another namespace, or not care about + * such things. */ + + free(nsdata); +} + +/*****************************************************************************/ + +void _nltst_object_identical(const void *a, const void *b) +{ + struct nl_object *o_a = (void *)a; + struct nl_object *o_b = (void *)b; + + ck_assert(a); + ck_assert(b); + + ck_assert_int_eq(nl_object_identical(o_a, o_b), 1); + ck_assert_int_eq(nl_object_identical(o_b, o_a), 1); + ck_assert_int_eq(nl_object_diff64(o_b, o_a), 0); + ck_assert_int_eq(nl_object_diff64(o_a, o_b), 0); + ck_assert_int_eq(nl_object_diff(o_a, o_b), 0); + ck_assert_int_eq(nl_object_diff(o_b, o_a), 0); +} + +/*****************************************************************************/ + +char *_nltst_object_to_string(const struct nl_object *obj) +{ + size_t L = 1024; + size_t l; + char *s; + struct nl_dump_params dp; + char canary; + + if (!obj) + return strdup("(null)"); + + s = malloc(L); + ck_assert_ptr_nonnull(s); + + canary = _nltst_rand_u32(); + s[L - 1u] = canary; + + dp = (struct nl_dump_params){ + .dp_type = NL_DUMP_LINE, + .dp_buf = s, + .dp_buflen = L - 1u, + }; + + nl_object_dump((struct nl_object *)obj, &dp); + + l = strlen(s); + ck_assert_int_ge(l, 2); + ck_assert_int_lt(l, L); + ck_assert(canary == s[L - 1u]); + s = realloc(s, l + 1); + ck_assert_ptr_nonnull(s); + + ck_assert_msg(s[l - 1u] == '\n', + "expects newline after dump. Got \"%s\"", s); + s[l - 1u] = '\0'; + + ck_assert_msg(!strchr(s, '\n'), + "no further newline expected. Got \"%s\"", s); + + return s; +} + +char *_nltst_objects_to_string(const char *description, + struct nl_object *const *objs, ssize_t len) +{ + _nl_auto_free char *result = NULL; + size_t extra_len; + size_t start_idx; + size_t l; + size_t i; + + l = _nl_ptrarray_len(objs, len); + + start_idx = 0; + extra_len = 300 + (description ? strlen(description) : 0u); + + result = _nltst_malloc0(extra_len); + + _nltst_sprintf(result, &start_idx, &extra_len, ">>> [%zu] objects", l); + if (description) { + _nltst_sprintf(result, &start_idx, &extra_len, " (%s)", + description); + } + _nltst_sprintf(result, &start_idx, &extra_len, "\n"); + + for (i = 0; i < l; i++) { + _nl_auto_free char *s = NULL; + size_t slen; + size_t extra_len; + + s = _nltst_object_to_string(objs[i]); + slen = strlen(s); + + /* Don't bother to calculate the exact extra space. Just always + * reallocate enough. */ + extra_len = slen + 100; + result = _nltst_assert_nonnull( + realloc(result, start_idx + extra_len)); + + _nltst_sprintf(result, &start_idx, &extra_len, ">>> [%zu] %s\n", + i, s); + } + + return _nl_steal_pointer(&result); +} + +struct cache_get_all_data { + struct nl_object **arr; + size_t len; + size_t idx; +}; + +static void _cache_get_all_fcn(struct nl_object *obj, void *user_data) +{ + struct cache_get_all_data *data = user_data; + size_t i; + + ck_assert(obj); + ck_assert_int_lt(data->idx, data->len); + + for (i = 0; i < data->idx; i++) + ck_assert_ptr_ne(data->arr[i], obj); + + data->arr[data->idx++] = obj; +} + +struct nl_object **_nltst_cache_get_all(struct nl_cache *cache, size_t *out_len) +{ + int nitems; + struct cache_get_all_data data = { + .idx = 0, + .len = 0, + }; + size_t len2 = 0; + size_t i; + size_t j; + + ck_assert(cache); + + nitems = nl_cache_nitems(cache); + ck_assert_int_ge(nitems, 0); + + data.len = nitems; + data.arr = malloc(sizeof(struct nl_object *) * (data.len + 1)); + ck_assert_ptr_nonnull(data.arr); + + nl_cache_foreach(cache, _cache_get_all_fcn, &data); + + ck_assert_int_eq(data.idx, data.len); + + ck_assert_int_le(data.len, SSIZE_MAX); + + data.arr[data.len] = NULL; + if (out_len) + *out_len = data.len; + + /* double check the result. */ + for (struct nl_object *obj = nl_cache_get_first(cache); obj; + obj = nl_cache_get_next(obj)) { + ck_assert_ptr_eq(data.arr[len2], obj); + len2++; + } + ck_assert_ptr_null(data.arr[len2]); + + for (i = 0; i < data.len; i++) { + ck_assert_ptr_nonnull(data.arr[i]); + for (j = i + 1; j < data.len; j++) + ck_assert_ptr_ne(data.arr[i], data.arr[j]); + } + + return data.arr; +} + +struct rtnl_link *_nltst_cache_get_link(struct nl_cache *cache, + const char *ifname) +{ + _nl_auto_free struct nl_object **objs = NULL; + struct rtnl_link *link = NULL; + size_t i; + + ck_assert_ptr_nonnull(cache); + ck_assert_ptr_nonnull(ifname); + + objs = _nltst_cache_get_all(cache, NULL); + for (i = 0; objs[i]; i++) { + if (_nl_streq(rtnl_link_get_name((struct rtnl_link *)objs[i]), + ifname)) { + ck_assert_ptr_null(link); + link = (struct rtnl_link *)objs[i]; + } + } + + if (_nltst_rand_u32_range(5) == 0) { + _nl_auto_rtnl_link struct rtnl_link *link2 = NULL; + + link2 = rtnl_link_get_by_name(cache, ifname); + ck_assert_ptr_eq(link2, link); + } + + return link; +} + +/*****************************************************************************/ + +struct nl_sock *_nltst_socket(int protocol) +{ + struct nl_sock *sk; + int r; + + sk = nl_socket_alloc(); + ck_assert(sk); + + r = nl_connect(sk, protocol); + ck_assert_int_eq(r, 0); + + if (_nltst_rand_u32_range(5) == 0) + nl_cache_free(_nltst_rtnl_link_alloc_cache(sk, AF_UNSPEC, 0)); + + if (_nltst_rand_u32_range(5) == 0) + nl_cache_free(_nltst_rtnl_route_alloc_cache( + sk, _nltst_rand_select(AF_UNSPEC, AF_INET, AF_INET6))); + + return sk; +} + +void _nltst_add_link(struct nl_sock *sk, const char *ifname, const char *kind, + int *out_ifindex) +{ + _nl_auto_nl_socket struct nl_sock *sk_free = NULL; + _nl_auto_rtnl_link struct rtnl_link *link = NULL; + _nl_auto_nl_cache struct nl_cache *cache = NULL; + struct rtnl_link *link2; + int ifindex; + int r; + + ck_assert(ifname); + ck_assert(kind); + + if (_nltst_rand_u32_range(5) == 0) + _nltst_assert_link_not_exists(ifname); + + if (!sk) { + sk = _nltst_socket(NETLINK_ROUTE); + sk_free = sk; + } + + link = rtnl_link_alloc(); + ck_assert(link); + + r = rtnl_link_set_type(link, kind); + ck_assert_int_eq(r, 0); + + rtnl_link_set_name(link, ifname); + + r = rtnl_link_add(sk, link, NLM_F_CREATE); + ck_assert_int_eq(r, 0); + + if (!out_ifindex && _nltst_rand_u32_range(5) != 0) + return; + + cache = _nltst_rtnl_link_alloc_cache(sk, AF_UNSPEC, 0); + + link2 = _nltst_cache_get_link(cache, ifname); + ck_assert_ptr_nonnull(link2); + + ifindex = rtnl_link_get_ifindex(link2); + ck_assert_int_gt(ifindex, 0); + + if (out_ifindex) + *out_ifindex = ifindex; +} + +void _nltst_delete_link(struct nl_sock *sk, const char *ifname) +{ + _nl_auto_nl_socket struct nl_sock *sk_free = NULL; + _nl_auto_rtnl_link struct rtnl_link *link = NULL; + + _nltst_assert_link_exists(ifname); + + if (!sk) { + sk = _nltst_socket(NETLINK_ROUTE); + sk_free = sk; + } + + if (_nltst_rand_u32_range(5) == 0) { + _nl_auto_nl_cache struct nl_cache *cache = NULL; + + cache = _nltst_rtnl_link_alloc_cache(sk, AF_UNSPEC, 0); + ck_assert_ptr_nonnull(_nltst_cache_get_link(cache, ifname)); + } + + link = rtnl_link_alloc(); + ck_assert_ptr_nonnull(link); + + rtnl_link_set_name(link, ifname); + + _nltst_assert_retcode(rtnl_link_delete(sk, link)); + + _nltst_assert_link_not_exists(ifname); +} + +void _nltst_get_link(struct nl_sock *sk, const char *ifname, int *out_ifindex, + struct rtnl_link **out_link) +{ + _nl_auto_nl_cache struct nl_cache *cache = NULL; + struct rtnl_link *link; + + if (_nltst_rand_u32_range(5) == 0) + _nltst_assert_link_exists(ifname); + + cache = _nltst_rtnl_link_alloc_cache(sk, AF_UNSPEC, 0); + + link = _nltst_cache_get_link(cache, ifname); + ck_assert(link); + + if (out_ifindex) + *out_ifindex = rtnl_link_get_ifindex(link); + + if (out_link) { + nl_object_get((struct nl_object *)link); + *out_link = link; + } +} + +struct nl_cache *_nltst_rtnl_link_alloc_cache(struct nl_sock *sk, + int addr_family, unsigned flags) +{ + _nl_auto_nl_socket struct nl_sock *sk_free = NULL; + struct nl_cache *cache; + int r; + + if (!sk) { + sk = _nltst_socket(NETLINK_ROUTE); + sk_free = sk; + } + + if (flags == 0 && _nltst_rand_bool()) + r = rtnl_link_alloc_cache(sk, addr_family, &cache); + else + r = rtnl_link_alloc_cache_flags(sk, addr_family, &cache, flags); + + _nltst_assert_retcode(r); + + if (_nltst_rand_u32_range(5) == 0) + free(_nltst_cache_get_all(cache, NULL)); + + return _nltst_assert(cache); +} + +struct nl_cache *_nltst_rtnl_route_alloc_cache(struct nl_sock *sk, + int addr_family) +{ + struct nl_cache *cache; + + ck_assert_ptr_nonnull(sk); + ck_assert(addr_family == AF_UNSPEC || addr_family == AF_INET || + addr_family == AF_INET6); + + _nltst_assert_retcode( + rtnl_route_alloc_cache(sk, addr_family, 0, &cache)); + + if (_nltst_rand_u32_range(5) == 0) + free(_nltst_cache_get_all(cache, NULL)); + + return _nltst_assert(cache); +} + +/*****************************************************************************/ + +char *_nltst_strtok(const char **p_str) +{ + const char *str; + _nl_auto_free char *dst = NULL; + size_t dst_len = 0; + size_t dst_alloc = 0; + size_t i; + + ck_assert_ptr_nonnull(p_str); + + str = _nltst_str_skip_space(*p_str); + + if (str[0] == '\0') { + *p_str = str; + return NULL; + } + + dst_len = 0; + dst_alloc = 10; + dst = malloc(dst_alloc); + ck_assert_ptr_nonnull(dst); + + i = 0; + while (true) { + char ch1 = '\0'; + char ch2 = '\0'; + + /* We take the first word, up until whitespace. Note that backslash + * escape is honored, so you can backslash escape spaces. The returned + * string will NOT have backslashes removed. */ + + if (str[i] == '\0') { + *p_str = &str[i]; + break; + } + if (_nltst_char_is_space(str[i])) { + *p_str = _nltst_str_skip_space(&str[i + 1]); + break; + } + ch1 = str[i]; + if (str[i] == '\\') { + if (str[i + 1] != '\0') { + ch2 = str[i + 1]; + i += 2; + } else + i += 1; + } else + i += 1; + + if (dst_len + 3 >= dst_alloc) { + dst_alloc *= 2; + dst = realloc(dst, dst_alloc); + ck_assert_ptr_nonnull(dst); + } + dst[dst_len++] = ch1; + if (ch2 != '\0') + dst[dst_len++] = ch2; + } + + ck_assert_int_gt(dst_len, 0); + return strndup(dst, dst_len); +} + +char **_nltst_strtokv(const char *str) +{ + _nl_auto_free char *s = NULL; + _nltst_auto_strfreev char **result = NULL; + size_t r_len = 0; + size_t r_alloc = 0; + + if (!str) + return NULL; + + r_alloc = 4; + result = malloc(sizeof(char *) * r_alloc); + ck_assert_ptr_nonnull(result); + + while ((s = _nltst_strtok(&str))) { + if (r_len + 2 >= r_alloc) { + r_alloc *= 2; + result = realloc(result, sizeof(char *) * r_alloc); + ck_assert_ptr_nonnull(result); + } + result[r_len++] = _nl_steal_pointer(&s); + } + ck_assert_int_lt(r_len, r_alloc); + result[r_len] = NULL; + return _nl_steal_pointer(&result); +} + +/*****************************************************************************/ + +void _nltst_assert_link_exists_full(const char *ifname, bool exists) +{ + _nl_auto_nl_cache struct nl_cache *cache = NULL; + _nl_auto_rtnl_link struct rtnl_link *link_clone = NULL; + struct rtnl_link *link; + char path[100]; + struct stat st; + int rnd; + int r; + + ck_assert_pstr_ne(ifname, NULL); + ck_assert_int_lt(strlen(ifname), IFNAMSIZ); + + strcpy(path, "/sys/class/net/"); + strcat(path, ifname); + ck_assert_int_lt(strlen(path), sizeof(path)); + + r = stat(path, &st); + if (exists) { + if (r != 0) { + const int errsv = (errno); + + ck_assert_msg( + 0, + "link(%s) does not exist (stat(%s) failed with r=%d, errno=%d (%s)", + ifname, path, r, errsv, strerror(errsv)); + } + } else { + if (r != -1 && errno != ENOENT) { + const int errsv = (errno); + + ck_assert_msg( + 0, + "link(%s) should not exist but stat(%s) gave r=%d, errno=%d (%s)", + ifname, path, r, errsv, strerror(errsv)); + } + } + + rnd = _nltst_rand_u32_range(3); + + if (rnd == 0) + return; + + cache = _nltst_rtnl_link_alloc_cache(NULL, AF_UNSPEC, 0); + + link = _nltst_cache_get_link(cache, ifname); + if (exists) + ck_assert_ptr_nonnull(link); + else + ck_assert_ptr_null(link); + + if (!link || rnd == 1) + return; + + link_clone = + (struct rtnl_link *)nl_object_clone((struct nl_object *)link); + ck_assert(link_clone); + + /* FIXME: we would expect that the cloned object is identical. It is not. */ + /* _nltst_object_identical(link, link_clone); */ +} + +/*****************************************************************************/ + +bool _nltst_in_ci(void) +{ + return _nl_streq0(getenv("NLTST_IN_CI"), "1"); +} + +/*****************************************************************************/ + +bool _nltst_has_iproute2(void) +{ + static int has = -1; + + if (has == -1) + has = (system("ip link &>/dev/null") == 0); + + return has; +} + +bool _nltst_skip_no_iproute2(const char *msg) +{ + if (_nltst_has_iproute2()) + return false; + + ck_assert_msg( + !_nltst_in_ci(), + "We seem to not have iproute2, but we are in NLTST_IN_CI=1. This is fatal."); + + printf("skip test due to missing iproute2%s%s%s\n", msg ? " (" : "", + msg ?: "", msg ? ")" : ""); + return true; +} + +/*****************************************************************************/ + +void _nltst_select_route_clear(NLTstSelectRoute *select_route) +{ + _nltst_assert_select_route(select_route); + + _nl_clear_free(&select_route->addr); + _nl_clear_free(&select_route->addr_pattern); +} + +int _nltst_select_route_cmp(const NLTstSelectRoute *select_route1, + const NLTstSelectRoute *select_route2) +{ + _NL_CMP_SELF(select_route1, select_route2); + _NL_CMP_FIELD_STR0(select_route1, select_route2, addr); + _NL_CMP_FIELD_STR0(select_route1, select_route2, addr_pattern); + _NL_CMP_FIELD(select_route1, select_route2, addr_family); + _NL_CMP_FIELD(select_route1, select_route2, ifindex); + _NL_CMP_FIELD(select_route1, select_route2, plen); + return 0; +} + +char *_nltst_select_route_to_string(const NLTstSelectRoute *select_route) +{ + char buf[1024]; + const char *family; + char b_plen[100]; + + _nltst_assert_select_route(select_route); + + if (select_route->addr_family == AF_INET) + family = "4 "; + else if (select_route->addr_family == AF_INET6) + family = "6 "; + else + family = ""; + + b_plen[0] = '\0'; + if (select_route->plen != -1) + _nltst_sprintf_arr(b_plen, "/%d", select_route->plen); + + _nltst_sprintf_arr(buf, + "%s" + "%s" + "%s" + "", + family, + select_route->addr_pattern ?: select_route->addr, + b_plen); + return _nltst_strdup(buf); +} + +void _nltst_select_route_parse(const char *str, + NLTstSelectRoute *out_select_route) +{ + _nltst_auto_strfreev char **tokens0 = NULL; + const char *const *tokens; + int addr_family = AF_UNSPEC; + int addr_family2 = AF_UNSPEC; + NLTstIPAddr addr; + int plen = -1; + _nl_auto_free char *addr_free = NULL; + const char *s_addr_pattern; + const char *s_addr = NULL; + const char *s; + const char *s1; + + ck_assert_ptr_nonnull(str); + _nltst_assert_select_route(out_select_route); + + tokens0 = _nltst_strtokv(str); + tokens = (const char *const *)tokens0; + + s = tokens[0]; + if (!s) + ck_abort_msg("invalid empty route pattern \"%s\"", str); + if (_nl_streq(s, "4") || _nl_streq(s, "inet") || + _nl_streq(s, "inet4")) { + addr_family = AF_INET; + tokens++; + } else if (_nl_streq(s, "6") || _nl_streq(s, "inet6")) { + addr_family = AF_INET6; + tokens++; + } + + s_addr_pattern = tokens[0]; + if (!s_addr_pattern) { + ck_abort_msg( + "the route pattern \"%s\" is invalid and contains no destination address", + str); + } + tokens++; + + s = strchr(s_addr_pattern, '/'); + if (s) { + long int plen2; + + if (s == s_addr_pattern) { + ck_abort_msg( + "the route pattern \"%s\" contains no valid destination address", + str); + } + addr_free = strndup(s_addr_pattern, s - s_addr_pattern); + s_addr_pattern = addr_free; + s++; + + errno = 0; + plen2 = strtol(s, (char **)&s1, 10); + if (errno != 0 || s1[0] != '\0' || plen2 < 0 || plen2 > 128 || + ((_nltst_str_find_first_not_from_charset( + s, "0123456789"))[0] != '\0')) { + ck_abort_msg( + "the route pattern \"%s\" contains no valid destination address", + str); + } + plen = plen2; + } + if ((_nltst_str_find_first_not_from_charset( + s_addr_pattern, "abcdefABCDEF0123456789:.?*"))[0] != '\0') { + ck_abort_msg( + "the route pattern \"%s\" contains no valid destination address", + str); + } + if (_nltst_inet_pton(addr_family, s_addr_pattern, &addr_family2, + &addr)) { + free(addr_free); + addr_free = _nltst_inet_ntop_dup(addr_family2, &addr); + s_addr_pattern = addr_free; + addr_family = addr_family2; + } else { + if (addr_family == AF_UNSPEC) { + ck_abort_msg( + "the route pattern \"%s\" contains a wild card address, it requires the address family", + str); + } + } + + ck_assert(addr_family == AF_INET || addr_family == AF_INET6); + + if (plen > (addr_family == AF_INET ? 32 : 128)) { + ck_abort_msg( + "the route pattern \"%s\" contains no valid destination address (prefix length too large)", + str); + } + ck_assert_int_ge(plen, -1); + + s = tokens[0]; + if (s) { + ck_abort_msg("the route pattern \"%s\" contains extra tokens", + str); + } + + if (_nltst_inet_valid(addr_family, s_addr_pattern)) + _NL_SWAP(&s_addr, &s_addr_pattern); + + _nltst_select_route_clear(out_select_route); + memset(out_select_route, 0, sizeof(*out_select_route)); + *out_select_route = (NLTstSelectRoute){ + .addr_family = addr_family, + .plen = plen, + .ifindex = 0, + .addr = s_addr ? strdup(s_addr) : NULL, + .addr_pattern = s_addr_pattern ? strdup(s_addr_pattern) : NULL, + }; + ck_assert(!s_addr || out_select_route->addr); + ck_assert(!s_addr_pattern || out_select_route->addr_pattern); + _nltst_assert_select_route(out_select_route); +} + +bool _nltst_select_route_match(struct nl_object *route, + const NLTstSelectRoute *select_route, + bool do_assert) +{ + struct nl_addr *addr; + struct rtnl_route *route_; + int i; + char sbuf1[200]; + + ck_assert_ptr_nonnull(route); + ck_assert_str_eq(nl_object_get_type(route), "route/route"); + ck_assert_ptr_nonnull(select_route); + + route_ = (struct rtnl_route *)route; + + _nltst_assert_select_route(select_route); + +#define _check(cond, msg, ...) \ + do { \ + if (do_assert) { \ + _nl_auto_free char *s1 = NULL; \ + _nl_auto_free char *s2 = NULL; \ + \ + ck_assert_msg( \ + (cond), \ + "Checking condition \"%s\" for route \"%s\" (expected \"%s\") failed (msg: " msg \ + ")", \ + #cond, (s1 = _nltst_object_to_string(route)), \ + (s2 = _nltst_select_route_to_string( \ + select_route)), \ + ##__VA_ARGS__); \ + } else if (cond) { \ + } else { \ + return false; \ + } \ + } while (0) + + if (select_route->addr_family != AF_UNSPEC) { + _check(rtnl_route_get_family(route_) == + select_route->addr_family, + "mismatching address family"); + } + + if (select_route->ifindex != 0) { + struct nl_list_head *list; + struct rtnl_nexthop *nh; + size_t n; + struct rtnl_nexthop *nh2; + + list = rtnl_route_get_nexthops(route_); + _check(list, "no nexthops for ifindex"); + + n = 0; + nl_list_for_each_entry(nh, list, rtnh_list) { + nh2 = nh; + n++; + } + _check(n == 1, "expects one nexthop for ifindex but got %zu", + n); + ck_assert_ptr_nonnull(nh2); + + i = rtnl_route_nh_get_ifindex(nh2); + _check(i == select_route->ifindex, + "route has unexpected ifindex %d for next hop", i); + } + + addr = rtnl_route_get_dst(route_); + + if (addr) { + if (select_route->addr_family != AF_UNSPEC) { + _check(nl_addr_get_family(addr) == + select_route->addr_family, + "unexecpted address family of dst"); + } + } + + if (select_route->plen != -1) { + _check(addr, "missing address"); + _check(nl_addr_get_prefixlen(addr) == + (unsigned)select_route->plen, + "unexpected prefix length"); + } + if (select_route->addr || select_route->addr_pattern) { + _check(addr, "missing address"); + + _nl_inet_ntop(nl_addr_get_family(addr), + nl_addr_get_binary_addr(addr), sbuf1); + + ck_assert(strlen(sbuf1) > 0); + ck_assert(strlen(sbuf1) < sizeof(sbuf1)); + + if (select_route->addr) { + _check(_nl_streq(sbuf1, select_route->addr), + "unexpected address, \"%s\" does not match \"%s\"", + sbuf1, select_route->addr); + } + if (select_route->addr_pattern) { + _check(fnmatch(select_route->addr_pattern, sbuf1, 0) == + 0, + "unexpected address, \"%s\" does not match pattern \"%s\"", + sbuf1, select_route->addr_pattern); + } + } + +#undef _check + + return true; +} + +/*****************************************************************************/ + +static void _nltst_assert_route_list_print(struct nl_object *const *objs, + size_t l_objs, + const char *const *expected_routes, + size_t l_expected) +{ + _nl_auto_free char *s2 = + _nltst_objects_to_string("route-list", objs, l_objs); + size_t j; + + printf("route content: %s", s2); + printf("expected routes: %zu\n", l_expected); + for (j = 0; j < l_expected; j++) { + printf("expected route: [%zu] %s\n", j, expected_routes[j]); + } + printf("ip-route:>>>\n"); + _nltst_system("ip -d -4 route show table all"); + _nltst_system("ip -d -6 route show table all"); + printf("<<<\n"); +} + +static bool +_nltst_assert_route_list_equal(struct nl_object *const *objs, + const NLTstSelectRoute *expected_route_selects, + size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + struct nl_object *route = objs[i]; + const NLTstSelectRoute *select_route = + &expected_route_selects[i]; + + if (!_nltst_select_route_match(route, select_route, false)) + return false; + } + return true; +} + +typedef struct { + const NLTstSelectRoute *expected_route_selects; +} NltstAssertRouteListPermData; + +static bool +_nltst_assert_route_list_permutate(const NltstAssertRouteListPermData *data, + struct nl_object **objs, size_t idx, + size_t num) +{ + size_t i; + + if (idx + 1 == num) + return true; + + for (i = idx; i < num; i++) { + if (!_nltst_select_route_match( + objs[i], &data->expected_route_selects[idx], + false)) { + /* This entry does not match. We can shortcut this permutation. */ + continue; + } + _nl_swap(&objs[idx], &objs[i]); + if (_nltst_assert_route_list_permutate(data, objs, idx + 1, + num)) { + /* This is a permutation that matches. Leave objs in + * this order and return */ + return true; + } + _nl_swap(&objs[idx], &objs[i]); + } + return false; +} + +void _nltst_assert_route_list(struct nl_object *const *objs, ssize_t len, + const char *const *expected_routes) +{ + const size_t l_expected = _nl_ptrarray_len(expected_routes, -1); + const size_t l_objs = _nl_ptrarray_len(objs, len); + _nl_auto_free NLTstSelectRoute *expected_route_selects = NULL; + _nl_auto_free struct nl_object **objs2 = NULL; + size_t i; + + if (l_expected != l_objs) + goto out_fail; + + if (l_objs == 0) + goto out_free; + + objs2 = _nltst_assert_nonnull( + _nl_memdup(objs, sizeof(struct nl_object *) * l_objs)); + + expected_route_selects = + _nltst_malloc0(sizeof(NLTstSelectRoute) * (l_expected + 1u)); + for (i = 0; i < l_expected; i++) { + _nltst_select_route_parse(expected_routes[i], + &expected_route_selects[i]); + } + + /* Permutate through the list of objects and check that we find at + * least one match with the list of expected routes. */ + if (!_nltst_assert_route_list_permutate( + &((const NltstAssertRouteListPermData){ + .expected_route_selects = expected_route_selects, + }), + objs2, 0, l_objs)) + goto out_fail; + + if (!_nltst_assert_route_list_equal(objs2, expected_route_selects, + len)) { + _nltst_assert_route_list_print(objs, l_objs, expected_routes, + l_expected); + ck_abort_msg( + "there is a in _nltst_assert_route_list_permutate(), the permutation should now match"); + } + + goto out_free; + +out_fail: + _nltst_assert_route_list_print(objs, l_objs, expected_routes, + l_expected); + ck_abort_msg( + "The list of %zu routes did not find a one-to-one match with the list of %zu expected routes", + l_objs, l_expected); + +out_free: + if (expected_route_selects) { + for (i = 0; i < l_expected; i++) { + _nltst_select_route_clear(&expected_route_selects[i]); + } + } +} + +void _nltst_assert_route_cache_v(struct nl_cache *cache, + const char *const *expected_routes) +{ + _nl_auto_free struct nl_object **objs = NULL; + size_t len; + + ck_assert(cache); + ck_assert(expected_routes); + + objs = _nltst_cache_get_all(cache, &len); + + _nltst_assert_route_list(objs, len, expected_routes); +} diff --git a/libnl/tests/nl-test-util.h b/libnl/tests/nl-test-util.h new file mode 100644 index 0000000..981228b --- /dev/null +++ b/libnl/tests/nl-test-util.h @@ -0,0 +1,552 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#ifndef __NL_TEST_UTIL_H__ +#define __NL_TEST_UTIL_H__ + +#include +#include + +#include +#include + +#include "base/nl-base-utils.h" +#include "nl-aux-core/nl-core.h" +#include "nl-aux-route/nl-route.h" + +/*****************************************************************************/ + +static inline void _nltst_strfreev(char **strv) +{ + size_t i; + + if (strv) { + for (i = 0; strv[i]; i++) + free(strv[i]); + free(strv); + } +} + +#define _nltst_auto_strfreev _nl_auto(_nltst_auto_strfreev_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(char **, _nltst_auto_strfreev_fcn, _nltst_strfreev); + +/*****************************************************************************/ + +#ifndef ck_assert_ptr_nonnull +#define ck_assert_ptr_nonnull(ptr) ck_assert(ptr) +#endif + +#ifndef ck_assert_pstr_ne +#define ck_assert_pstr_ne(a, b) \ + do { \ + const char *_a = (a); \ + const char *_b = (b); \ + \ + ck_assert(!(_a == _b || (_a && _b && strcmp(_a, _b) == 0))); \ + } while (0) +#endif + +#ifndef ck_assert_ptr_null +#define ck_assert_ptr_null(ptr) ck_assert(!(ptr)) +#endif + +/*****************************************************************************/ + +#define _nltst_assert(expr) \ + ({ \ + typeof(expr) _expr = (expr); \ + \ + if (!_expr) { \ + ck_assert_msg(0, "assert(%s) failed", #expr); \ + } \ + _expr; \ + }) + +#define _nltst_assert_errno(expr) \ + do { \ + if (expr) { \ + } else { \ + const int _errno = (errno); \ + \ + ck_assert_msg(0, "assert(%s) failed (errno=%d, %s)", \ + #expr, _errno, strerror(_errno)); \ + } \ + } while (0) + +#define _nltst_assert_retcode(expr) \ + do { \ + const int _r = (expr); \ + \ + if (_r < 0) { \ + ck_assert_msg( \ + 0, "command(%s) failed with return code %d", \ + #expr, _r); \ + } \ + if (_r > 0) { \ + ck_assert_msg( \ + 0, \ + "command(%s) has unexpected positive return code %d", \ + #expr, _r); \ + } \ + } while (0) + +/*****************************************************************************/ + +#define __nltst_assert_nonnull(uniq, x) \ + ({ \ + typeof(x) _NL_UNIQ_T(_x, uniq) = (x); \ + \ + ck_assert_ptr_nonnull(_NL_UNIQ_T(_x, uniq)); \ + \ + _NL_UNIQ_T(_x, uniq); \ + }) + +#define _nltst_assert_nonnull(x) __nltst_assert_nonnull(_NL_UNIQ, x) + +/*****************************************************************************/ + +static inline char *_nltst_strdup(const char *str) +{ + return str ? _nltst_assert_nonnull(strdup(str)) : NULL; +} + +#define _nltst_malloc0(size) _nltst_assert_nonnull(calloc((size), 1u)) + +/*****************************************************************************/ + +#define _nltst_sprintf(buf, p_start, p_extra_len, ...) \ + do { \ + char *_buf = (buf); \ + size_t *_p_start = (p_start); \ + size_t *_p_extra_len = (p_extra_len); \ + int _r; \ + \ + _nltst_assert_nonnull(_buf); \ + _nltst_assert_nonnull(_p_start); \ + _nltst_assert_nonnull(_p_extra_len); \ + \ + _buf = &_buf[*_p_start]; \ + _r = snprintf(_buf, *_p_extra_len, __VA_ARGS__); \ + _nltst_assert(_r >= 0); \ + _nltst_assert((size_t)_r < *_p_extra_len); \ + _nltst_assert((size_t)_r == strlen(_buf)); \ + \ + *_p_start += (size_t)_r; \ + *_p_extra_len -= (size_t)_r; \ + } while (0) + +/*****************************************************************************/ + +#define __nltst_sprintf_arr(uniq, arr, fmt, ...) \ + ({ \ + char *const _NL_UNIQ_T(arr, uniq) = (arr); \ + int _NL_UNIQ_T(c, uniq); \ + \ + _NL_STATIC_ASSERT(sizeof(arr) > \ + sizeof(_NL_UNIQ_T(arr, uniq))); \ + \ + _NL_UNIQ_T(c, uniq) = snprintf(_NL_UNIQ_T(arr, uniq), \ + sizeof(arr), fmt, \ + ##__VA_ARGS__); \ + \ + ck_assert_int_lt(_NL_UNIQ_T(c, uniq), sizeof(arr)); \ + \ + _NL_UNIQ_T(arr, uniq); \ + }) + +#define _nltst_sprintf_arr(arr, fmt, ...) \ + __nltst_sprintf_arr(_NL_UNIQ, arr, fmt, ##__VA_ARGS__) + +/*****************************************************************************/ + +void _nltst_get_urandom(void *ptr, size_t len); + +uint32_t _nltst_rand_u32(void); + +static inline uint32_t _nltst_rand_u32_range(uint32_t n) +{ + uint32_t rem; + uint32_t i; + + if (n == 0) + return _nltst_rand_u32(); + if (n == 1) + return 0; + + rem = UINT32_MAX % n; + for (;;) { + i = _nltst_rand_u32(); + if (i < (UINT32_MAX - rem)) + return i % n; + } +} + +static inline bool _nltst_rand_bool(void) +{ + return _nltst_rand_u32() % 2 == 0; +} + +#define _nltst_rand_select(a, ...) \ + ({ \ + const typeof(a) _lst[] = { (a), ##__VA_ARGS__ }; \ + \ + _lst[_nltst_rand_u32_range(_NL_N_ELEMENTS(_lst))]; \ + }) + +/*****************************************************************************/ + +#define _nltst_close(fd) \ + do { \ + int _r; \ + \ + _r = _nl_close((fd)); \ + _nltst_assert_errno(_r == 0); \ + } while (0) + +#define _nltst_fclose(f) \ + do { \ + int _r; \ + \ + _r = fclose((f)); \ + _nltst_assert_errno(_r == 0); \ + } while (0) + +void _nltst_assert_link_exists_full(const char *ifname, bool exists); + +#define _nltst_assert_link_exists(ifname) \ + _nltst_assert_link_exists_full((ifname), true) + +#define _nltst_assert_link_not_exists(ifname) \ + _nltst_assert_link_exists_full((ifname), false) + +/*****************************************************************************/ + +typedef union { + in_addr_t addr4; + struct in_addr a4; + struct in6_addr a6; +} NLTstIPAddr; + +static inline char *_nltst_inet_ntop(int addr_family, const void *addr, + char buf[static INET_ADDRSTRLEN]) +{ + char *r; + + ck_assert(addr_family == AF_INET || addr_family == AF_INET6); + ck_assert(addr); + + r = (char *)inet_ntop(addr_family, addr, buf, + (addr_family == AF_INET) ? INET_ADDRSTRLEN : + INET6_ADDRSTRLEN); + ck_assert_ptr_eq(r, buf); + ck_assert_int_lt(strlen(r), (addr_family == AF_INET) ? + INET_ADDRSTRLEN : + INET6_ADDRSTRLEN); + return r; +} + +static inline char *_nltst_inet_ntop_dup(int addr_family, const void *addr) +{ + return (char *)_nltst_inet_ntop(addr_family, addr, + malloc((addr_family == AF_INET) ? + INET_ADDRSTRLEN : + INET6_ADDRSTRLEN)); +} + +static inline bool _nltst_inet_pton(int addr_family, const char *str, + int *out_addr_family, void *out_addr) +{ + NLTstIPAddr a; + int r; + + ck_assert(addr_family == AF_UNSPEC || addr_family == AF_INET || + addr_family == AF_INET6); + + /* when requesting @out_addr, then the addr-family must either be + * pre-determined or requested too. */ + ck_assert(!out_addr || out_addr_family || addr_family != AF_UNSPEC); + + if (!str) + return false; + + if (addr_family == AF_UNSPEC) + addr_family = strchr(str, ':') ? AF_INET6 : AF_INET; + + r = inet_pton(addr_family, str, &a); + if (r != 1) + return false; + + if (out_addr) { + memcpy(out_addr, &a, + addr_family == AF_INET ? sizeof(in_addr_t) : + sizeof(struct in6_addr)); + } + if (out_addr_family) + *out_addr_family = addr_family; + + return true; +} + +static inline bool _nltst_inet_valid(int addr_family, const char *addr) +{ + return _nltst_inet_pton(addr_family, addr, NULL, NULL); +} + +static inline in_addr_t _nltst_inet4(const char *addr) +{ + in_addr_t addr_bin = 0; + + _nltst_assert(_nltst_inet_pton(AF_INET, addr, NULL, &addr_bin)); + return addr_bin; +} + +static inline struct in6_addr *_nltst_inet6p(const char *addr) +{ + _nl_thread_local static struct in6_addr addr_bin; + + ck_assert(_nltst_inet_pton(AF_INET6, addr, NULL, &addr_bin)); + return &addr_bin; +} + +static inline struct in6_addr _nltst_inet6(const char *addr) +{ + struct in6_addr addr_bin; + + ck_assert(_nltst_inet_pton(AF_INET6, addr, NULL, &addr_bin)); + return addr_bin; +} + +static inline int _nltst_inet_addr_family(int addr_family, const char *addr) +{ + if (!_nltst_inet_pton(addr_family, addr, &addr_family, NULL)) + return AF_UNSPEC; + return addr_family; +} + +static inline char *_nltst_inet_normalize(int addr_family, const char *addr, + char buf[static INET_ADDRSTRLEN]) +{ + NLTstIPAddr a; + + buf[0] = '\0'; + if (!_nltst_inet_pton(addr_family, addr, &addr_family, &a)) + return NULL; + return _nltst_inet_ntop(addr_family, &a, buf); +} + +/*****************************************************************************/ + +char *_nltst_strtok(const char **p_str); + +char **_nltst_strtokv(const char *str); + +#define _nltst_assert_strv_equal(strv1, strv2) \ + do { \ + typeof(strv1) _strv1 = (strv1); \ + typeof(strv2) _strv2 = (strv2); \ + _nl_unused const void *_strv1_typecheck1 = _strv1; \ + _nl_unused const void *_strv2_typecheck1 = _strv2; \ + _nl_unused const char *_strv1_typecheck2 = \ + _strv1 ? _strv1[0] : NULL; \ + _nl_unused const char *_strv2_typecheck2 = \ + _strv2 ? _strv2[0] : NULL; \ + size_t _i; \ + \ + ck_assert_int_eq(!!_strv1, !!_strv2); \ + if (_strv1) { \ + for (_i = 0; _strv1[_i] || _strv2[_i]; _i++) { \ + ck_assert_str_eq(_strv1[_i], _strv2[_i]); \ + } \ + } \ + } while (0) + +#define _NLTST_CHARSET_SPACE " \n\r\t" + +#define _nltst_char_is(ch, charset) (!!(strchr("" charset "", (ch)))) + +#define _nltst_char_is_space(ch) _nltst_char_is(ch, _NLTST_CHARSET_SPACE) + +#define _nltst_str_skip_predicate(s, ch, predicate) \ + ({ \ + typeof(s) _s1 = (s); \ + _nl_unused const char *_s1_typecheck = (_s1); \ + \ + if (_s1) { \ + while (({ \ + const char ch = _s1[0]; \ + \ + (ch != '\0') && (predicate); \ + })) \ + _s1++; \ + } \ + _s1; \ + }) + +#define _nltst_str_skip_charset(s, charset) \ + _nltst_str_skip_predicate(s, _ch, _nltst_char_is(_ch, "" charset "")) + +#define _nltst_str_skip_space(s) \ + _nltst_str_skip_charset(s, _NLTST_CHARSET_SPACE) + +#define _nltst_str_has_prefix_and_space(s, prefix) \ + ({ \ + typeof(s) _s2 = (s); \ + _nl_unused const char *_s2_typecheck = (_s2); \ + const size_t _l = strlen("" prefix ""); \ + \ + if (_s2) { \ + if ((strncmp(_s2, "" prefix "", _l)) == 0 && \ + _nltst_char_is_space(_s2[_l])) \ + _s2 = _nltst_str_skip_space(&_s2[_l + 1]); \ + else \ + _s2 = NULL; \ + } \ + _s2; \ + }) + +#define _nltst_str_find_first_not_from_charset(s, charset) \ + ({ \ + typeof(s) _s3 = (s); \ + _nl_unused const char *_s3_typecheck = (_s3); \ + size_t _l3; \ + \ + _l3 = strspn(_s3, "" charset ""); \ + \ + &_s3[_l3]; \ + }) + +#define _nltst_str_find_first_from_charset(s, charset) \ + ({ \ + typeof(s) _s3 = (s); \ + _nl_unused const char *_s3_typecheck = (_s3); \ + size_t _l3; \ + \ + _l3 = strcspn(_s3, "" charset ""); \ + \ + &_s3[_l3]; \ + }) + +/*****************************************************************************/ + +void nltst_netns_fixture_setup(void); +void nltst_netns_fixture_teardown(void); + +struct nltst_netns; + +struct nltst_netns *nltst_netns_enter(void); +void nltst_netns_leave(struct nltst_netns *nsdata); + +/*****************************************************************************/ + +#define _nltst_system(command) _nltst_assert_retcode(system(command)) + +bool _nltst_in_ci(void); + +bool _nltst_has_iproute2(void); +bool _nltst_skip_no_iproute2(const char *msg); + +/*****************************************************************************/ + +typedef struct { + int addr_family; + int ifindex; + int plen; + char *addr; + char *addr_pattern; +} NLTstSelectRoute; + +#define _nltst_assert_select_route(select_route) \ + do { \ + const NLTstSelectRoute *_select_route_5 = (select_route); \ + \ + ck_assert_ptr_nonnull(_select_route_5); \ + _nl_assert_addr_family_or_unspec( \ + _select_route_5->addr_family); \ + ck_assert_int_ge(_select_route_5->ifindex, 0); \ + ck_assert_int_ge(_select_route_5->plen, -1); \ + ck_assert_int_le( \ + _select_route_5->plen, \ + _select_route_5->addr_family == AF_INET ? 32 : 128); \ + ck_assert(!_select_route_5->addr || ({ \ + char _buf[INET6_ADDRSTRLEN]; \ + const char *_s2; \ + \ + _s2 = _nltst_inet_normalize( \ + _select_route_5->addr_family, \ + _select_route_5->addr, _buf); \ + (_select_route_5->addr_family != AF_UNSPEC && _s2 && \ + _nl_streq(_s2, _select_route_5->addr)); \ + })); \ + ck_assert(!_select_route_5->addr_pattern || \ + !_select_route_5->addr); \ + ck_assert(!_select_route_5->addr_pattern || \ + _select_route_5->addr_family != AF_UNSPEC); \ + } while (0) + +void _nltst_select_route_clear(NLTstSelectRoute *select_route); + +#define _nltst_auto_clear_select_route \ + _nl_auto(_nltst_auto_clear_select_route_fcn) +_NL_AUTO_DEFINE_FCN_STRUCT(NLTstSelectRoute, _nltst_auto_clear_select_route_fcn, + _nltst_select_route_clear); + +int _nltst_select_route_cmp(const NLTstSelectRoute *select_route1, + const NLTstSelectRoute *select_route2); + +static inline bool +_nltst_select_route_equal(const NLTstSelectRoute *select_route1, + const NLTstSelectRoute *select_route2) +{ + return _nltst_select_route_cmp(select_route1, select_route2) == 0; +} + +char *_nltst_select_route_to_string(const NLTstSelectRoute *select_route); + +char *_nltst_objects_to_string(const char *description, + struct nl_object *const *objs, ssize_t len); + +void _nltst_select_route_parse(const char *str, + NLTstSelectRoute *out_select_route); + +bool _nltst_select_route_match(struct nl_object *route, + const NLTstSelectRoute *select_route, + bool do_assert); + +/*****************************************************************************/ + +void _nltst_object_identical(const void *a, const void *b); + +char *_nltst_object_to_string(const struct nl_object *obj); + +struct nl_object **_nltst_cache_get_all(struct nl_cache *cache, + size_t *out_len); + +struct rtnl_link *_nltst_cache_get_link(struct nl_cache *cache, + const char *ifname); + +struct nl_cache *_nltst_rtnl_link_alloc_cache(struct nl_sock *sk, + int addr_family, unsigned flags); + +struct nl_cache *_nltst_rtnl_route_alloc_cache(struct nl_sock *sk, + int addr_family); + +struct nl_sock *_nltst_socket(int protocol); + +void _nltst_add_link(struct nl_sock *sk, const char *ifname, const char *kind, + int *out_ifindex); + +void _nltst_delete_link(struct nl_sock *sk, const char *ifname); + +void _nltst_get_link(struct nl_sock *sk, const char *ifname, int *out_ifindex, + struct rtnl_link **out_link); + +void _nltst_assert_route_list(struct nl_object *const *objs, ssize_t len, + const char *const *expected_routes); + +void _nltst_assert_route_cache_v(struct nl_cache *cache, + const char *const *expected_routes); + +#define _nltst_assert_route_cache(cache, ...) \ + _nltst_assert_route_cache_v(cache, \ + ((const char *const[200]){ __VA_ARGS__ })) + +#endif /* __NL_TEST_UTIL_H__ */ diff --git a/libnl/tests/test-cache-mngr.c b/libnl/tests/test-cache-mngr.c new file mode 100644 index 0000000..cc0e15b --- /dev/null +++ b/libnl/tests/test-cache-mngr.c @@ -0,0 +1,189 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include +#include +#include + +#include + +#include +#include +#include + +static int quit = 0; +static int change = 1; +static int print_ts = 0; + +static struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, +}; + + +static void print_timestamp(FILE *fp) +{ + struct timeval tv; + char tshort[40]; + struct tm *tm; + struct tm tm_buf; + + gettimeofday(&tv, NULL); + tm = localtime_r(&tv.tv_sec, &tm_buf); + + strftime(tshort, sizeof(tshort), "%Y-%m-%dT%H:%M:%S", tm); + fprintf(fp, "[%s.%06ld] ", tshort, tv.tv_usec); +} + +static void change_cb(struct nl_cache *cache, struct nl_object *obj, + int action, void *data) +{ + if (print_ts) + print_timestamp(stdout); + + if (action == NL_ACT_NEW) + printf("NEW "); + else if (action == NL_ACT_DEL) + printf("DEL "); + else if (action == NL_ACT_CHANGE) + printf("CHANGE "); + + nl_object_dump(obj, ¶ms); + fflush(stdout); + + change = 1; +} + +static void sigint(int arg) +{ + quit = 1; +} + +static void print_usage(FILE* stream, const char *name) +{ + fprintf(stream, + "Usage: %s [OPTIONS]... ... \n" + "\n" + "OPTIONS\n" + " -f, --format=TYPE Output format { brief | details | stats }\n" + " Default: brief\n" + " -d, --dump Dump cache content after a change.\n" + " -i, --interval=TIME Dump cache content after TIME seconds when there is no\n" + " change; 0 to disable. Default: 1\n" + " -I, --iter Iterate over all address families when updating caches.\n" + " -t, --tshort Print a short timestamp before change messages.\n" + " -h, --help Show this help text.\n" + , name); +} + +int main(int argc, char *argv[]) +{ + bool dump_on_change = false, dump_on_timeout = true, iter = false; + struct nl_cache_mngr *mngr; + int timeout = 1000, err; + + for (;;) { + static struct option long_opts[] = { + { "format", required_argument, 0, 'f' }, + { "dump", no_argument, 0, 'd' }, + { "interval", required_argument, 0, 'i' }, + { "iter", no_argument, 0, 'I' }, + { "tshort", no_argument, 0, 't' }, + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } + }; + int c; + + c = getopt_long(argc, argv, "hf:di:It", long_opts, NULL); + if (c == -1) + break; + + switch (c) { + char *endptr; + long interval; + + case 'f': + params.dp_type = nl_cli_parse_dumptype(optarg); + break; + + case 'd': + dump_on_change = true; + break; + + case 'i': + errno = 0; + interval = strtol(optarg, &endptr, 0); + if (interval < 0 || errno || *endptr) { + nl_cli_fatal(EINVAL, "Invalid interval \"%s\".\n", + optarg); + exit(1); + } + if (!interval) { + dump_on_timeout = false; + } else { + timeout = interval * 1000; + } + + break; + + case 'I': + iter = true; + break; + + case 't': + print_ts = true; + break; + + case 'h': + print_usage(stdout, argv[0]); + exit(0); + + case '?': + print_usage(stderr, argv[0]); + exit(1); + } + } + + err = nl_cache_mngr_alloc(NULL, NETLINK_ROUTE, NL_AUTO_PROVIDE, &mngr); + if (err < 0) + nl_cli_fatal(err, "Unable to allocate cache manager: %s", + nl_geterror(err)); + + while (optind < argc) { + struct nl_cache *cache; + + err = nl_cache_alloc_name(argv[optind], &cache); + if (err < 0) + nl_cli_fatal(err, "Couldn't add cache %s: %s\n", + argv[optind], nl_geterror(err)); + + if (iter) + nl_cache_set_flags(cache, NL_CACHE_AF_ITER); + + err = nl_cache_mngr_add_cache(mngr, cache, &change_cb, NULL); + if (err < 0) + nl_cli_fatal(err, "Unable to add cache %s: %s", + argv[optind], nl_geterror(err)); + + optind++; + } + + params.dp_fd = stdout; + signal(SIGINT, sigint); + + while (!quit) { + err = nl_cache_mngr_poll(mngr, timeout); + if (err < 0 && err != -NLE_INTR) + nl_cli_fatal(err, "Polling failed: %s", nl_geterror(err)); + + if (dump_on_timeout || (dump_on_change && change)) { + nl_cache_mngr_info(mngr, ¶ms); + fflush(stdout); + change = 0; + } + } + + nl_cache_mngr_free(mngr); + + return 0; +} diff --git a/libnl/tests/test-complex-HTB-with-hash-filters.c b/libnl/tests/test-complex-HTB-with-hash-filters.c new file mode 100644 index 0000000..713ed3e --- /dev/null +++ b/libnl/tests/test-complex-HTB-with-hash-filters.c @@ -0,0 +1,757 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2011 Adrian Ban + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TC_HANDLE(maj, min) (TC_H_MAJ((maj) << 16) | TC_H_MIN(min)) + +/* some functions are copied from iproute-tc tool */ +static 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); + if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL) + return -1; + *val = res; + return 0; +} + +static int get_u32_handle(__u32 *handle, const char *str) +{ + __u32 htid=0, hash=0, nodeid=0; + char *tmp = strchr(str, ':'); + + if (tmp == NULL) { + if (memcmp("0x", str, 2) == 0) + return get_u32(handle, str, 16); + return -1; + } + htid = strtoul(str, &tmp, 16); + if (tmp == str && *str != ':' && *str != 0) + return -1; + if (htid>=0x1000) + return -1; + if (*tmp) { + str = tmp+1; + hash = strtoul(str, &tmp, 16); + if (tmp == str && *str != ':' && *str != 0) + return -1; + if (hash>=0x100) + return -1; + if (*tmp) { + str = tmp+1; + nodeid = strtoul(str, &tmp, 16); + if (tmp == str && *str != 0) + return -1; + if (nodeid>=0x1000) + return -1; + } + } + *handle = (htid<<20)|(hash<<12)|nodeid; + return 0; +} + +static uint32_t get_u32_parse_handle(const char *cHandle) +{ + uint32_t handle=0; + + if(get_u32_handle(&handle, cHandle)) { + printf ("Illegal \"ht\"\n"); + return -1; + } + + if (handle && TC_U32_NODE(handle)) { + printf("\"link\" must be a hash table.\n"); + return -1; + } + return handle; +} + +static int get_tc_classid(__u32 *h, const char *str) +{ + __u32 maj, min; + char *p; + + maj = TC_H_ROOT; + if (strcmp(str, "root") == 0) + goto ok; + maj = TC_H_UNSPEC; + if (strcmp(str, "none") == 0) + goto ok; + maj = strtoul(str, &p, 16); + if (p == str) { + maj = 0; + if (*p != ':') + return -1; + } + if (*p == ':') { + if (maj >= (1<<16)) + return -1; + maj <<= 16; + str = p+1; + min = strtoul(str, &p, 16); + if (*p != 0) + return -1; + if (min >= (1<<16)) + return -1; + maj |= min; + } else if (*p != 0) + return -1; + +ok: + *h = maj; + return 0; +} + +/* + * Function that adds a new filter and attach it to a hash table + * + */ +static int u32_add_filter_on_ht(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, + uint32_t keyval, uint32_t keymask, int keyoff, int keyoffmask, + uint32_t htid, uint32_t classid +) +{ + struct rtnl_cls *cls; + int err; + + //printf("Key Val : 0x%x\n", keyval); + //printf("Key Mask : 0x%x\n", keymask); + + cls=rtnl_cls_alloc(); + if (!(cls)) { + printf("Can not allocate classifier\n"); + nl_socket_free(sock); + exit(1); + } + + rtnl_tc_set_link(TC_CAST(cls), rtnlLink); + + if ((err = rtnl_tc_set_kind(TC_CAST(cls), "u32"))) { + printf("Can not set classifier as u32\n"); + return 1; + } + + rtnl_cls_set_prio(cls, prio); + rtnl_cls_set_protocol(cls, ETH_P_IP); + + rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(1, 0)); + + rtnl_u32_set_hashtable(cls, htid); + + rtnl_u32_add_key_uint32(cls, keyval, keymask, keyoff, keyoffmask); /* 10.0.0.0/8 */ + + rtnl_u32_set_classid(cls, classid); + + rtnl_u32_set_cls_terminal(cls); + + if ((err = rtnl_cls_add(sock, cls, NLM_F_CREATE))) { + printf("Can not add classifier: %s\n", nl_geterror(err)); + return -1; + } + rtnl_cls_put(cls); + return 0; + +} + +/* + * Function that adds a new filter and attach it to a hash table + * and set next hash table link with hash mask + * + */ +static int u32_add_filter_on_ht_with_hashmask(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, + uint32_t keyval, uint32_t keymask, int keyoff, int keyoffmask, + uint32_t htid, uint32_t htlink, uint32_t hmask, uint32_t hoffset +) +{ + struct rtnl_cls *cls; + int err; + + //printf("Key Val : 0x%x\n", keyval); + //printf("Key Mask : 0x%x\n", keymask); + + cls=rtnl_cls_alloc(); + if (!(cls)) { + printf("Can not allocate classifier\n"); + nl_socket_free(sock); + exit(1); + } + + rtnl_tc_set_link(TC_CAST(cls), rtnlLink); + + if ((err = rtnl_tc_set_kind(TC_CAST(cls), "u32"))) { + printf("Can not set classifier as u32\n"); + return 1; + } + + rtnl_cls_set_prio(cls, prio); + rtnl_cls_set_protocol(cls, ETH_P_IP); + + rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(1, 0)); + + if (htid) + rtnl_u32_set_hashtable(cls, htid); + + rtnl_u32_add_key_uint32(cls, keyval, keymask, keyoff, keyoffmask); + + rtnl_u32_set_hashmask(cls, hmask, hoffset); + + rtnl_u32_set_link(cls, htlink); + + + if ((err = rtnl_cls_add(sock, cls, NLM_F_CREATE))) { + printf("Can not add classifier: %s\n", nl_geterror(err)); + return -1; + } + rtnl_cls_put(cls); + return 0; +} + +/* + * function that creates a new hash table + */ +static int u32_add_ht(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, uint32_t htid, uint32_t divisor) +{ + + int err; + struct rtnl_cls *cls; + + cls=rtnl_cls_alloc(); + if (!(cls)) { + printf("Can not allocate classifier\n"); + nl_socket_free(sock); + exit(1); + } + + rtnl_tc_set_link(TC_CAST(cls), rtnlLink); + + if ((err = rtnl_tc_set_kind(TC_CAST(cls), "u32"))) { + printf("Can not set classifier as u32\n"); + return 1; + } + + rtnl_cls_set_prio(cls, prio); + rtnl_cls_set_protocol(cls, ETH_P_IP); + rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(1, 0)); + + rtnl_u32_set_handle(cls, htid, 0x0, 0x0); + //printf("htid: 0x%X\n", htid); + rtnl_u32_set_divisor(cls, divisor); + + if ((err = rtnl_cls_add(sock, cls, NLM_F_CREATE))) { + printf("Can not add classifier: %s\n", nl_geterror(err)); + return -1; + } + rtnl_cls_put(cls); + return 0; +} + +/* + * function that adds a new HTB qdisc and set the default class for unclassified traffic + */ +static int qdisc_add_HTB(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t defaultClass) +{ + + struct rtnl_qdisc *qdisc; + int err; + + /* Allocation of a qdisc object */ + if (!(qdisc = rtnl_qdisc_alloc())) { + printf("Can not allocate Qdisc\n"); + return -1; + } + + //rtnl_tc_set_ifindex(TC_CAST(qdisc), master_index); + rtnl_tc_set_link(TC_CAST(qdisc), rtnlLink); + rtnl_tc_set_parent(TC_CAST(qdisc), TC_H_ROOT); + + //delete the qdisc + //printf("Delete current qdisc\n"); + rtnl_qdisc_delete(sock, qdisc); + //rtnl_qdisc_put(qdisc); + + //add a HTB qdisc + //printf("Add a new HTB qdisc\n"); + rtnl_tc_set_handle(TC_CAST(qdisc), TC_HANDLE(1,0)); + + if ((err = rtnl_tc_set_kind(TC_CAST(qdisc), "htb"))) { + printf("Can not allocate HTB\n"); + return -1; + } + + /* Set default class for unclassified traffic */ + //printf("Set default class for unclassified traffic\n"); + rtnl_htb_set_defcls(qdisc, TC_HANDLE(1, defaultClass)); + rtnl_htb_set_rate2quantum(qdisc, 1); + + /* Submit request to kernel and wait for response */ + if ((err = rtnl_qdisc_add(sock, qdisc, NLM_F_CREATE))) { + printf("Can not allocate HTB Qdisc\n"); + return -1; + } + + /* Return the qdisc object to free memory resources */ + rtnl_qdisc_put(qdisc); + + return 0; +} + +/* + * function that adds a new HTB class and set its parameters + */ +static int class_add_HTB(struct nl_sock *sock, struct rtnl_link *rtnlLink, + uint32_t parentMaj, uint32_t parentMin, + uint32_t childMaj, uint32_t childMin, + uint64_t rate, uint64_t ceil, + uint32_t burst, uint32_t cburst, + uint32_t prio +) +{ + int err; + struct rtnl_class *class; + //struct rtnl_class *class = (struct rtnl_class *) tc; + + //create a HTB class + //class = (struct rtnl_class *)rtnl_class_alloc(); + if (!(class = rtnl_class_alloc())) { + printf("Can not allocate class object\n"); + return 1; + } + // + rtnl_tc_set_link(TC_CAST(class), rtnlLink); + //add a HTB qdisc + //printf("Add a new HTB class with 0x%X:0x%X on parent 0x%X:0x%X\n", childMaj, childMin, parentMaj, parentMin); + rtnl_tc_set_parent(TC_CAST(class), TC_HANDLE(parentMaj, parentMin)); + rtnl_tc_set_handle(TC_CAST(class), TC_HANDLE(childMaj, childMin)); + + if ((err = rtnl_tc_set_kind(TC_CAST(class), "htb"))) { + printf("Can not set HTB to class\n"); + return 1; + } + + //printf("set HTB class prio to %u\n", prio); + rtnl_htb_set_prio((struct rtnl_class *)class, prio); + + if (rate) { + //rate=rate/8; + rtnl_htb_set_rate(class, rate); + } + if (ceil) { + //ceil=ceil/8; + rtnl_htb_set_ceil(class, ceil); + } + + if (burst) { + //printf ("Class HTB: set rate burst: %u\n", burst); + rtnl_htb_set_rbuffer(class, burst); + } + if (cburst) { + //printf ("Class HTB: set rate cburst: %u\n", cburst); + rtnl_htb_set_cbuffer(class, cburst); + } + /* Submit request to kernel and wait for response */ + if ((err = rtnl_class_add(sock, class, NLM_F_CREATE))) { + printf("Can not allocate HTB Qdisc\n"); + return 1; + } + rtnl_class_put(class); + return 0; +} + +/* + * function that adds a HTB root class and set its parameters + */ +static int class_add_HTB_root(struct nl_sock *sock, struct rtnl_link *rtnlLink, + uint64_t rate, uint64_t ceil, + uint32_t burst, uint32_t cburst +) +{ + int err; + struct rtnl_class *class; + + //create a HTB class + class = (struct rtnl_class *)rtnl_class_alloc(); + //class = rtnl_class_alloc(); + if (!class) { + printf("Can not allocate class object\n"); + return 1; + } + // + rtnl_tc_set_link(TC_CAST(class), rtnlLink); + rtnl_tc_set_parent(TC_CAST(class), TC_H_ROOT); + //add a HTB class + //printf("Add a new HTB ROOT class\n"); + rtnl_tc_set_handle(TC_CAST(class), 1); + + if ((err = rtnl_tc_set_kind(TC_CAST(class), "htb"))) { + printf("Can not set HTB to class\n"); + return 1; + } + + if (rate) { + //rate=rate/8; + rtnl_htb_set_rate(class, rate); + } + if (ceil) { + //ceil=ceil/8; + rtnl_htb_set_ceil(class, ceil); + } + + if (burst) { + rtnl_htb_set_rbuffer(class, burst); + } + if (cburst) { + rtnl_htb_set_cbuffer(class, cburst); + } + + /* Submit request to kernel and wait for response */ + if ((err = rtnl_class_add(sock, class, NLM_F_CREATE))) { + printf("Can not allocate HTB Qdisc\n"); + return 1; + } + rtnl_class_put(class); + return 0; +} + +/* + * function that adds a new SFQ qdisc as a leaf for a HTB class + */ +static int qdisc_add_SFQ_leaf(struct nl_sock *sock, struct rtnl_link *rtnlLink, + uint32_t parentMaj, uint32_t parentMin, + int quantum, int limit, int perturb +) +{ + int err; + struct rtnl_qdisc *qdisc; + + if (!(qdisc = rtnl_qdisc_alloc())) { + printf("Can not allocate qdisc object\n"); + return 1; + } + rtnl_tc_set_link(TC_CAST(qdisc), rtnlLink); + rtnl_tc_set_parent(TC_CAST(qdisc), TC_HANDLE(parentMaj, parentMin)); + + rtnl_tc_set_handle(TC_CAST(qdisc), TC_HANDLE(parentMin,0)); + + if ((err = rtnl_tc_set_kind(TC_CAST(qdisc), "sfq"))) { + printf("Can not set SQF class\n"); + return 1; + } + + if(quantum) { + rtnl_sfq_set_quantum(qdisc, quantum); + } else { + rtnl_sfq_set_quantum(qdisc, 16000); // tc default value + } + if(limit) { + rtnl_sfq_set_limit(qdisc, limit); // default is 127 + } + if(perturb) { + rtnl_sfq_set_perturb(qdisc, perturb); // default never perturb the hash + } + + /* Submit request to kernel and wait for response */ + if ((err = rtnl_qdisc_add(sock, qdisc, NLM_F_CREATE))) { + printf("Can not allocate SFQ qdisc\n"); + return -1; + } + + /* Return the qdisc object to free memory resources */ + rtnl_qdisc_put(qdisc); + return 0; +} + + + + +int main() { + + struct nl_sock *sock; + struct rtnl_link *link; + + //struct rtnl_qdisc *qdisc; + //struct rtnl_class *class; + //struct rtnl_cls *cls; + + uint32_t ht, htlink, htid, direction, classid; + //uint32_t hash, hashmask, nodeid, divisor, handle; + //struct rtnl_u32 *f_u32; + char chashlink[16]=""; + + //uint64_t drops, qlen; + + //int master_index; + int err; + + //uint64_t rate=0, ceil=0; + + struct nl_cache *link_cache; + + uint32_t i; + + if (!(sock = nl_socket_alloc())) { + printf("Unable to allocate netlink socket\n"); + exit(1); + } + + if ((err = nl_connect(sock, NETLINK_ROUTE)) < 0 ) { + printf("Nu s-a putut conecta la NETLINK!\n"); + nl_socket_free(sock); + exit(1); + } + + + if ((err = rtnl_link_alloc_cache(sock, AF_UNSPEC, &link_cache)) < 0) { + printf("Unable to allocate link cache: %s\n", + nl_geterror(err)); + nl_socket_free(sock); + exit(1); + } + + /* lookup interface index of eth0 */ + if (!(link = rtnl_link_get_by_name(link_cache, "imq0"))) { + /* error */ + printf("Interface not found\n"); + nl_socket_free(sock); + exit(1); + } + + err=qdisc_add_HTB(sock, link, 0xffff); + //drops = rtnl_tc_get_stat(TC_CAST(qdisc), RTNL_TC_DROPS); + + //printf("Add ROOT HTB class\n"); + err=class_add_HTB_root(sock, link, 12500000, 12500000, 25000, 25000); + err=class_add_HTB(sock, link, 1, 0, 1, 0xffff, 1250000, 12500000, 25000, 25000, 5); + err=qdisc_add_SFQ_leaf(sock, link, 1, 0xffff, 16000, 0, 10); + err=class_add_HTB(sock, link, 1, 1, 1, 0x5, 2000000, 2000000, 25000, 25000, 5); + err=qdisc_add_SFQ_leaf(sock, link, 1, 0x5, 16000, 0, 10); + err=class_add_HTB(sock, link, 1, 1, 1, 0x6, 1000000, 1000000, 25000, 25000, 5); + err=qdisc_add_SFQ_leaf(sock, link, 1, 0x6, 16000, 0, 10); + //err=class_add_HTB(sock, link, 1, 0, 1, 0x7, 1024000, 100000000, 5); + //err=class_add_HTB(sock, link, 1, 0, 1, 0x8, 2048000, 100000000, 5); + //err=class_add_HTB(sock, link, 1, 0, 1, 0x9, 4096000, 100000000, 5); + //err=class_add_HTB(sock, link, 1, 0, 1, 0xa, 8192000, 100000000, 5); + + //printf("Add main hash table\n"); + + /* create u32 first hash filter table + * + */ + /* formula calcul handle: + * uint32_t handle = (htid << 20) | (hash << 12) | nodeid; + */ + + /* + * Upper limit of number of hash tables: 4096 (0xFFF) + * Number of hashes in a table: 256 values (0xFF) + * + */ + + /* using 256 values for hash table + * each entry in hash table match a byte from IP address specified later by a hash key + */ + + for (i = 1; i <= 0xf; i++) + u32_add_ht(sock, link, 1, i, 256); + + /* + * attach a u32 filter to the first hash + * that redirects all traffic and make a hash key + * from the fist byte of the IP address + * + */ + + //divisor=0x0; // unused here + //handle = 0x0; // unused here + //hash = 0x0; // unused here + //htid = 0x0; // unused here + //nodeid = 0x0; // unused here + + // direction = 12 -> source IP + // direction = 16 -> destination IP + direction = 16; + + /* + * which hash table will use + * in our case is hash table no 1 defined previous + * + * There are 2 posibilities to set the the hash table: + * 1. Using function get_u32_handle and sent a string in + * format 10: where 10 is number of the hash table + * 2. Create your own value in format: 0xa00000 + * + */ + strcpy(chashlink, "1:"); + //printf("Hash Link: %s\n", chashlink); + //chashlink=malloc(sizeof(char) * + htlink = 0x0; // is used by get_u32_handle to return the correct value of hash table (link) + + if(get_u32_handle(&htlink, chashlink)) { + printf ("Illegal \"link\""); + nl_socket_free(sock); + exit(1); + } + //printf ("hash link : 0x%X\n", htlink); + //printf ("hash link test : %u\n", (htlink && TC_U32_NODE(htlink))); + + if (htlink && TC_U32_NODE(htlink)) { + printf("\"link\" must be a hash table.\n"); + nl_socket_free(sock); + exit(1); + } + /* the hash mask will hit the hash table (link) no 1: in our case + */ + + /* set the hash key mask */ + //hashmask = 0xFF000000UL; // the mask that is used to match the hash in specific table, in our case for example 1:a with mean the first byte which is 10 in hash table 1 + + /* Here we add a hash filter which match the first byte (see the hashmask value) + * of the source IP (offset 12 in the packet header) + * You can use also offset 16 to match the destination IP + */ + + /* + * Also we need a filter to match our rule + * This mean that we will put a 0.0.0.0/0 filter in our first rule + * that match the offset 12 (source IP) + * Also you can put offset 16 to match the destination IP + */ + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0x0, 0x0, direction, 0, + 0, htlink, 0xff000000, direction); + + /* + * For each first byte that we need to match we will create a new hash table + * For example: you have those clases: 10.0.0.0/24 and 172.16.0.0/23 + * For byte 10 and byte 172 will create a separate hash table that will match the second + * byte from each class. + * + */ + + + // Create a new hash table with prio 1, id 2 and 256 entries +// u32_CreateNewHashTable(sock, link, 1, 2, 256); + // Create a new hash table with prio 1, id 3 and 256 entries +// u32_CreateNewHashTable(sock, link, 1, 3, 256); +// u32_CreateNewHashTable(sock, link, 1, 4, 256); +// u32_CreateNewHashTable(sock, link, 1, 5, 256); + + /* + * Now we will create other filter under (ATENTION) our first hash table (link) 1: + * Previous rule redirects the trafic according the hash mask to hash table (link) no 1: + * Here we will match the hash tables from 1:0 to 1:ff. Under each hash table we will attach + * other rules that matches next byte from IP source/destination IP and we will repeat the + * previous steps. + * + */ + + + // /8 check + + // 10.0.0.0/8 + ht=get_u32_parse_handle("1:a:"); + htid = (ht&0xFFFFF000); + htlink=get_u32_parse_handle("2:"); + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0x0a000000, 0xff000000, direction, 0, + htid, htlink, 0x00ff0000, direction); + + // 172.0.0.0/8 + ht=get_u32_parse_handle("1:ac:"); + htid = (ht&0xFFFFF000); + htlink=get_u32_parse_handle("3:"); + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0xac000000, 0xff000000, direction, 0, + htid, htlink, 0x00ff0000, direction); + + + // /16 check + // 10.0.0.0/16 + ht=get_u32_parse_handle("2:0:"); + htid = (ht&0xFFFFF000); + htlink=get_u32_parse_handle("4:"); + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0x0a000000, 0xffff0000, direction, 0, + htid, htlink, 0x0000ff00, direction); + + // 172.17.0.0/16 + ht=get_u32_parse_handle("3:11:"); + htid = (ht&0xFFFFF000); + htlink=get_u32_parse_handle("5:"); + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0xac110000, 0xffff0000, direction, 0, + htid, htlink, 0x0000ff00, direction); + + // /24 check + // 10.0.9.0/24 + ht=get_u32_parse_handle("4:9:"); + htid = (ht&0xFFFFF000); + htlink=get_u32_parse_handle("6:"); + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0x0a000900, 0xffffff00, direction, 0, + htid, htlink, 0x000000ff, direction); + + // 172.17.2.0/16 + ht=get_u32_parse_handle("5:2:"); + htid = (ht&0xFFFFF000); + htlink=get_u32_parse_handle("7:"); + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0xac110200, 0xffffff00, direction, 0, + htid, htlink, 0x000000ff, direction); + + + // final filters + // 10.0.9.20 + ht=get_u32_parse_handle("6:14:"); + htid = (ht&0xFFFFF000); + + err = get_tc_classid(&classid, "1:5"); + + u32_add_filter_on_ht(sock, link, 1, + 0x0a000914, 0xffffffff, direction, 0, + htid, classid); + + // 172.17.2.120 + ht=get_u32_parse_handle("7:78:"); + htid = (ht&0xFFFFF000); + + err = get_tc_classid(&classid, "1:6"); + + u32_add_filter_on_ht(sock, link, 1, + 0xac110278, 0xffffffff, direction, 0, + htid, classid); + + + + nl_socket_free(sock); + return 0; +} diff --git a/libnl/tests/test-create-bond.c b/libnl/tests/test-create-bond.c new file mode 100644 index 0000000..6edb41f --- /dev/null +++ b/libnl/tests/test-create-bond.c @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct rtnl_link *link; + struct nl_sock *sk; + int err; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + link = rtnl_link_bond_alloc(); + rtnl_link_set_name(link, "my_bond"); + + if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/libnl/tests/test-create-bridge.c b/libnl/tests/test-create-bridge.c new file mode 100644 index 0000000..758aaa2 --- /dev/null +++ b/libnl/tests/test-create-bridge.c @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +#define TEST_BRIDGE_NAME "testbridge" +#define TEST_INTERFACE_NAME "testtap1" + +static int create_bridge(struct nl_sock *sk, struct nl_cache *link_cache, const char *name) { + struct rtnl_link *link; + int err; + + link = rtnl_link_alloc(); + if ((err = rtnl_link_set_type(link, "bridge")) < 0) { + rtnl_link_put(link); + return err; + } + rtnl_link_set_name(link, name); + + if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) { + return err; + } + rtnl_link_put(link); + + return 0; +} + +int main(int argc, char *argv[]) +{ + struct rtnl_link *link; + struct nl_cache *link_cache; + struct nl_sock *sk; + struct rtnl_link *ltap; + int err; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache)) < 0) { + nl_perror(err, "Unable to allocate cache"); + return err; + } + + if ((err = create_bridge(sk, link_cache, TEST_BRIDGE_NAME)) < 0) { + nl_perror(err, "Unable to allocate testbridge"); + return err; + } + + nl_cache_refill(sk, link_cache); + + link = rtnl_link_get_by_name(link_cache, TEST_BRIDGE_NAME); + ltap = rtnl_link_get_by_name(link_cache, TEST_INTERFACE_NAME); + if (!ltap) { + fprintf(stderr, "You should create a tap interface before lunch this test (# tunctl -t %s)\n", TEST_INTERFACE_NAME); + return -1; + } + + if ((err = rtnl_link_enslave(sk, link, ltap)) < 0) { + nl_perror(err, "Unable to enslave interface to his bridge\n"); + return err; + } + + if(rtnl_link_is_bridge(link) == 0) { + fprintf(stderr, "Link is not a bridge\n"); + return -2; + } + + rtnl_link_put(ltap); + nl_cache_refill(sk, link_cache); + ltap = rtnl_link_get_by_name(link_cache, TEST_INTERFACE_NAME); + + if(rtnl_link_get_master(ltap) <= 0) { + fprintf(stderr, "Interface is not attached to a bridge\n"); + return -3; + } + + rtnl_link_put(ltap); + rtnl_link_put(link); + + nl_cache_free(link_cache); + nl_socket_free(sk); + + return 0; +} diff --git a/libnl/tests/test-create-geneve.c b/libnl/tests/test-create-geneve.c new file mode 100644 index 0000000..1816e5b --- /dev/null +++ b/libnl/tests/test-create-geneve.c @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +#define IPv6 1 + +int main(int argc, char *argv[]) +{ + struct rtnl_link *link; + struct nl_addr *addr; + struct nl_sock *sk; + int err; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + link = rtnl_link_geneve_alloc(); + + rtnl_link_set_name(link, "gnv123"); + + if ((err = rtnl_link_geneve_set_id(link, 123)) < 0) { + nl_perror(err, "Unable to set GENEVE ID"); + return err; + } + +#if IPv6 + if ((err = nl_addr_parse("2001:0db8:0:f101::1/64", AF_INET6, &addr)) < 0) { + nl_perror(err, "Unable to parse IPv6 address"); + return err; + } + if ((err = rtnl_link_geneve_set_label(link, 123)) < 0) { + nl_perror(err, "Unable to set label"); + return err; + } + + if ((err = rtnl_link_geneve_set_udp_zero_csum6_tx(link, 1)) < 0) { + nl_perror(err, "Unable to set skip transmitted UDP checksum"); + return err; + } + + if ((err = rtnl_link_geneve_set_udp_zero_csum6_rx(link, 1)) < 0) { + nl_perror(err, "Unable to set skip received UDP checksum"); + return err; + } +#else + if ((err = nl_addr_parse("10.4.4.4", AF_INET, &addr)) < 0) { + nl_perror(err, "Unable to parse IP address"); + return err; + } +#endif + + if ((err = rtnl_link_geneve_set_remote(link, addr)) < 0) { + nl_perror(err, "Unable to set remote address"); + return err; + } + nl_addr_put(addr); + + if ((err = rtnl_link_geneve_set_ttl(link, 1)) < 0) { + nl_perror(err, "Unable to set TTL"); + return err; + } + + if ((err = rtnl_link_geneve_set_tos(link, 0)) < 0) { + nl_perror(err, "Unable to set ToS"); + return err; + } + + if ((err = rtnl_link_geneve_set_port(link, 5060)) < 0) { + nl_perror(err, "Unable to set port"); + return err; + } + + if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/libnl/tests/test-create-ifb.c b/libnl/tests/test-create-ifb.c new file mode 100644 index 0000000..46524fe --- /dev/null +++ b/libnl/tests/test-create-ifb.c @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +#include +#include + +int main(int argc, char *argv[]) +{ + struct rtnl_link *link; + struct nl_sock *sk; + int err; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + link = rtnl_link_alloc(); + rtnl_link_set_type(link, "ifb"); + rtnl_link_set_name(link, "ifb1"); + + if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/libnl/tests/test-create-ip6tnl.c b/libnl/tests/test-create-ip6tnl.c new file mode 100644 index 0000000..b0c0281 --- /dev/null +++ b/libnl/tests/test-create-ip6tnl.c @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +int main(int argc, char *argv[]) +{ + struct nl_cache *link_cache; + struct rtnl_link *link; + struct in6_addr addr; + struct nl_sock *sk; + int err, if_index; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache); + if ( err < 0) { + nl_perror(err, "Unable to allocate cache"); + return err; + } + + if_index = rtnl_link_name2i(link_cache, "ens33"); + if (!if_index) { + fprintf(stderr, "Unable to lookup ens33"); + return -1; + } + + link = rtnl_link_ip6_tnl_alloc(); + if(!link) { + nl_perror(err, "Unable to allocate link"); + return -1; + + } + rtnl_link_set_name(link, "ip6tnl-tun"); + rtnl_link_ip6_tnl_set_link(link, if_index); + + inet_pton(AF_INET6, "2607:f0d0:1002:51::4", &addr); + rtnl_link_ip6_tnl_set_local(link, &addr); + + inet_pton(AF_INET6, "2607:f0d0:1002:52::5", &addr); + rtnl_link_ip6_tnl_set_remote(link, &addr); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + if (err < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/libnl/tests/test-create-ipgre.c b/libnl/tests/test-create-ipgre.c new file mode 100644 index 0000000..31d0eec --- /dev/null +++ b/libnl/tests/test-create-ipgre.c @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +int main(int argc, char *argv[]) +{ + struct nl_cache *link_cache; + struct rtnl_link *link; + struct in_addr addr; + struct nl_sock *sk; + int err, if_index; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache); + if ( err < 0) { + nl_perror(err, "Unable to allocate cache"); + return err; + } + + if_index = rtnl_link_name2i(link_cache, "eno16777736"); + if (!if_index) { + fprintf(stderr, "Unable to lookup eno16777736"); + return -1; + } + + link = rtnl_link_ipgre_alloc(); + if(!link) { + nl_perror(err, "Unable to allocate link"); + return -1; + + } + rtnl_link_set_name(link, "ipgre-tun"); + rtnl_link_ipgre_set_link(link, if_index); + + inet_pton(AF_INET, "192.168.254.12", &addr.s_addr); + rtnl_link_ipgre_set_local(link, addr.s_addr); + + inet_pton(AF_INET, "192.168.254.13", &addr.s_addr); + rtnl_link_ipgre_set_remote(link, addr.s_addr); + + rtnl_link_ipgre_set_ttl(link, 64); + err = rtnl_link_add(sk, link, NLM_F_CREATE); + if (err < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/libnl/tests/test-create-ipgretap.c b/libnl/tests/test-create-ipgretap.c new file mode 100644 index 0000000..8dceaf1 --- /dev/null +++ b/libnl/tests/test-create-ipgretap.c @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +int main(int argc, char *argv[]) +{ + struct nl_cache *link_cache; + struct rtnl_link *link; + struct in_addr addr; + struct nl_sock *sk; + int err, if_index; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache); + if ( err < 0) { + nl_perror(err, "Unable to allocate cache"); + return err; + } + + if_index = rtnl_link_name2i(link_cache, "enp0s5"); + if (!if_index) { + fprintf(stderr, "Unable to lookup enp0s5"); + return -1; + } + + link = rtnl_link_ipgretap_alloc(); + if(!link) { + nl_perror(err, "Unable to allocate link"); + return -1; + + } + rtnl_link_set_name(link, "ipgre-tap"); + rtnl_link_ipgre_set_link(link, if_index); + + inet_pton(AF_INET, "10.211.55.10", &addr.s_addr); + rtnl_link_ipgre_set_local(link, addr.s_addr); + + inet_pton(AF_INET, "10.133.6.33", &addr.s_addr); + rtnl_link_ipgre_set_remote(link, addr.s_addr); + + rtnl_link_ipgre_set_ttl(link, 64); + err = rtnl_link_add(sk, link, NLM_F_CREATE); + if (err < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/libnl/tests/test-create-ipip.c b/libnl/tests/test-create-ipip.c new file mode 100644 index 0000000..2ea3bf0 --- /dev/null +++ b/libnl/tests/test-create-ipip.c @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +int main(int argc, char *argv[]) +{ + struct nl_cache *link_cache; + struct rtnl_link *link; + struct in_addr addr; + struct nl_sock *sk; + int err, if_index; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache); + if ( err < 0) { + nl_perror(err, "Unable to allocate cache"); + return err; + } + + if_index = rtnl_link_name2i(link_cache, "eno16777736"); + if (!if_index) { + fprintf(stderr, "Unable to lookup eno16777736"); + return -1; + } + + link = rtnl_link_ipip_alloc(); + if(!link) { + nl_perror(err, "Unable to allocate link"); + return -1; + } + + rtnl_link_set_name(link, "ipip-tun"); + rtnl_link_ipip_set_link(link, if_index); + + inet_pton(AF_INET, "192.168.254.12", &addr.s_addr); + rtnl_link_ipip_set_local(link, addr.s_addr); + + inet_pton(AF_INET, "192.168.254.13", &addr.s_addr); + rtnl_link_ipip_set_remote(link, addr.s_addr); + + rtnl_link_ipip_set_ttl(link, 64); + err = rtnl_link_add(sk, link, NLM_F_CREATE); + if (err < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/libnl/tests/test-create-ipvlan.c b/libnl/tests/test-create-ipvlan.c new file mode 100644 index 0000000..b0b200c --- /dev/null +++ b/libnl/tests/test-create-ipvlan.c @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct rtnl_link *link; + struct nl_cache *link_cache; + struct nl_sock *sk; + int err, master_index; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache)) < 0) { + nl_perror(err, "Unable to allocate cache"); + return err; + } + + if (!(master_index = rtnl_link_name2i(link_cache, "eth0"))) { + fprintf(stderr, "Unable to lookup eth0"); + return -1; + } + + if (!(link = rtnl_link_ipvlan_alloc())) { + fprintf(stderr, "Unable to allocate link"); + return -1; + } + + rtnl_link_set_link(link, master_index); + rtnl_link_ipvlan_set_mode(link, rtnl_link_ipvlan_str2mode("l2")); + + if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/libnl/tests/test-create-ipvti.c b/libnl/tests/test-create-ipvti.c new file mode 100644 index 0000000..33eb646 --- /dev/null +++ b/libnl/tests/test-create-ipvti.c @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +int main(int argc, char *argv[]) +{ + struct nl_cache *link_cache; + struct rtnl_link *link; + struct in_addr addr; + struct nl_sock *sk; + int err, if_index; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache); + if ( err < 0) { + nl_perror(err, "Unable to allocate cache"); + return err; + } + + if_index = rtnl_link_name2i(link_cache, "ens33"); + if (!if_index) { + fprintf(stderr, "Unable to lookup ens33"); + return -1; + } + + link = rtnl_link_ipvti_alloc(); + if(!link) { + nl_perror(err, "Unable to allocate link"); + return -1; + + } + rtnl_link_set_name(link, "ipvti-tun"); + rtnl_link_ipvti_set_link(link, if_index); + + inet_pton(AF_INET, "192.168.254.12", &addr.s_addr); + rtnl_link_ipvti_set_local(link, addr.s_addr); + + inet_pton(AF_INET, "192.168.254.13", &addr.s_addr); + rtnl_link_ipvti_set_remote(link, addr.s_addr); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + if (err < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/libnl/tests/test-create-macsec.c b/libnl/tests/test-create-macsec.c new file mode 100644 index 0000000..b702f7d --- /dev/null +++ b/libnl/tests/test-create-macsec.c @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include +#include + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct rtnl_link *link; + struct nl_cache *link_cache; + struct nl_sock *sk; + int err, master_index; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache)) < 0) { + nl_perror(err, "Unable to allocate cache"); + return err; + } + + if (!(master_index = rtnl_link_name2i(link_cache, "eth0"))) { + fprintf(stderr, "Unable to lookup eth0"); + return -1; + } + + + link = rtnl_link_macsec_alloc(); + + rtnl_link_set_link(link, master_index); + + rtnl_link_macsec_set_port(link, 10); + rtnl_link_macsec_set_encrypt(link, 1); + rtnl_link_macsec_set_replay_protect(link, 1); + rtnl_link_macsec_set_window(link, 200); + + if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/libnl/tests/test-create-macvlan.c b/libnl/tests/test-create-macvlan.c new file mode 100644 index 0000000..665c502 --- /dev/null +++ b/libnl/tests/test-create-macvlan.c @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +#include + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct rtnl_link *link; + struct nl_cache *link_cache; + struct nl_sock *sk; + struct nl_addr* addr; + int err, master_index; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache)) < 0) { + nl_perror(err, "Unable to allocate cache"); + return err; + } + + if (!(master_index = rtnl_link_name2i(link_cache, "eth0"))) { + fprintf(stderr, "Unable to lookup eth0"); + return -1; + } + + link = rtnl_link_macvlan_alloc(); + + rtnl_link_set_link(link, master_index); + + addr = nl_addr_build(AF_LLC, ether_aton("00:11:22:33:44:55"), ETH_ALEN); + rtnl_link_set_addr(link, addr); + nl_addr_put(addr); + + rtnl_link_macvlan_set_mode(link, rtnl_link_macvlan_str2mode("bridge")); + + if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/libnl/tests/test-create-macvtap.c b/libnl/tests/test-create-macvtap.c new file mode 100644 index 0000000..c3e19a7 --- /dev/null +++ b/libnl/tests/test-create-macvtap.c @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +#include + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct rtnl_link *link; + struct nl_cache *link_cache; + struct nl_sock *sk; + struct nl_addr* addr; + int err, master_index; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache)) < 0) { + nl_perror(err, "Unable to allocate cache"); + return err; + } + + if (!(master_index = rtnl_link_name2i(link_cache, "eth0"))) { + fprintf(stderr, "Unable to lookup eth0"); + return -1; + } + + link = rtnl_link_macvtap_alloc(); + + rtnl_link_set_link(link, master_index); + + addr = nl_addr_build(AF_LLC, ether_aton("00:11:22:33:44:55"), ETH_ALEN); + rtnl_link_set_addr(link, addr); + nl_addr_put(addr); + + rtnl_link_macvtap_set_mode(link, rtnl_link_macvtap_str2mode("bridge")); + + if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/libnl/tests/test-create-sit.c b/libnl/tests/test-create-sit.c new file mode 100644 index 0000000..5c7aaeb --- /dev/null +++ b/libnl/tests/test-create-sit.c @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +int main(int argc, char *argv[]) +{ + struct nl_cache *link_cache; + struct rtnl_link *link; + struct in_addr addr; + struct nl_sock *sk; + int err, if_index; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache); + if ( err < 0) { + nl_perror(err, "Unable to allocate cache"); + return err; + } + + if_index = rtnl_link_name2i(link_cache, "eno16777736"); + if (!if_index) { + fprintf(stderr, "Unable to lookup eno16777736"); + return -1; + } + + link = rtnl_link_sit_alloc(); + if(!link) { + nl_perror(err, "Unable to allocate link"); + return -1; + + } + rtnl_link_set_name(link, "sit-tun"); + rtnl_link_sit_set_link(link, if_index); + + inet_pton(AF_INET, "192.168.254.12", &addr.s_addr); + rtnl_link_sit_set_local(link, addr.s_addr); + + inet_pton(AF_INET, "192.168.254.13", &addr.s_addr); + rtnl_link_sit_set_remote(link, addr.s_addr); + + rtnl_link_sit_set_ttl(link, 64); + err = rtnl_link_add(sk, link, NLM_F_CREATE); + if (err < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/libnl/tests/test-create-veth.c b/libnl/tests/test-create-veth.c new file mode 100644 index 0000000..fda4d2e --- /dev/null +++ b/libnl/tests/test-create-veth.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct rtnl_link *link; + struct nl_sock *sk; + int err; + struct rtnl_link *peer; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + +#if 0 + rtnl_link_veth_add(sk, "veth2", "veth3", getpid()); +#else + link = rtnl_link_veth_alloc(); + if (!link) { + nl_perror(err, "Unable to alloc link"); + return err; + } + + rtnl_link_set_name(link, "veth8"); + peer = rtnl_link_veth_get_peer(link); + rtnl_link_set_name(peer, "veth9"); + + if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + printf("peer is %s\n", rtnl_link_get_name(peer)); + rtnl_link_put(peer); + rtnl_link_put(link); +#endif + nl_close(sk); + + return 0; +} diff --git a/libnl/tests/test-create-vlan.c b/libnl/tests/test-create-vlan.c new file mode 100644 index 0000000..e2cd2b1 --- /dev/null +++ b/libnl/tests/test-create-vlan.c @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct rtnl_link *link; + struct nl_cache *link_cache; + struct nl_sock *sk; + int err, master_index; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache)) < 0) { + nl_perror(err, "Unable to allocate cache"); + return err; + } + + if (!(master_index = rtnl_link_name2i(link_cache, "eth0"))) { + fprintf(stderr, "Unable to lookup eth0"); + return -1; + } + + link = rtnl_link_vlan_alloc(); + + rtnl_link_set_link(link, master_index); + + rtnl_link_vlan_set_id(link, 10); + + if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/libnl/tests/test-create-vrf.c b/libnl/tests/test-create-vrf.c new file mode 100644 index 0000000..b5f7176 --- /dev/null +++ b/libnl/tests/test-create-vrf.c @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct nl_cache *link_cache; + struct rtnl_link *link, *link2; + struct nl_sock *sk; + uint32_t tb_id; + int err; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + if (!(link = rtnl_link_vrf_alloc())) { + fprintf(stderr, "Unable to allocate link"); + return -1; + } + + rtnl_link_set_name(link, "vrf-red"); + + if ((err = rtnl_link_vrf_set_tableid(link, 10)) < 0) { + nl_perror(err, "Unable to set VRF table id"); + return err; + } + + if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache)) < 0) { + nl_perror(err, "Unable to allocate cache"); + return err; + } + + if (!(link2 = rtnl_link_get_by_name(link_cache, "vrf-red"))) { + fprintf(stderr, "Unable to lookup vrf-red"); + return -1; + } + + if ((err = rtnl_link_vrf_get_tableid(link2, &tb_id)) < 0) { + nl_perror(err, "Unable to get VRF table id"); + return err; + } + + if (tb_id != 10) { + fprintf(stderr, "Mismatch with VRF table id\n"); + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/libnl/tests/test-create-vxlan.c b/libnl/tests/test-create-vxlan.c new file mode 100644 index 0000000..6d2fd94 --- /dev/null +++ b/libnl/tests/test-create-vxlan.c @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct rtnl_link *link; + struct nl_addr *addr; + struct nl_sock *sk; + int err; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + link = rtnl_link_vxlan_alloc(); + + rtnl_link_set_name(link, "vxlan128"); + + if ((err = rtnl_link_vxlan_set_id(link, 128)) < 0) { + nl_perror(err, "Unable to set VXLAN network identifier"); + return err; + } + + if ((err = nl_addr_parse("239.0.0.1", AF_INET, &addr)) < 0) { + nl_perror(err, "Unable to parse IP address"); + return err; + } + + if ((err = rtnl_link_vxlan_set_group(link, addr)) < 0) { + nl_perror(err, "Unable to set multicast IP address"); + return err; + } + nl_addr_put(addr); + + if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/libnl/tests/test-create-xfrmi.c b/libnl/tests/test-create-xfrmi.c new file mode 100644 index 0000000..8fd7c3a --- /dev/null +++ b/libnl/tests/test-create-xfrmi.c @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +int main(int argc, char *argv[]) +{ + struct nl_cache *link_cache; + struct rtnl_link *link; + struct nl_sock *sk; + int err, if_index; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache); + if (err < 0) { + nl_perror(err, "Unable to allocate cache"); + return err; + } + + if_index = rtnl_link_name2i(link_cache, "eth0"); + if (!if_index) { + fprintf(stderr, "Unable to lookup eth0"); + return -1; + } + + link = rtnl_link_xfrmi_alloc(); + if (!link) { + nl_perror(err, "Unable to allocate link"); + return -1; + + } + + rtnl_link_set_name(link, "ipsec0"); + rtnl_link_xfrmi_set_link(link, if_index); + rtnl_link_xfrmi_set_if_id(link, 16); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + if (err < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + return 0; +} diff --git a/libnl/tests/test-delete-link.c b/libnl/tests/test-delete-link.c new file mode 100644 index 0000000..58c70f0 --- /dev/null +++ b/libnl/tests/test-delete-link.c @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +#include +#include + +int main(int argc, char *argv[]) +{ + struct rtnl_link *link; + struct nl_sock *sk; + int err; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + link = rtnl_link_alloc(); + rtnl_link_set_name(link, "my_bond"); + + if ((err = rtnl_link_delete(sk, link)) < 0) { + nl_perror(err, "Unable to delete link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/libnl/tests/test-genl.c b/libnl/tests/test-genl.c new file mode 100644 index 0000000..5e32c6d --- /dev/null +++ b/libnl/tests/test-genl.c @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +#include + +#include + +static struct nla_policy attr_policy[TASKSTATS_TYPE_MAX+1] = { + [TASKSTATS_TYPE_PID] = { .type = NLA_U32 }, + [TASKSTATS_TYPE_TGID] = { .type = NLA_U32 }, + [TASKSTATS_TYPE_STATS] = { .minlen = sizeof(struct taskstats) }, + [TASKSTATS_TYPE_AGGR_PID] = { .type = NLA_NESTED }, + [TASKSTATS_TYPE_AGGR_TGID] = { .type = NLA_NESTED }, +}; + + +static int parse_cmd_new(struct nl_cache_ops *unused, struct genl_cmd *cmd, + struct genl_info *info, void *arg) +{ + struct nlattr *attrs[TASKSTATS_TYPE_MAX+1]; + struct nlattr *nested; + int err; + + if (info->attrs[TASKSTATS_TYPE_AGGR_PID]) + nested = info->attrs[TASKSTATS_TYPE_AGGR_PID]; + else if (info->attrs[TASKSTATS_TYPE_AGGR_TGID]) + nested = info->attrs[TASKSTATS_TYPE_AGGR_TGID]; + else { + fprintf(stderr, "Invalid taskstats message: Unable to find " + "nested attribute/\n"); + return NL_SKIP; + } + + err = nla_parse_nested(attrs, TASKSTATS_TYPE_MAX, nested, attr_policy); + if (err < 0) { + nl_perror(err, "Error while parsing generic netlink message"); + return err; + } + + + if (attrs[TASKSTATS_TYPE_STATS]) { + struct taskstats *stats = nla_data(attrs[TASKSTATS_TYPE_STATS]); + + printf("%s pid %u uid %u gid %u parent %u\n", + stats->ac_comm, stats->ac_pid, stats->ac_uid, + stats->ac_gid, stats->ac_ppid); + } + + return 0; +} + +static int parse_cb(struct nl_msg *msg, void *arg) +{ + return genl_handle_msg(msg, NULL); +} + +static struct genl_cmd cmds[] = { + { + .c_id = TASKSTATS_CMD_NEW, + .c_name = "taskstats_new()", + .c_maxattr = TASKSTATS_TYPE_MAX, + .c_attr_policy = attr_policy, + .c_msg_parser = &parse_cmd_new, + }, +}; + +static struct genl_ops ops = { + .o_name = TASKSTATS_GENL_NAME, + .o_cmds = cmds, + .o_ncmds = ARRAY_SIZE(cmds), +}; + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nl_msg *msg; + void *hdr; + int err; + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_GENERIC); + + if ((err = genl_register_family(&ops)) < 0) + nl_cli_fatal(err, "Unable to register Generic Netlink family"); + + if ((err = genl_ops_resolve(sock, &ops)) < 0) + nl_cli_fatal(err, "Unable to resolve family name"); + + if (genl_ctrl_resolve(sock, "nlctrl") != GENL_ID_CTRL) + nl_cli_fatal(NLE_INVAL, "Resolving of \"nlctrl\" failed"); + + msg = nlmsg_alloc(); + if (msg == NULL) + nl_cli_fatal(NLE_NOMEM, "Unable to allocate netlink message"); + + hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, ops.o_id, + 0, 0, TASKSTATS_CMD_GET, TASKSTATS_GENL_VERSION); + if (hdr == NULL) + nl_cli_fatal(ENOMEM, "Unable to write genl header"); + + if ((err = nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, 1)) < 0) + nl_cli_fatal(err, "Unable to add attribute: %s", nl_geterror(err)); + + if ((err = nl_send_auto(sock, msg)) < 0) + nl_cli_fatal(err, "Unable to send message: %s", nl_geterror(err)); + + nlmsg_free(msg); + + if ((err = nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, + parse_cb, NULL)) < 0) + nl_cli_fatal(err, "Unable to modify valid message callback"); + + if ((err = nl_recvmsgs_default(sock)) < 0) + nl_cli_fatal(err, "Unable to receive message: %s", nl_geterror(err)); + + nl_close(sock); + nl_socket_free(sock); + + return 0; +} diff --git a/libnl/tests/test-loopback-up-down.c b/libnl/tests/test-loopback-up-down.c new file mode 100644 index 0000000..fdb5a98 --- /dev/null +++ b/libnl/tests/test-loopback-up-down.c @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +#include + +int main(void) +{ + struct nl_sock *sk; + struct rtnl_link *link, *change; + struct nl_cache *cache; + int err = 0; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &cache)) < 0) { + nl_perror(err, "Unable to allocate cache"); + goto out; + } + + if (!(link = rtnl_link_get_by_name(cache, "lo"))) { + fprintf(stderr, "Interface not found\n"); + err = 1; + goto out; + } + + /* exit if the loopback interface is already deactivated */ + err = rtnl_link_get_flags(link); + if (!(err & IFF_UP)) { + err = 0; + goto out; + } + + change = rtnl_link_alloc(); + rtnl_link_unset_flags(change, IFF_UP); + + if ((err = rtnl_link_change(sk, link, change, 0)) < 0) { + nl_perror(err, "Unable to deactivate lo"); + goto out; + } + + rtnl_link_set_flags(change, IFF_UP); + if ((err = rtnl_link_change(sk, link, change, 0)) < 0) { + nl_perror(err, "Unable to activate lo"); + goto out; + } + + err = 0; + +out: + nl_socket_free(sk); + return err; +} diff --git a/libnl/tests/test-nf-cache-mngr.c b/libnl/tests/test-nf-cache-mngr.c new file mode 100644 index 0000000..7dcc9fc --- /dev/null +++ b/libnl/tests/test-nf-cache-mngr.c @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +#include + +static void change_cb(struct nl_cache *cache, struct nl_object *obj, + int action, void *data) +{ + struct nfnl_ct *ct = (struct nfnl_ct *) obj; + static struct nl_addr *hack = NULL; + + if (!hack) + nl_addr_parse("194.88.212.233", AF_INET, &hack); + + if (!nl_addr_cmp(hack, nfnl_ct_get_src(ct, 1)) || + !nl_addr_cmp(hack, nfnl_ct_get_dst(ct, 1))) { + struct nl_dump_params dp = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + + printf("UPDATE "); + nl_object_dump(obj, &dp); + } +} + +int main(int argc, char *argv[]) +{ + struct nl_cache_mngr *mngr; + struct nl_sock *sock; + struct nl_cache *ct; + int err; + + sock = nl_cli_alloc_socket(); + + err = nl_cache_mngr_alloc(sock, NETLINK_NETFILTER, NL_AUTO_PROVIDE, &mngr); + if (err < 0) { + nl_perror(err, "nl_cache_mngr_alloc"); + return -1; + } + + err = nl_cache_mngr_add(mngr, "netfilter/ct", &change_cb, NULL, &ct); + if (err < 0) { + nl_perror(err, "nl_cache_mngr_add(netfilter/ct)"); + return -1; + } + + for (;;) { + int err = nl_cache_mngr_poll(mngr, 5000); + if (err < 0) { + nl_perror(err, "nl_cache_mngr_poll()"); + return -1; + } + + } + + nl_cache_mngr_free(mngr); + + return 0; +} diff --git a/libnl/tests/test-socket-creation.c b/libnl/tests/test-socket-creation.c new file mode 100644 index 0000000..00259a1 --- /dev/null +++ b/libnl/tests/test-socket-creation.c @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#include "nl-default.h" + +#include + +int main(int argc, char *argv[]) +{ + struct nl_sock *h[1025]; + int i; + + h[0] = nl_socket_alloc(); + printf("Created handle with port 0x%x\n", + nl_socket_get_local_port(h[0])); + nl_socket_free(h[0]); + h[0] = nl_socket_alloc(); + printf("Created handle with port 0x%x\n", + nl_socket_get_local_port(h[0])); + nl_socket_free(h[0]); + + for (i = 0; i < 1025; i++) { + h[i] = nl_socket_alloc(); + if (h[i] == NULL) + nl_perror(ENOMEM, "Unable to allocate socket"); + else + printf("Created handle with port 0x%x\n", + nl_socket_get_local_port(h[i])); + } + + return 0; +} diff --git a/libnl/tests/test-u32-filter-with-actions.c b/libnl/tests/test-u32-filter-with-actions.c new file mode 100644 index 0000000..d676013 --- /dev/null +++ b/libnl/tests/test-u32-filter-with-actions.c @@ -0,0 +1,409 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + * + * Stolen from tests/test-complex-HTB-with-hash-filters.c + */ + +#include "nl-default.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TC_HANDLE(maj, min) (TC_H_MAJ((maj) << 16) | TC_H_MIN(min)) + +/* some functions are copied from iproute-tc tool */ +static 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); + if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL) + return -1; + *val = res; + return 0; +} + +static int get_u32_handle(__u32 *handle, const char *str) +{ + __u32 htid=0, hash=0, nodeid=0; + char *tmp = strchr(str, ':'); + + if (tmp == NULL) { + if (memcmp("0x", str, 2) == 0) + return get_u32(handle, str, 16); + return -1; + } + htid = strtoul(str, &tmp, 16); + if (tmp == str && *str != ':' && *str != 0) + return -1; + if (htid>=0x1000) + return -1; + if (*tmp) { + str = tmp+1; + hash = strtoul(str, &tmp, 16); + if (tmp == str && *str != ':' && *str != 0) + return -1; + if (hash>=0x100) + return -1; + if (*tmp) { + str = tmp+1; + nodeid = strtoul(str, &tmp, 16); + if (tmp == str && *str != 0) + return -1; + if (nodeid>=0x1000) + return -1; + } + } + *handle = (htid<<20)|(hash<<12)|nodeid; + return 0; +} + +static uint32_t get_u32_parse_handle(const char *cHandle) +{ + uint32_t handle=0; + + if(get_u32_handle(&handle, cHandle)) { + printf ("Illegal \"ht\"\n"); + return -1; + } + + if (handle && TC_U32_NODE(handle)) { + printf("\"link\" must be a hash table.\n"); + return -1; + } + return handle; +} + +/* + * Function that adds a new filter and attach it to a hash table + * and set next hash table link with hash mask + * + */ +static +int u32_add_filter_on_ht_with_hashmask(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, + uint32_t keyval, uint32_t keymask, int keyoff, int keyoffmask, + uint32_t htid, uint32_t htlink, uint32_t hmask, uint32_t hoffset, struct rtnl_act *act, struct rtnl_act *act2) +{ + struct rtnl_cls *cls; + int err; + + cls=rtnl_cls_alloc(); + if (!(cls)) { + printf("Can not allocate classifier\n"); + nl_socket_free(sock); + exit(1); + } + + rtnl_tc_set_link(TC_CAST(cls), rtnlLink); + + if ((err = rtnl_tc_set_kind(TC_CAST(cls), "u32"))) { + printf("Can not set classifier as u32\n"); + return 1; + } + + rtnl_cls_set_prio(cls, prio); + rtnl_cls_set_protocol(cls, ETH_P_IP); + + rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(0xffff, 0)); + + if (htid) + rtnl_u32_set_hashtable(cls, htid); + + rtnl_u32_add_key_uint32(cls, keyval, keymask, keyoff, keyoffmask); + + rtnl_u32_set_hashmask(cls, hmask, hoffset); + + rtnl_u32_set_link(cls, htlink); + + rtnl_u32_add_action(cls, act); + + rtnl_u32_add_action(cls, act2); + + + if ((err = rtnl_cls_add(sock, cls, NLM_F_CREATE))) { + printf("Can not add classifier: %s\n", nl_geterror(err)); + return -1; + } + rtnl_cls_put(cls); + return 0; +} + +/* + * function that creates a new hash table + */ +static +int u32_add_ht(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, uint32_t htid, uint32_t divisor) +{ + + int err; + struct rtnl_cls *cls; + + cls=rtnl_cls_alloc(); + if (!(cls)) { + printf("Can not allocate classifier\n"); + nl_socket_free(sock); + exit(1); + } + + rtnl_tc_set_link(TC_CAST(cls), rtnlLink); + + if ((err = rtnl_tc_set_kind(TC_CAST(cls), "u32"))) { + printf("Can not set classifier as u32\n"); + return 1; + } + + rtnl_cls_set_prio(cls, prio); + rtnl_cls_set_protocol(cls, ETH_P_IP); + rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(0xffff, 0)); + + rtnl_u32_set_handle(cls, htid, 0x0, 0x0); + //printf("htid: 0x%X\n", htid); + rtnl_u32_set_divisor(cls, divisor); + + if ((err = rtnl_cls_add(sock, cls, NLM_F_CREATE))) { + printf("Can not add classifier: %s\n", nl_geterror(err)); + return -1; + } + rtnl_cls_put(cls); + return 0; +} + +/* + * function that adds a new ingress qdisc and set the default class for unclassified traffic + */ +static +int qdisc_add_ingress(struct nl_sock *sock, struct rtnl_link *rtnlLink) +{ + + struct rtnl_qdisc *qdisc; + int err; + + /* Allocation of a qdisc object */ + if (!(qdisc = rtnl_qdisc_alloc())) { + printf("Can not allocate Qdisc\n"); + return -1; + } + + //rtnl_tc_set_ifindex(TC_CAST(qdisc), master_index); + rtnl_tc_set_link(TC_CAST(qdisc), rtnlLink); + rtnl_tc_set_parent(TC_CAST(qdisc), TC_H_ROOT); + + //printf("Delete current qdisc\n"); + rtnl_qdisc_delete(sock, qdisc); + //rtnl_qdisc_put(qdisc); + + rtnl_tc_set_handle(TC_CAST(qdisc), TC_HANDLE(0xffff, 0)); + + if ((err = rtnl_tc_set_kind(TC_CAST(qdisc), "ingress"))) { + printf("Can not allocate ingress\n"); + return -1; + } + + /* Submit request to kernel and wait for response */ + if ((err = rtnl_qdisc_add(sock, qdisc, NLM_F_CREATE))) { + printf("Can not allocate ingress Qdisc\n"); + return -1; + } + + /* Return the qdisc object to free memory resources */ + rtnl_qdisc_put(qdisc); + + return 0; +} + +int main(void) +{ + struct nl_sock *sock; + struct rtnl_link *link; + uint32_t ht, htlink, htid, direction; + char chashlink[16]=""; + int err; + struct nl_cache *link_cache; + struct rtnl_act *act, *act2; + uint32_t i; + + if (!(sock = nl_socket_alloc())) { + printf("Unable to allocate netlink socket\n"); + exit(1); + } + + if ((err = nl_connect(sock, NETLINK_ROUTE)) < 0 ) { + printf("Nu s-a putut conecta la NETLINK!\n"); + nl_socket_free(sock); + exit(1); + } + + if ((err = rtnl_link_alloc_cache(sock, AF_UNSPEC, &link_cache)) < 0) { + printf("Unable to allocate link cache: %s\n", + nl_geterror(err)); + nl_socket_free(sock); + exit(1); + } + + /* lookup interface index of eth0 */ + if (!(link = rtnl_link_get_by_name(link_cache, "eth0"))) { + /* error */ + printf("Interface not found\n"); + nl_socket_free(sock); + exit(1); + } + + err=qdisc_add_ingress(sock, link); + //printf("Add main hash table\n"); + + /* create u32 first hash filter table + * + */ + /* formula calcul handle: + * uint32_t handle = (htid << 20) | (hash << 12) | nodeid; + */ + + /* + * Upper limit of number of hash tables: 4096 (0xFFF) + * Number of hashes in a table: 256 values (0xFF) + * + */ + + /* using 256 values for hash table + * each entry in hash table match a byte from IP address specified later by a hash key + */ + + for (i = 1; i <= 0xf; i++) + u32_add_ht(sock, link, 1, i, 256); + + /* + * attach a u32 filter to the first hash + * that redirects all traffic and make a hash key + * from the fist byte of the IP address + * + */ + + //divisor=0x0; // unused here + //handle = 0x0; // unused here + //hash = 0x0; // unused here + //htid = 0x0; // unused here + //nodeid = 0x0; // unused here + + // direction = 12 -> source IP + // direction = 16 -> destination IP + direction = 16; + + /* + * which hash table will use + * in our case is hash table no 1 defined previous + * + * There are 2 posibilities to set the the hash table: + * 1. Using function get_u32_handle and sent a string in + * format 10: where 10 is number of the hash table + * 2. Create your own value in format: 0xa00000 + * + */ + strcpy(chashlink, "1:"); + //printf("Hash Link: %s\n", chashlink); + //chashlink=malloc(sizeof(char) * + htlink = 0x0; // is used by get_u32_handle to return the correct value of hash table (link) + + if(get_u32_handle(&htlink, chashlink)) { + printf ("Illegal \"link\""); + nl_socket_free(sock); + exit(1); + } + //printf ("hash link : 0x%X\n", htlink); + //printf ("hash link test : %u\n", (htlink && TC_U32_NODE(htlink))); + + if (htlink && TC_U32_NODE(htlink)) { + printf("\"link\" must be a hash table.\n"); + nl_socket_free(sock); + exit(1); + } + + /* the hash mask will hit the hash table (link) no 1: in our case + */ + + /* set the hash key mask */ + //hashmask = 0xFF000000UL; // the mask that is used to match the hash in specific table, in our case for example 1:a with mean the first byte which is 10 in hash table 1 + + /* Here we add a hash filter which match the first byte (see the hashmask value) + * of the source IP (offset 12 in the packet header) + * You can use also offset 16 to match the destination IP + */ + + /* + * Also we need a filter to match our rule + * This mean that we will put a 0.0.0.0/0 filter in our first rule + * that match the offset 12 (source IP) + * Also you can put offset 16 to match the destination IP + */ + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0x0, 0x0, direction, 0, + 0, htlink, 0xff000000, direction, NULL, NULL); + + /* + * For each first byte that we need to match we will create a new hash table + * For example: you have those clases: 10.0.0.0/24 and 172.16.0.0/23 + * For byte 10 and byte 172 will create a separate hash table that will match the second + * byte from each class. + * + */ + + + /* + * Now we will create other filter under (ATENTION) our first hash table (link) 1: + * Previous rule redirects the trafic according the hash mask to hash table (link) no 1: + * Here we will match the hash tables from 1:0 to 1:ff. Under each hash table we will attach + * other rules that matches next byte from IP source/destination IP and we will repeat the + * previous steps. + * + */ + act = rtnl_act_alloc(); + if (!act) { + printf("rtnl_act_alloc() returns %p\n", act); + return -1; + } + rtnl_tc_set_kind(TC_CAST(act), "skbedit"); + rtnl_skbedit_set_queue_mapping(act, 4); + rtnl_skbedit_set_action(act, TC_ACT_PIPE); + + act2 = rtnl_act_alloc(); + if (!act2) { + printf("rtnl_act_alloc() returns %p\n", act2); + return -1; + } + rtnl_tc_set_kind(TC_CAST(act2), "mirred"); + rtnl_mirred_set_action(act2, TCA_EGRESS_REDIR); + rtnl_mirred_set_policy(act2, TC_ACT_STOLEN); + rtnl_mirred_set_ifindex(act2, rtnl_link_name2i(link_cache, "eth1")); + // /8 check + + // 10.0.0.0/8 + ht=get_u32_parse_handle("1:a:"); + htid = (ht&0xFFFFF000); + htlink=get_u32_parse_handle("2:"); + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0x0a000000, 0xff000000, direction, 0, + htid, htlink, 0x00ff0000, direction, act, act2); + + rtnl_act_put(act); + nl_socket_free(sock); + return 0; +} diff --git a/libnl/tools/build_release.sh b/libnl/tools/build_release.sh new file mode 100755 index 0000000..e12a195 --- /dev/null +++ b/libnl/tools/build_release.sh @@ -0,0 +1,87 @@ +#!/bin/bash + +# script to create libnl release. +# Steps: +# - create new commit, bumping version number +# - run this script +# - check all is good +# - tag the commit (signed) +# git tag -m 'libnl-3.2.26-rc1' -s libnl3_2_26rc1 HEAD +# - publish the tarballs +# - push the commit to github +# - publish the tag on github +# - publish the tarballs on github +# - send ANN email + + +die() { + printf '%s\n' "$@" + exit 1 +} + +set -x +set -e + +cd "$(dirname "$0")/.." +git_dir="$(readlink -f "$(git rev-parse --show-toplevel)")" +test -f "$git_dir/tools/build_release.sh" + +Build() { + test "$(git status --porcelain)" = "" || die "there are uncommited changes" + git clean -fdx + ./autogen.sh + ./configure + pushd ./doc/ + ./configure --enable-doc + popd + make -j 5 + make -C doc + make -C doc gendoc + make -j 5 distcheck + make -C doc dist + echo "Build: success" +} + +Copy() { + local V="$(ls -1 ./libnl-*.tar.gz | sed -n 's/^\.\/libnl-\(3\.[0-9]\+\.[0-9]\+\(-rc[0-9]\)\?\).tar.gz$/\1/p')" + test -n "$V" + local REL="libnl-$V" + rm -rf "./$REL" + mkdir "./$REL" + ln "./libnl-$V.tar.gz" "./$REL/" + ln "./doc/libnl-doc-$V.tar.gz" "./$REL/" + ( + cd "./$REL/" + for F in "libnl-$V.tar.gz" "libnl-doc-$V.tar.gz"; do + md5sum "./$F" > "./$F.md5sum" + sha256sum "./$F" > "./$F.sha256sum" + if [ "$NO_GPG_SIGN" != 1 ]; then + gpg ${GPG_USER--u thaller@redhat.com} --armor --verbose -o "./$F.sig" --detach-sign "./$F" + fi + done + ) + tar -cvf "./$REL.tar" "./$REL/" + echo "Copy: success" +} + +BuildAll() { + Build + Copy + echo "BuildAll: success" +} + +case "$1" in + Build) + Build + ;; + Copy) + Copy + ;; + BuildAll) + BuildAll + ;; + *) + echo "SYNOPSIS: $0 Build|Copy|BuildAll" + echo "WARNING: does a git-clean first!!" + ;; +esac diff --git a/libnl/tools/clang-format-container.sh b/libnl/tools/clang-format-container.sh new file mode 100755 index 0000000..69a68b1 --- /dev/null +++ b/libnl/tools/clang-format-container.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +set -e + +die() { + echo "$@" >&2 + exit 1 +} + +DIR="$(realpath "$(dirname "$0")/../")" +cd "$DIR" + +# The correct clang-format version is the one from the Fedora version used in our +# github action pipeline. Parse it from ".github/workflows/ci.yml". +FEDORA_VERSION="$(sed -n 's/^ image: fedora:\([0-9]\+\)$/\1/p' .github/workflows/ci.yml)" + +test -n "$FEDORA_VERSION" || die "Could not detect the Fedora version in .github/workflows/ci.yml" + +IMAGENAME="libnl-code-format-f$FEDORA_VERSION" + +ARGS=( "$@" ) + +if ! podman image exists "$IMAGENAME" ; then + echo "Building image \"$IMAGENAME\"..." + podman build \ + --squash-all \ + --tag "$IMAGENAME" \ + -f <(cat <&2 + exit 1 +} + +EXCLUDE_PATHS_TOPLEVEL=( + "include/linux-private" +) + +# The following files are currently not formatted with clang. +# Exclude them too. +EXCLUDE_PATHS_TOPLEVEL+=( + "include/netlink/addr.h" + "include/netlink/attr.h" + "include/netlink/cache-api.h" + "include/netlink/cache.h" + "include/netlink/cli/addr.h" + "include/netlink/cli/cls.h" + "include/netlink/cli/link.h" + "include/netlink/cli/mdb.h" + "include/netlink/cli/neigh.h" + "include/netlink/cli/qdisc.h" + "include/netlink/cli/route.h" + "include/netlink/cli/tc.h" + "include/netlink/cli/utils.h" + "include/netlink/data.h" + "include/netlink/errno.h" + "include/netlink/fib_lookup/lookup.h" + "include/netlink/fib_lookup/request.h" + "include/netlink/genl/ctrl.h" + "include/netlink/genl/family.h" + "include/netlink/genl/genl.h" + "include/netlink/genl/mngt.h" + "include/netlink/handlers.h" + "include/netlink/hash.h" + "include/netlink/hashtable.h" + "include/netlink/idiag/idiagnl.h" + "include/netlink/idiag/meminfo.h" + "include/netlink/idiag/msg.h" + "include/netlink/idiag/req.h" + "include/netlink/idiag/vegasinfo.h" + "include/netlink/list.h" + "include/netlink/msg.h" + "include/netlink/netfilter/ct.h" + "include/netlink/netfilter/exp.h" + "include/netlink/netfilter/log.h" + "include/netlink/netfilter/log_msg.h" + "include/netlink/netfilter/netfilter.h" + "include/netlink/netfilter/nfnl.h" + "include/netlink/netfilter/queue.h" + "include/netlink/netfilter/queue_msg.h" + "include/netlink/netlink-compat.h" + "include/netlink/netlink-kernel.h" + "include/netlink/netlink.h" + "include/netlink/object.h" + "include/netlink/route/act/skbedit.h" + "include/netlink/route/action.h" + "include/netlink/route/addr.h" + "include/netlink/route/class.h" + "include/netlink/route/classifier.h" + "include/netlink/route/cls/basic.h" + "include/netlink/route/cls/cgroup.h" + "include/netlink/route/cls/ematch.h" + "include/netlink/route/cls/ematch/cmp.h" + "include/netlink/route/cls/ematch/meta.h" + "include/netlink/route/cls/ematch/nbyte.h" + "include/netlink/route/cls/ematch/text.h" + "include/netlink/route/cls/flower.h" + "include/netlink/route/cls/fw.h" + "include/netlink/route/cls/matchall.h" + "include/netlink/route/cls/police.h" + "include/netlink/route/cls/u32.h" + "include/netlink/route/link.h" + "include/netlink/route/link/api.h" + "include/netlink/route/link/bonding.h" + "include/netlink/route/link/bridge.h" + "include/netlink/route/link/can.h" + "include/netlink/route/link/geneve.h" + "include/netlink/route/link/inet.h" + "include/netlink/route/link/inet6.h" + "include/netlink/route/link/info-api.h" + "include/netlink/route/link/ip6gre.h" + "include/netlink/route/link/ip6tnl.h" + "include/netlink/route/link/ip6vti.h" + "include/netlink/route/link/ipgre.h" + "include/netlink/route/link/ipip.h" + "include/netlink/route/link/ipvlan.h" + "include/netlink/route/link/ipvti.h" + "include/netlink/route/link/macsec.h" + "include/netlink/route/link/macvlan.h" + "include/netlink/route/link/macvtap.h" + "include/netlink/route/link/ppp.h" + "include/netlink/route/link/sit.h" + "include/netlink/route/link/sriov.h" + "include/netlink/route/link/team.h" + "include/netlink/route/link/vlan.h" + "include/netlink/route/link/vxlan.h" + "include/netlink/route/link/xfrmi.h" + "include/netlink/route/mdb.h" + "include/netlink/route/neighbour.h" + "include/netlink/route/neightbl.h" + "include/netlink/route/netconf.h" + "include/netlink/route/nexthop.h" + "include/netlink/route/pktloc.h" + "include/netlink/route/qdisc.h" + "include/netlink/route/qdisc/cbq.h" + "include/netlink/route/qdisc/dsmark.h" + "include/netlink/route/qdisc/fifo.h" + "include/netlink/route/qdisc/fq_codel.h" + "include/netlink/route/qdisc/hfsc.h" + "include/netlink/route/qdisc/htb.h" + "include/netlink/route/qdisc/mqprio.h" + "include/netlink/route/qdisc/netem.h" + "include/netlink/route/qdisc/plug.h" + "include/netlink/route/qdisc/prio.h" + "include/netlink/route/qdisc/red.h" + "include/netlink/route/qdisc/sfq.h" + "include/netlink/route/route.h" + "include/netlink/route/rtnl.h" + "include/netlink/route/rule.h" + "include/netlink/route/tc-api.h" + "include/netlink/route/tc.h" + "include/netlink/socket.h" + "include/netlink/types.h" + "include/netlink/utils.h" + "include/netlink/xfrm/ae.h" + "include/netlink/xfrm/lifetime.h" + "include/netlink/xfrm/sa.h" + "include/netlink/xfrm/selector.h" + "include/netlink/xfrm/sp.h" + "include/netlink/xfrm/template.h" + "include/nl-priv-dynamic-core/cache-api.h" + "include/nl-priv-dynamic-core/object-api.h" + "lib/addr.c" + "lib/attr.c" + "lib/cache.c" + "lib/cache_mngr.c" + "lib/cache_mngt.c" + "lib/cli/cls/basic.c" + "lib/cli/cls/cgroup.c" + "lib/cli/qdisc/bfifo.c" + "lib/cli/qdisc/blackhole.c" + "lib/cli/qdisc/fq_codel.c" + "lib/cli/qdisc/hfsc.c" + "lib/cli/qdisc/htb.c" + "lib/cli/qdisc/ingress.c" + "lib/cli/qdisc/pfifo.c" + "lib/cli/qdisc/plug.c" + "lib/data.c" + "lib/error.c" + "lib/fib_lookup/lookup.c" + "lib/fib_lookup/request.c" + "lib/genl/ctrl.c" + "lib/genl/family.c" + "lib/genl/genl.c" + "lib/genl/mngt.c" + "lib/genl/nl-genl.h" + "lib/handlers.c" + "lib/hash.c" + "lib/hashtable.c" + "lib/idiag/idiag.c" + "lib/idiag/idiag_meminfo_obj.c" + "lib/idiag/idiag_msg_obj.c" + "lib/idiag/idiag_req_obj.c" + "lib/idiag/idiag_vegasinfo_obj.c" + "lib/mpls.c" + "lib/mpls.h" + "lib/msg.c" + "lib/netfilter/ct.c" + "lib/netfilter/ct_obj.c" + "lib/netfilter/exp.c" + "lib/netfilter/exp_obj.c" + "lib/netfilter/log.c" + "lib/netfilter/log_msg.c" + "lib/netfilter/log_msg_obj.c" + "lib/netfilter/log_obj.c" + "lib/netfilter/netfilter.c" + "lib/netfilter/nfnl.c" + "lib/netfilter/queue.c" + "lib/netfilter/queue_msg.c" + "lib/netfilter/queue_msg_obj.c" + "lib/netfilter/queue_obj.c" + "lib/nl-core.h" + "lib/nl.c" + "lib/object.c" + "lib/route/act.c" + "lib/route/act/gact.c" + "lib/route/act/mirred.c" + "lib/route/act/skbedit.c" + "lib/route/act/vlan.c" + "lib/route/addr.c" + "lib/route/class.c" + "lib/route/classid.c" + "lib/route/cls.c" + "lib/route/cls/basic.c" + "lib/route/cls/cgroup.c" + "lib/route/cls/ematch.c" + "lib/route/cls/ematch/cmp.c" + "lib/route/cls/ematch/container.c" + "lib/route/cls/ematch/meta.c" + "lib/route/cls/ematch/nbyte.c" + "lib/route/cls/ematch/text.c" + "lib/route/cls/flower.c" + "lib/route/cls/fw.c" + "lib/route/cls/mall.c" + "lib/route/cls/police.c" + "lib/route/cls/u32.c" + "lib/route/link-sriov.h" + "lib/route/link.c" + "lib/route/link/api.c" + "lib/route/link/bonding.c" + "lib/route/link/bridge.c" + "lib/route/link/can.c" + "lib/route/link/dummy.c" + "lib/route/link/geneve.c" + "lib/route/link/ifb.c" + "lib/route/link/inet.c" + "lib/route/link/inet6.c" + "lib/route/link/ip6gre.c" + "lib/route/link/ip6tnl.c" + "lib/route/link/ip6vti.c" + "lib/route/link/ipgre.c" + "lib/route/link/ipip.c" + "lib/route/link/ipvlan.c" + "lib/route/link/ipvti.c" + "lib/route/link/link-api.h" + "lib/route/link/macsec.c" + "lib/route/link/macvlan.c" + "lib/route/link/ppp.c" + "lib/route/link/sit.c" + "lib/route/link/sriov.c" + "lib/route/link/team.c" + "lib/route/link/veth.c" + "lib/route/link/vlan.c" + "lib/route/link/vrf.c" + "lib/route/link/vxlan.c" + "lib/route/link/xfrmi.c" + "lib/route/mdb.c" + "lib/route/neigh.c" + "lib/route/netconf.c" + "lib/route/nexthop-encap.h" + "lib/route/nexthop.c" + "lib/route/nexthop_encap.c" + "lib/route/nh_encap_mpls.c" + "lib/route/pktloc.c" + "lib/route/qdisc.c" + "lib/route/qdisc/blackhole.c" + "lib/route/qdisc/cbq.c" + "lib/route/qdisc/dsmark.c" + "lib/route/qdisc/fifo.c" + "lib/route/qdisc/fq_codel.c" + "lib/route/qdisc/hfsc.c" + "lib/route/qdisc/htb.c" + "lib/route/qdisc/ingress.c" + "lib/route/qdisc/mqprio.c" + "lib/route/qdisc/netem.c" + "lib/route/qdisc/plug.c" + "lib/route/qdisc/prio.c" + "lib/route/qdisc/red.c" + "lib/route/qdisc/sfq.c" + "lib/route/qdisc/tbf.c" + "lib/route/route.c" + "lib/route/route_obj.c" + "lib/route/route_utils.c" + "lib/route/rtnl.c" + "lib/route/rule.c" + "lib/route/tc-api.h" + "lib/route/tc.c" + "lib/socket.c" + "lib/utils.c" + "lib/version.c" + "lib/xfrm/ae.c" + "lib/xfrm/lifetime.c" + "lib/xfrm/sa.c" + "lib/xfrm/selector.c" + "lib/xfrm/sp.c" + "lib/xfrm/template.c" + "python/netlink/utils.h" + "src/genl-ctrl-list.c" + "src/idiag-socket-details.c" + "src/lib/addr.c" + "src/lib/cls.c" + "src/lib/ct.c" + "src/lib/exp.c" + "src/lib/link.c" + "src/lib/neigh.c" + "src/lib/route.c" + "src/lib/tc.c" + "src/lib/utils.c" + "src/nf-ct-add.c" + "src/nf-ct-events.c" + "src/nf-ct-list.c" + "src/nf-exp-add.c" + "src/nf-exp-delete.c" + "src/nf-exp-list.c" + "src/nf-log.c" + "src/nf-monitor.c" + "src/nf-queue.c" + "src/nl-addr-add.c" + "src/nl-addr-delete.c" + "src/nl-addr-list.c" + "src/nl-class-add.c" + "src/nl-class-delete.c" + "src/nl-class-list.c" + "src/nl-classid-lookup.c" + "src/nl-cls-add.c" + "src/nl-cls-delete.c" + "src/nl-cls-list.c" + "src/nl-fib-lookup.c" + "src/nl-link-enslave.c" + "src/nl-link-list.c" + "src/nl-link-release.c" + "src/nl-link-set.c" + "src/nl-link-stats.c" + "src/nl-list-caches.c" + "src/nl-list-sockets.c" + "src/nl-monitor.c" + "src/nl-neigh-add.c" + "src/nl-neigh-delete.c" + "src/nl-neigh-list.c" + "src/nl-neightbl-list.c" + "src/nl-pktloc-lookup.c" + "src/nl-qdisc-add.c" + "src/nl-qdisc-delete.c" + "src/nl-qdisc-list.c" + "src/nl-route-add.c" + "src/nl-route-delete.c" + "src/nl-route-get.c" + "src/nl-route-list.c" + "src/nl-rule-list.c" + "src/nl-tctree-list.c" + "src/nl-util-addr.c" + "tests/test-cache-mngr.c" + "tests/test-complex-HTB-with-hash-filters.c" + "tests/test-create-bridge.c" + "tests/test-create-geneve.c" + "tests/test-create-ip6tnl.c" + "tests/test-create-ipgre.c" + "tests/test-create-ipgretap.c" + "tests/test-create-ipip.c" + "tests/test-create-ipvti.c" + "tests/test-create-macsec.c" + "tests/test-create-macvlan.c" + "tests/test-create-macvtap.c" + "tests/test-create-sit.c" + "tests/test-create-veth.c" + "tests/test-create-xfrmi.c" + "tests/test-genl.c" + "tests/test-nf-cache-mngr.c" + "tests/test-socket-creation.c" + "tests/test-u32-filter-with-actions.c" +) + +DIR_ROOT="$(git rev-parse --show-toplevel)" || die "not inside a git repository" +DIR_PREFIX="$(git rev-parse --show-prefix)" || die "not inside a git repository" + +if [ ! -f "$DIR_ROOT/.clang-format" ]; then + die "Error: the clang-format file in \"$DIR_ROOT\" does not exist" +fi + +if ! command -v clang-format &> /dev/null; then + die "Error: clang-format is not installed. On RHEL/Fedora/CentOS run 'dnf install clang-tools-extra'" +fi + +if test -n "$DIR_PREFIX"; then + EXCLUDE_PATHS=() + for e in "${EXCLUDE_PATHS_TOPLEVEL[@]}"; do + REGEX="^$DIR_PREFIX([^/].*)$" + if [[ "$e" =~ $REGEX ]]; then + EXCLUDE_PATHS+=("${BASH_REMATCH[1]}") + fi + done +else + EXCLUDE_PATHS=("${EXCLUDE_PATHS_TOPLEVEL[@]}") +fi + +FILES=() +HAS_EXPLICIT_FILES=0 +SHOW_FILENAMES=0 +TEST_ONLY=0 +CHECK_UPSTREAM= + +usage() { + printf "Usage: %s [OPTION]... [FILE]...\n" "$(basename "$0")" + printf "Reformat source files using clang-format.\n\n" + printf "If no file is given the script runs on the whole codebase.\n" + printf "OPTIONS:\n" + printf " -h Print this help message.\n" + printf " -i Reformat files (the default).\n" + printf " -n|--dry-run Only check the files (contrary to \"-i\").\n" + printf " -a|--all Check all files (the default).\n" + printf " -u|--upstream COMMIT Check only files from \`git diff --name-only COMMIT\` (contrary to \"-a\").\n" + printf " This also affects directories given in the [FILE] list, but not files.\n" + printf " If this is the last parameter and COMMIT is unspecified/empty, it defaults to \"main\".\n" + printf " -F|--fast Same as \`-u HEAD^\`.\n" + printf " -l|--show-filenames Only print the filenames that would be checked/formatted\n" + printf " -- Separate options from filenames/directories\n" + if [ -n "${_LIBNL_CODE_FORMAT_CONTAINER+x}" ] ; then + printf "\n" + printf "Command runs inside container image \"$_LIBNL_CODE_FORMAT_CONTAINER\".\n" + printf "Delete/renew image with \`podman rmi \"$_LIBNL_CODE_FORMAT_CONTAINER\"\`.\n" + fi +} + +ls_files_exist() { + local OLD_IFS="$IFS" + local f + + IFS=$'\n' + for f in $(cat) ; do + test -f "$f" && printf '%s\n' "$f" + done + IFS="$OLD_IFS" +} + +ls_files_filter() { + local OLD_IFS="$IFS" + local f + + IFS=$'\n' + for f in $(cat) ; do + local found=1 + local p + for p; do + [[ "$f" = "$p/"* ]] && found= + [[ "$f" = "$p" ]] && found= + done + test -n "$found" && printf '%s\n' "$f" + done + IFS="$OLD_IFS" +} + +g_ls_files() { + local pattern="$1" + shift + + if [ -z "$CHECK_UPSTREAM" ]; then + git ls-files -- "$pattern" + else + git diff --no-renames --name-only "$CHECK_UPSTREAM" -- "$pattern" \ + | ls_files_exist + fi | ls_files_filter "$@" +} + +HAD_DASHDASH=0 +while (( $# )); do + if [ "$HAD_DASHDASH" = 0 ]; then + case "$1" in + -h) + usage + exit 0 + ;; + -l|--show-filenames) + SHOW_FILENAMES=1 + shift + continue + ;; + -a|--all) + CHECK_UPSTREAM= + shift + continue + ;; + -u|--upstream) + shift + CHECK_UPSTREAM="$1" + test -n "$CHECK_UPSTREAM" || CHECK_UPSTREAM=main + shift || : + continue + ;; + -F|--fast) + CHECK_UPSTREAM='HEAD^' + shift + continue + ;; + -n|--dry-run) + TEST_ONLY=1 + shift + continue + ;; + -i) + TEST_ONLY=0 + shift + continue + ;; + --) + HAD_DASHDASH=1 + shift + continue + ;; + esac + fi + if [ -d "$1" ]; then + while IFS='' read -r line; + do FILES+=("$line") + done < <(CHECK_UPSTREAM="$CHECK_UPSTREAM" g_ls_files "${1}/*.[hc]" "${EXCLUDE_PATHS[@]}") + elif [ -f "$1" ]; then + FILES+=("$1") + else + usage >&2 + echo >&2 + die "Unknown argument \"$1\" which also is neither a file nor a directory." + fi + shift + HAS_EXPLICIT_FILES=1 +done + +if [ $HAS_EXPLICIT_FILES = 0 ]; then + while IFS='' read -r line; do + FILES+=("$line") + done < <(CHECK_UPSTREAM="$CHECK_UPSTREAM" g_ls_files '*.[ch]' "${EXCLUDE_PATHS[@]}") +fi + +if [ $SHOW_FILENAMES = 1 ]; then + for f in "${FILES[@]}" ; do + printf '%s\n' "$f" + done + exit 0 +fi + +if [ "${#FILES[@]}" = 0 ]; then + if [ -z "$CHECK_UPSTREAM" ]; then + die "Error: no files to check" + fi + exit 0 +fi + +FLAGS_TEST=( --Werror -n --ferror-limit=1 ) + +if [ $TEST_ONLY = 1 ]; then + # We assume that all formatting is correct. In that mode, passing + # all filenames to clang-format is significantly faster. + # + # Only in case of an error, we iterate over the files one by one + # until we find the first invalid file. + for f in "${FILES[@]}"; do + [ -f "$f" ] || die "Error: file \"$f\" does not exist (or is not a regular file)" + done + clang-format "${FLAGS_TEST[@]}" "${FILES[@]}" &>/dev/null && exit 0 + for f in "${FILES[@]}"; do + [ -f "$f" ] || die "Error: file \"$f\" does not exist (or is not a regular file)" + if ! clang-format "${FLAGS_TEST[@]}" "$f" &>/dev/null; then + FF="$(mktemp)" + trap 'rm -f "$FF"' EXIT + clang-format "$f" 2>/dev/null > "$FF" + git --no-pager diff "$f" "$FF" || : + FEDORA_VERSION="$(sed -n 's/^ image: fedora:\([0-9]\+\)$/\1/p' .github/workflows/ci.yml)" + die "Error: file \"$f\" has style issues."$'\n'"Fix it by running \`\"$0\"\` using $(clang-format --version) +Alternatively, run \`./tools/clang-format-container.sh\` to use a podman container named \"libnl-code-format-f$FEDORA_VERSION\"." + fi + done + die "an unknown error happened." +fi + +clang-format -i "${FILES[@]}" diff --git a/libnl3.spec b/libnl3.spec deleted file mode 100644 index 28c9d37..0000000 --- a/libnl3.spec +++ /dev/null @@ -1,187 +0,0 @@ -Name: libnl3 -Version: 3.7.0 -Release: 4 -Summary: Providing APIs to netlink protocol based Linux kernel interfaces -License: LGPLv2 -URL: http://www.infradead.org/~tgr/libnl/ -Source: https://github.com/thom311/libnl/releases/download/libnl3_7_0/libnl-3.7.0.tar.gz - -Patch6000: backport-prevent-segfault-in-af_request_type.patch -Patch6001: backport-fix-bridge-info-parsing.patch - -Patch9000: solve-redefinition-of-struct-ipv6_mreq.patch - -patch6002: backport-add-some-tests-about-addr-class-rule-neigh-qdisc.patch -patch6003: backport-clear-XFRM_SP_ATTR_TMPL-when-removing-the-last-template.patch -patch6004: backport-fix-reference-counters-of-sa-selector-addresses.patch -patch6005: backport-do-not-use-static-array-indices-for-buffer.patch -patch6006: backport-fix-leak-in-error-handling-of-rtnl_flower_append_action.patch -patch6007: backport-fix-signed-overflow-warning-in-nl_object_diff.patch -patch6008: backport-workaround-coverity-warning-about-time_t-handling.patch -patch6009: backport-fix-leaking-usertemplate-in-xfrmnl_sp_parse.patch -patch6010: backport-avoid-integer-overflow-in-rtnl_tc_calc_cell_log.patch -patch6011: backport-fix-crashes-in-case-of-ENOMEM.patch -patch6012: backport-accept-NULL-argument-in-nla_nest_cancel-for-robustness.patch -patch6013: backport-fix-error-handling-in-nl_str2ip_protos.patch -patch6014: backport-handle-negative-and-zero-size-in-nla_memcpy.patch -patch6015: backport-use-thread-safe-gmtime_r-instead-of-gmtime.patch - -BuildRequires: flex bison libtool autoconf automake swig -Requires: %{name} = %{version}-%{release} - -Provides: %{name}-cli -Obsoletes: %{name}-cli - -%description -This package contains a collection of libraries providing -APIs to netlink based Linux kernel sockets interfaces. - -%package devel -Summary: Libraries and headers for libnl3 -Requires: %{name} = %{version}-%{release} kernel-headers - -%description devel -This package provides various libraries and headers for using libnl3 - -%package help -Summary: Document for libnl3 -Requires: %{name} = %{version}-%{release} - -Provides: %{name}-doc = %{version}-%{release} -Obsoletes: %{name}-doc < %{version}-%{release} - -%description help -This package contains libnl3 related documentations - -%package -n python3-libnl3 -Summary: Python3 binding for libnl3 -BuildRequires: python3-devel -Requires: %{name} = %{version}-%{release} - -%description -n python3-libnl3 -Python3 bindings for libnl3 - -%prep -%autosetup -n libnl-%{version} -p1 - -%build -autoreconf -vif -%configure --disable-static -%make_build - -cd python -CFLAGS="$RPM_OPT_FLAGS" %py3_build -CFLAGS="$RPM_OPT_FLAGS" %py3_build - -%install -%make_install - -find $RPM_BUILD_ROOT -name *.la |xargs rm -f - -cd python -%py3_install - -%check -make check - -cd python -%{__python3} setup.py check - -%ldconfig_scriptlets - -%files -%doc COPYING -%{_libdir}/libnl-*.so.* -%config(noreplace) %{_sysconfdir}/* -%{_libdir}/libnl/ -%{_bindir}/* - -%files devel -%{_includedir}/libnl3/netlink/ -%{_libdir}/*.so -%{_libdir}/pkgconfig/*.pc - -%files help -%{_mandir}/man8/* - -%files -n python3-libnl3 -%{python3_sitearch}/netlink -%{python3_sitearch}/netlink-*.egg-info - -%changelog -* Thu Apr 25 2024 sunhai - 3.7.0-4 -- Type:bugfix -- ID:NA -- SUG:NA -- DESC:sync some pathes from upstream - -* Fri Jan 12 2024 chengyechun - 3.7.0-3 -- Type:bugfix -- ID:NA -- SUG:NA -- DESC:Use the user-mode IPv6 header file - -* Mon Aug 14 2023 chengyechun - 3.7.0-2 -- Type:bugfix -- ID:NA -- SUG:NA -- DESC:prevent segfault in af_request_type - fix bridge info parsing - -* Wed Nov 9 2022 chengyechun - 3.7.0-1 -- Type:enhancement -- ID:NA -- SUG:NA -- DESC:update to libnl-3.7.0 - -* Sat Dec 18 2021 chengyechun - 3.5.0-6 -- Type:bugfix -- ID:NA -- SUG:NA --DES:add seome tests about add and delele addr, - add and delete neigh, - add and delete qdisc, - add link and rule, - delete route; - -* Thu Dec 16 2021 chengyechun - 3.5.0-5 -- Type:bugfix -- ID:NA -- SUG:NA -- DES:add a test:test add route - -* Wed Mar 10 2021 zengwefeng - 3.5.0-4 -- Type:bugfix -- ID:NA -- SUG:NA -- DESC:add missing check for NULL return from allocate_rfd - add include netlink private nl-auto-h header - use proper int type for id attributes - add RTNL_LINK_REASM_OVERLAPS stat - check for null pointer in macvlan - fix leaking in link msg parser - fix NLE_NOMEM handling in parse multipath - -* Thu Oct 29 2020 gaihuiying - 3.5.0-3 -- Type:requirement -- ID:NA -- SUG:NA -- DESC:remove python2 - -* Thu Sep 10 2020 lunankun - 3.5.0-2 -- Type: bugfix -- ID: NA -- SUG: NA -- DESC: fix Source0 url - -* Sun Jan 12 2020 openEuler Buildteam - 3.5.0-1 -- update software to 3.5.0 - -* Wed Dec 25 2019 openEuler Buildteam - 3.4.0-8 -- Type:bugfix -- Id:NA -- SUG:NA -- DESC:provides libnl3-doc - -* Sat Sep 7 2019 liyongqiang - 3.4.0-7 -- Package init diff --git a/prepare.sh b/prepare.sh new file mode 100755 index 0000000..9eda88d --- /dev/null +++ b/prepare.sh @@ -0,0 +1,215 @@ +#!/bin/bash +# Copyright(c) 2026 Huawei Device Co., Ltd. + +# 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. + +# prepare.sh —— libnl 代码环境准备脚本(仅升级 libnl 时手动运行,构建期 BUILD.gn +# 不调用本脚本)。 +# +# 为区分「上游原始代码 / 构建环境产物 / OHOS 适配」三类变更,本脚本拆成三个阶段, +# 每个阶段对应一次独立 git commit,便于源码可信追踪与后续升级: +# +# stage 1 upstream 清空 libnl/ 并解压纯上游源码树(无任何改动/生成物) +# -> 提交 commit 1 [Import libnl upstream] +# stage 2 build autogen + configure(产出 config.h/version.h)、flex/bison +# 预生成语法文件、清理 autotools 中间产物 +# -> 提交 commit 2 [libnl build env (configure + grammars)] +# stage 3 patch 应用 OHOS 适配 patch,并把(解决冲突后的)patch 归档到 +# archive/patches/ 供后续版本升级使用 +# -> 提交 commit 3 [libnl OHOS adaptation patch] +# +# 用法: +# ./prepare.sh upstream [/path/to/libnl-.tar.gz] # 阶段1(省略路径时则从上游下载) +# ./prepare.sh build # 阶段2 +# ./prepare.sh patch # 阶段3 +# ./prepare.sh all [/path/to/libnl-.tar.gz] # 一次性跑完(非升级快速搭建和验证) +# +# 依赖主机工具: tar, sha256sum, patch, flex, bison, +# autoreconf(automake/autoconf/libtool), 以及下载场景的 curl/wget。 +# 这些工具仅本脚本需要,日常 gn/ninja 构建不需要。 + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +cd "$SCRIPT_DIR" + +# ============ 升级时按实际情况更新以下变量 ============ +LIBNL_VERSION="3.11.0" +UPSTREAM_URL="https://github.com/thom311/libnl/archive/refs/tags/libnl3_11_0.tar.gz" +EXTRACTED_DIR="libnl-libnl3_11_0" # 上游归档解压后的顶层目录名 +PATCH_FILE="$SCRIPT_DIR/solve-oh-compile-problem3_11_0.patch" +# =============================================== + +ARCHIVE_PATCH_DIR="$SCRIPT_DIR/archive/patches" + +die() { echo "[prepare] ERROR: $*" >&2; exit 1; } +info() { echo "[prepare] $*"; } + +# --------------------------------------------------------------------------- +# 阶段 1:清空 libnl/ 并解压纯上游源码树(不做任何修改/生成) +# --------------------------------------------------------------------------- +stage_upstream() { + local arg="$1" tarball tmp="" + + if [ -n "$arg" ]; then + tarball="$arg" + [ -f "$tarball" ] || die "tarball not found: $tarball" + else + tmp="$(mktemp /tmp/libnl-src.XXXXXX.tar.gz)" + tarball="$tmp" + info "未指定 tarball,从上游下载: $UPSTREAM_URL" + if command -v curl >/dev/null 2>&1; then + curl -fL "$UPSTREAM_URL" -o "$tarball" || die "下载失败" + elif command -v wget >/dev/null 2>&1; then + wget -O "$tarball" "$UPSTREAM_URL" || die "下载失败" + else + die "需要 curl 或 wget 来下载 tarball;或显式传入本地 tarball 路径" + fi + fi + + # 对整个上游 tar 包计算一次 SHA256(标准 sha256sum 格式,单行、记录规范包名), + # 随 commit 1 入库。用户可用同名 tar 包 `sha256sum -c` 复算,或直接与上游社区 + # 发布的 sha256sum 文件比对。 + mkdir -p archive + local sums="archive/libnl-${LIBNL_VERSION}.sha256sums" + local tarname + tarname="$(basename "$UPSTREAM_URL")" # 规范 tar 包名(如 libnl3_11_0.tar.gz) + printf '%s %s\n' "$(sha256sum "$tarball" | awk '{print $1}')" "$tarname" > "$sums" + info "已生成上游整包 SHA256 -> $sums" + info " 校验方式: 将上游 tar 包按 $tarname 命名后,在 archive/ 下执行" + info " sha256sum -c libnl-${LIBNL_VERSION}.sha256sums" + info " 或直接与上游社区发布的 sha256sum 文件比对该值。" + + info "清空 libnl/ 并解压纯上游源码树" + rm -rf libnl "$EXTRACTED_DIR" + tar xf "$tarball" + [ -d "$EXTRACTED_DIR" ] || die "解压后未找到目录 $EXTRACTED_DIR" + mv "$EXTRACTED_DIR" libnl + + [ -n "$tmp" ] && rm -f "$tmp" + + info "阶段1完成。请提交 commit 1(纯上游源码基线 + 整包 SHA256),例如:" + info " git add libnl/ \"$sums\" && git commit -m \"Import libnl ${LIBNL_VERSION} upstream\"" +} + +# --------------------------------------------------------------------------- +# 阶段 2:autogen + configure + 语法预生成 + 清理 autotools 中间产物 +# --------------------------------------------------------------------------- +stage_build() { + [ -d libnl ] || die "未找到 libnl/,请先运行: ./prepare.sh upstream" + + cd libnl + + info "运行 autogen.sh + configure(产出 config.h / version.h)" + ./autogen.sh + ./configure + + info "flex/bison 预生成 4 组(共 8 个)语法文件" + flex --header-file=lib/route/pktloc_grammar.h -o lib/route/pktloc_grammar.c lib/route/pktloc_grammar.l + bison -y -d -o lib/route/pktloc_syntax.c lib/route/pktloc_syntax.y + flex --header-file=lib/route/cls/ematch_grammar.h -o lib/route/cls/ematch_grammar.c lib/route/cls/ematch_grammar.l + bison -y -d -o lib/route/cls/ematch_syntax.c lib/route/cls/ematch_syntax.y + + info "清理 autotools 中间产物(GN 构建不需要,且会泄漏主机路径)" + rm -f config.status config.log libtool aclocal.m4 stamp-h1 configure + rm -rf autom4te.cache build-aux + rm -f libnl-*.pc + find . -name 'Makefile' -delete + find . -name 'Makefile.in' -delete + find . -name '.dirstamp' -delete + find . -name 'stamp-h1' -delete + rm -f doc/configure doc/config.status doc/config.log + rm -rf doc/autom4te.cache doc/build-aux + # 以下为 autogen/configure 在源码树各处留下的其余生成物(均被上游 .gitignore 忽略, + # 不会入库;此处物理删除以保证准备好的源码树 == 纯上游 + config.h/version.h/语法文件, + # 便于与上游社区源码逐文件对比校验): + find . -type d -name '.deps' -exec rm -rf {} + 2>/dev/null || true # 依赖跟踪桩(.Plo) + find . -name '*~' -delete # 备份文件(configure~ 等) + rm -f include/config.h.in # autoheader 生成模板 + rm -f doc/Doxyfile doc/aclocal.m4 # doc 配置/autoreconf 产物 + rm -f m4/libtool.m4 m4/lt*.m4 # libtoolize 生成 + rm -f python/setup.py # configure 由 setup.py.in 生成 + + cd "$SCRIPT_DIR" + + # 让上一步生成的 config.h/version.h/语法文件不再被 libnl 自带 .gitignore 忽略, + # 从而能正常纳入 commit 2(属于「构建环境产物」而非 OHOS 源码适配)。 + unignore_prepared_files + + info "阶段2完成。请提交 commit 2(构建环境产物),例如:" + info " git add libnl/ && git commit -m \"libnl ${LIBNL_VERSION} build env (configure + grammars)\"" +} + +# 取消忽略提前生成的构建产物(精确整行匹配,避免正则转义问题) +unignore_prepared_files() { + local gi="libnl/.gitignore" line + [ -f "$gi" ] || return 0 + for line in \ + '/include/config.h' \ + '/include/netlink/version.h' \ + '/lib/route/cls/ematch_grammar.[ch]' \ + '/lib/route/cls/ematch_syntax.[ch]' \ + '/lib/route/pktloc_grammar.c' \ + '/lib/route/pktloc_grammar.h' \ + '/lib/route/pktloc_syntax.c' \ + '/lib/route/pktloc_syntax.h' + do + awk -v t="$line" '{ if ($0==t) print "# (OHOS prepared, tracked) " $0; else print }' \ + "$gi" > "$gi.tmp" && mv "$gi.tmp" "$gi" + done +} + +# --------------------------------------------------------------------------- +# 阶段 3:应用 OHOS 适配 patch,并归档(解决冲突后的)patch +# --------------------------------------------------------------------------- +stage_patch() { + [ -d libnl ] || die "未找到 libnl/,请先运行: ./prepare.sh upstream && ./prepare.sh build" + [ -f "$PATCH_FILE" ] || die "patch 不存在: $PATCH_FILE" + + info "应用 OHOS 适配 patch: $(basename "$PATCH_FILE")" + info " (必须在 configure 之后:patch 会把 config.h 的 HAVE_STRERROR_L 等改为交叉编译正确值)" + ( cd libnl && patch -p1 < "$PATCH_FILE" --fuzz=0 --no-backup-if-mismatch ) \ + || die "patch 应用失败:请手动解决冲突、更新 $(basename "$PATCH_FILE") 后重试" + + # 归档当前(解决冲突后的)patch,供后续版本升级参考复用 + mkdir -p "$ARCHIVE_PATCH_DIR" + cp "$PATCH_FILE" "$ARCHIVE_PATCH_DIR/solve-oh-compile-problem-${LIBNL_VERSION}.patch" + info "已归档 patch -> archive/patches/solve-oh-compile-problem-${LIBNL_VERSION}.patch" + + info "阶段3完成。请确认无 *.rej/*.orig 残留后提交 commit 3(OHOS 适配),例如:" + info " git add libnl/ archive/ && git commit -m \"libnl ${LIBNL_VERSION} OHOS adaptation patch\"" +} + +usage() { + cat >&2 <.tar.gz] # 阶段1:纯上游源码 -> commit 1 + ./prepare.sh build # 阶段2:构建环境产物 -> commit 2 + ./prepare.sh patch # 阶段3:OHOS 适配 patch -> commit 3 + ./prepare.sh all [/path/to/libnl-.tar.gz] # 一次性跑完(非升级快速搭建) + +升级 libnl 时请按 upstream -> build -> patch 顺序运行,并在每阶段之间各提交一次 +独立 commit。详见 docs/UPGRADE_GUIDE.md。 +EOF + exit 1 +} + +case "$1" in + upstream) stage_upstream "$2" ;; + build) stage_build ;; + patch) stage_patch ;; + all) stage_upstream "$2"; stage_build; stage_patch ;; + -h|--help|help) usage ;; + "") usage ;; + *) die "未知子命令: $1(运行 ./prepare.sh --help 查看用法)" ;; +esac diff --git a/slove-oh-bug-fix.patch b/slove-oh-bug-fix.patch deleted file mode 100644 index 0602a60..0000000 --- a/slove-oh-bug-fix.patch +++ /dev/null @@ -1,35 +0,0 @@ -diff -urN libnl-3.7.0/lib/nl.c libnl-3.7.0_new/lib/nl.c ---- libnl-3.7.0/lib/nl.c 2022-05-04 00:50:34.000000000 +0800 -+++ libnl-3.7.0_new/lib/nl.c 2024-08-08 15:28:38.266789900 +0800 -@@ -481,7 +481,7 @@ - nlh->nlmsg_pid = nl_socket_get_local_port(sk); - - if (nlh->nlmsg_seq == NL_AUTO_SEQ) -- nlh->nlmsg_seq = sk->s_seq_next++; -+ nlh->nlmsg_seq = nl_socket_use_seq(sk); - - if (msg->nm_protocol == -1) - msg->nm_protocol = sk->s_proto; -diff -urN libnl-3.7.0/lib/socket.c libnl-3.7.0_new/lib/socket.c ---- libnl-3.7.0/lib/socket.c 2022-05-24 16:55:12.000000000 +0800 -+++ libnl-3.7.0_new/lib/socket.c 2024-08-08 15:29:22.409796400 +0800 -@@ -22,7 +22,7 @@ - */ - - #include "defs.h" -- -+#include - #include "sys/socket.h" - - #include -@@ -291,6 +291,10 @@ - */ - unsigned int nl_socket_use_seq(struct nl_sock *sk) - { -+ if (sk->s_seq_next == UINT_MAX) { -+ sk->s_seq_next = 0; -+ return UINT_MAX; -+ } - return sk->s_seq_next++; - } - diff --git a/slove-oh-update-sp3.patch b/slove-oh-update-sp3.patch deleted file mode 100644 index 6ce9c8b..0000000 --- a/slove-oh-update-sp3.patch +++ /dev/null @@ -1,1145 +0,0 @@ -diff -urN libnl-3.7.0/include/linux-private/linux/if_bridge.h libnl-3.7.0_new/include/linux-private/linux/if_bridge.h ---- libnl-3.7.0/include/linux-private/linux/if_bridge.h 2022-05-04 00:50:34.000000000 +0800 -+++ libnl-3.7.0_new/include/linux-private/linux/if_bridge.h 2024-06-03 17:46:47.872030900 +0800 -@@ -16,7 +16,7 @@ - - #include - #include --#include -+#include - - #define SYSFS_BRIDGE_ATTR "bridge" - #define SYSFS_BRIDGE_FDB "brforward" -diff -urN libnl-3.7.0/include/netlink-private/nl-auto.h libnl-3.7.0_new/include/netlink-private/nl-auto.h ---- libnl-3.7.0/include/netlink-private/nl-auto.h 2022-05-24 16:55:12.000000000 +0800 -+++ libnl-3.7.0_new/include/netlink-private/nl-auto.h 2024-06-03 17:37:51.785691800 +0800 -@@ -99,4 +99,11 @@ - #define _nl_auto_nl_socket _nl_auto(_nl_auto_nl_socket_fcn) - _NL_AUTO_DEFINE_FCN_TYPED0(struct nl_sock *, _nl_auto_nl_socket_fcn, nl_socket_free); - -+struct xfrmnl_user_tmpl; -+void xfrmnl_user_tmpl_free(struct xfrmnl_user_tmpl *utmpl); -+#define _nl_auto_xfrmnl_user_tmpl _nl_auto(_nl_auto_xfrmnl_user_tmpl_fcn) -+_NL_AUTO_DEFINE_FCN_TYPED0(struct xfrmnl_user_tmpl *, -+ _nl_auto_xfrmnl_user_tmpl_fcn, -+ xfrmnl_user_tmpl_free); -+ - #endif /* NETLINK_NL_AUTO_H_ */ -diff -urN libnl-3.7.0/include/netlink-private/utils.h libnl-3.7.0_new/include/netlink-private/utils.h ---- libnl-3.7.0/include/netlink-private/utils.h 2022-05-24 16:55:12.000000000 +0800 -+++ libnl-3.7.0_new/include/netlink-private/utils.h 2024-06-03 17:36:49.655447000 +0800 -@@ -361,8 +361,7 @@ - struct in6_addr a6; - } _NLIPAddr; - --static inline char *_nl_inet_ntop(int addr_family, const void *addr, -- char buf[static INET_ADDRSTRLEN]) -+static inline char *_nl_inet_ntop(int addr_family, const void *addr, char *buf) - { - char *r; - -diff -urN libnl-3.7.0/lib/attr.c libnl-3.7.0_new/lib/attr.c ---- libnl-3.7.0/lib/attr.c 2022-07-06 23:01:59.000000000 +0800 -+++ libnl-3.7.0_new/lib/attr.c 2024-06-03 17:39:30.549111300 +0800 -@@ -349,10 +349,13 @@ - - if (!src) - return 0; -- -+ - minlen = min_t(int, count, nla_len(src)); -- memcpy(dest, nla_data(src), minlen); - -+ if (minlen <= 0) -+ return 0; -+ -+ memcpy(dest, nla_data(src), minlen); - return minlen; - } - -@@ -988,6 +991,15 @@ - { - ssize_t len; - -+ if (!attr) { -+ /* For robustness, allow a NULL attr to do nothing. NULL is also -+ * what nla_nest_start() when out of buffer space. -+ * -+ * Warning, before libnl-3.8, the function did not accept NULL! -+ * If you care, catch NULL yourself. */ -+ return; -+ } -+ - len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) attr; - if (len < 0) - BUG(); -diff -urN libnl-3.7.0/lib/object.c libnl-3.7.0_new/lib/object.c ---- libnl-3.7.0/lib/object.c 2022-07-06 22:13:48.000000000 +0800 -+++ libnl-3.7.0_new/lib/object.c 2024-06-03 17:41:15.060021200 +0800 -@@ -392,7 +392,7 @@ - diff = nl_object_diff64(a, b); - - return (diff & ~((uint64_t) 0xFFFFFFFF)) -- ? (uint32_t) diff | (1 << 31) -+ ? (uint32_t) diff | (((uint32_t ) 1u) << 31) - : (uint32_t) diff; - } - -diff -urN libnl-3.7.0/lib/route/cls/flower.c libnl-3.7.0_new/lib/route/cls/flower.c ---- libnl-3.7.0/lib/route/cls/flower.c 2022-07-06 23:02:41.000000000 +0800 -+++ libnl-3.7.0_new/lib/route/cls/flower.c 2024-06-03 18:28:20.501852400 +0800 -@@ -787,6 +787,7 @@ - int rtnl_flower_append_action(struct rtnl_cls *cls, struct rtnl_act *act) - { - struct rtnl_flower *f; -+ int err; - - if (!act) - return 0; -@@ -796,8 +797,11 @@ - - f->cf_mask |= FLOWER_ATTR_ACTION; - -+ if ((err = rtnl_act_append(&f->cf_act, act)) < 0) -+ return err; -+ - rtnl_act_get(act); -- return rtnl_act_append(&f->cf_act, act); -+ return 0; - } - - /** -diff -urN libnl-3.7.0/lib/route/link/bridge.c libnl-3.7.0_new/lib/route/link/bridge.c ---- libnl-3.7.0/lib/route/link/bridge.c 2022-05-24 16:55:12.000000000 +0800 -+++ libnl-3.7.0_new/lib/route/link/bridge.c 2024-06-03 16:55:11.850716100 +0800 -@@ -189,6 +189,7 @@ - if (nla_type(attr) == IFLA_BRIDGE_MODE) { - bd->b_hwmode = nla_get_u16(attr); - bd->ce_mask |= BRIDGE_ATTR_HWMODE; -+ continue; - } else if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO) - continue; - -diff -urN libnl-3.7.0/lib/route/link.c libnl-3.7.0_new/lib/route/link.c ---- libnl-3.7.0/lib/route/link.c 2022-05-24 16:55:12.000000000 +0800 -+++ libnl-3.7.0_new/lib/route/link.c 2024-06-03 17:44:18.163118300 +0800 -@@ -115,7 +115,7 @@ - struct rtnl_link_af_ops *ops; - - ops = rtnl_link_af_ops_lookup(af_type); -- if (ops && ops->ao_override_rtm(changes)) -+ if (ops && ops->ao_override_rtm && ops->ao_override_rtm(changes)) - return RTM_SETLINK; - - return RTM_NEWLINK; -diff -urN libnl-3.7.0/lib/route/tc.c libnl-3.7.0_new/lib/route/tc.c ---- libnl-3.7.0/lib/route/tc.c 2022-05-24 16:55:12.000000000 +0800 -+++ libnl-3.7.0_new/lib/route/tc.c 2024-06-03 17:36:24.831904500 +0800 -@@ -666,14 +666,14 @@ - /** - * Calculate the binary logarithm for a specific cell size - * @arg cell_size Size of cell, must be a power of two. -- * @return Binary logirhtm of cell size or a negative error code. -+ * @return Binary logarithm of cell size or a negative error code. - */ - int rtnl_tc_calc_cell_log(int cell_size) - { - int i; - - for (i = 0; i < 32; i++) -- if ((1 << i) == cell_size) -+ if ((((uint32_t)1u) << i) == cell_size) - return i; - - return -NLE_INVAL; -diff -urN libnl-3.7.0/lib/socket.c libnl-3.7.0_new/lib/socket.c ---- libnl-3.7.0/lib/socket.c 2022-05-24 16:55:12.000000000 +0800 -+++ libnl-3.7.0_new/lib/socket.c 2024-06-03 17:45:33.823477800 +0800 -@@ -54,6 +54,24 @@ - } - } - -+static uint32_t _badrandom_from_time(void) -+{ -+ uint32_t result; -+ uint64_t v64; -+ time_t t; -+ -+ t = time(NULL); -+ v64 = (uint64_t)t; -+ result = (uint32_t)v64; -+ -+ /* XOR with the upper bits. Otherwise, coverity warns about only -+ * considering 32 bit from time_t. Use the inverse, so that for the -+ * most part the bits don't change. */ -+ result ^= (~(v64 >> 32)); -+ -+ return result; -+} -+ - static uint32_t used_ports_map[32]; - static NL_RW_LOCK(port_map_lock); - -@@ -67,7 +85,7 @@ - nl_write_lock(&port_map_lock); - - if (idx_state == 0) { -- uint32_t t = time(NULL); -+ uint32_t t = _badrandom_from_time(); - - /* from time to time (on average each 2^15 calls), the idx_state will - * be zero again. No problem, just "seed" anew with time(). */ -@@ -184,7 +202,8 @@ - sk->s_cb = nl_cb_get(cb); - sk->s_local.nl_family = AF_NETLINK; - sk->s_peer.nl_family = AF_NETLINK; -- sk->s_seq_expect = sk->s_seq_next = time(NULL); -+ sk->s_seq_next = _badrandom_from_time(); -+ sk->s_seq_expect = sk->s_seq_next; - - /* the port is 0 (unspecified), meaning NL_OWN_PORT */ - sk->s_flags = NL_OWN_PORT; -diff -urN libnl-3.7.0/lib/utils.c libnl-3.7.0_new/lib/utils.c ---- libnl-3.7.0/lib/utils.c 2022-07-06 23:21:11.000000000 +0800 -+++ libnl-3.7.0_new/lib/utils.c 2024-06-03 17:37:18.340704700 +0800 -@@ -880,7 +880,7 @@ - return p->p_proto; - - l = strtoul(name, &end, 0); -- if (l == ULONG_MAX || *end != '\0') -+ if (name == end || *end != '\0' || l > (unsigned long)INT_MAX) - return -NLE_OBJ_NOTFOUND; - - return (int) l; -diff -urN libnl-3.7.0/lib/xfrm/ae.c libnl-3.7.0_new/lib/xfrm/ae.c ---- libnl-3.7.0/lib/xfrm/ae.c 2022-05-24 16:55:12.000000000 +0800 -+++ libnl-3.7.0_new/lib/xfrm/ae.c 2024-06-03 17:42:21.017726700 +0800 -@@ -301,6 +301,7 @@ - char flags[128], buf[128]; - time_t add_time, use_time; - struct tm *add_time_tm, *use_time_tm; -+ struct tm tm_buf; - - nl_dump_line(p, "src %s dst %s \n", nl_addr2str(ae->saddr, src, sizeof(src)), - nl_addr2str(ae->sa_id.daddr, dst, sizeof(dst))); -@@ -320,7 +321,7 @@ - if (ae->lifetime_cur.add_time != 0) - { - add_time = ae->lifetime_cur.add_time; -- add_time_tm = gmtime (&add_time); -+ add_time_tm = gmtime_r (&add_time, &tm_buf); - strftime (flags, 128, "%Y-%m-%d %H-%M-%S", add_time_tm); - } - else -@@ -331,7 +332,7 @@ - if (ae->lifetime_cur.use_time != 0) - { - use_time = ae->lifetime_cur.use_time; -- use_time_tm = gmtime (&use_time); -+ use_time_tm = gmtime_r (&use_time, &tm_buf); - strftime (buf, 128, "%Y-%m-%d %H-%M-%S", use_time_tm); - } - else -@@ -505,11 +506,18 @@ - if (err < 0) - goto errout; - -- ae->sa_id.daddr = nl_addr_build(ae_id->sa_id.family, &ae_id->sa_id.daddr, sizeof (ae_id->sa_id.daddr)); -+ if (!(ae->sa_id.daddr = nl_addr_build(ae_id->sa_id.family, &ae_id->sa_id.daddr, -+ sizeof (ae_id->sa_id.daddr)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } - ae->sa_id.family= ae_id->sa_id.family; - ae->sa_id.spi = ntohl(ae_id->sa_id.spi); - ae->sa_id.proto = ae_id->sa_id.proto; -- ae->saddr = nl_addr_build(ae_id->sa_id.family, &ae_id->saddr, sizeof (ae_id->saddr)); -+ if (!(ae->saddr = nl_addr_build(ae_id->sa_id.family, &ae_id->saddr, sizeof (ae_id->saddr)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } - ae->reqid = ae_id->reqid; - ae->flags = ae_id->flags; - ae->ce_mask |= (XFRM_AE_ATTR_DADDR | XFRM_AE_ATTR_FAMILY | XFRM_AE_ATTR_SPI | -diff -urN libnl-3.7.0/lib/xfrm/sa.c libnl-3.7.0_new/lib/xfrm/sa.c ---- libnl-3.7.0/lib/xfrm/sa.c 2022-05-24 16:55:12.000000000 +0800 -+++ libnl-3.7.0_new/lib/xfrm/sa.c 2024-06-03 18:32:07.233173600 +0800 -@@ -415,6 +415,7 @@ - char flags[128], mode[128]; - time_t add_time, use_time; - struct tm *add_time_tm, *use_time_tm; -+ struct tm tm_buf; - - nl_dump_line(p, "src %s dst %s family: %s\n", nl_addr2str(sa->saddr, src, sizeof(src)), - nl_addr2str(sa->id.daddr, dst, sizeof(dst)), -@@ -467,7 +468,7 @@ - if (sa->curlft.add_time != 0) - { - add_time = sa->curlft.add_time; -- add_time_tm = gmtime (&add_time); -+ add_time_tm = gmtime_r (&add_time, &tm_buf); - strftime (flags, 128, "%Y-%m-%d %H-%M-%S", add_time_tm); - } - else -@@ -478,7 +479,7 @@ - if (sa->curlft.use_time != 0) - { - use_time = sa->curlft.use_time; -- use_time_tm = gmtime (&use_time); -+ use_time_tm = gmtime_r (&use_time, &tm_buf); - strftime (mode, 128, "%Y-%m-%d %H-%M-%S", use_time_tm); - } - else -@@ -717,9 +718,19 @@ - goto errout; - - if (sa_info->sel.family == AF_INET) -- addr = nl_addr_build (sa_info->sel.family, &sa_info->sel.daddr.a4, sizeof (sa_info->sel.daddr.a4)); -+ { -+ if (!(addr = nl_addr_build (sa_info->sel.family, &sa_info->sel.daddr.a4, sizeof (sa_info->sel.daddr.a4)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } -+ } - else -- addr = nl_addr_build (sa_info->sel.family, &sa_info->sel.daddr.a6, sizeof (sa_info->sel.daddr.a6)); -+ { -+ if (!(addr = nl_addr_build (sa_info->sel.family, &sa_info->sel.daddr.a6, sizeof (sa_info->sel.daddr.a6)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } -+ } - nl_addr_set_prefixlen (addr, sa_info->sel.prefixlen_d); - xfrmnl_sel_set_daddr (sa->sel, addr); - /* Drop the reference count from the above set operation */ -@@ -727,9 +738,19 @@ - xfrmnl_sel_set_prefixlen_d (sa->sel, sa_info->sel.prefixlen_d); - - if (sa_info->sel.family == AF_INET) -- addr = nl_addr_build (sa_info->sel.family, &sa_info->sel.saddr.a4, sizeof (sa_info->sel.saddr.a4)); -+ { -+ if (!(addr = nl_addr_build (sa_info->sel.family, &sa_info->sel.saddr.a4, sizeof (sa_info->sel.saddr.a4)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } -+ } - else -- addr = nl_addr_build (sa_info->sel.family, &sa_info->sel.saddr.a6, sizeof (sa_info->sel.saddr.a6)); -+ { -+ if (!(addr = nl_addr_build (sa_info->sel.family, &sa_info->sel.saddr.a6, sizeof (sa_info->sel.saddr.a6)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } -+ } - nl_addr_set_prefixlen (addr, sa_info->sel.prefixlen_s); - xfrmnl_sel_set_saddr (sa->sel, addr); - /* Drop the reference count from the above set operation */ -@@ -747,17 +768,37 @@ - sa->ce_mask |= XFRM_SA_ATTR_SEL; - - if (sa_info->family == AF_INET) -- sa->id.daddr = nl_addr_build (sa_info->family, &sa_info->id.daddr.a4, sizeof (sa_info->id.daddr.a4)); -+ { -+ if (!(sa->id.daddr = nl_addr_build (sa_info->family, &sa_info->id.daddr.a4, sizeof (sa_info->id.daddr.a4)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } -+ } - else -- sa->id.daddr = nl_addr_build (sa_info->family, &sa_info->id.daddr.a6, sizeof (sa_info->id.daddr.a6)); -+ { -+ if (!(sa->id.daddr = nl_addr_build (sa_info->family, &sa_info->id.daddr.a6, sizeof (sa_info->id.daddr.a6)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } -+ } - sa->id.spi = ntohl(sa_info->id.spi); - sa->id.proto = sa_info->id.proto; - sa->ce_mask |= (XFRM_SA_ATTR_DADDR | XFRM_SA_ATTR_SPI | XFRM_SA_ATTR_PROTO); - - if (sa_info->family == AF_INET) -- sa->saddr = nl_addr_build (sa_info->family, &sa_info->saddr.a4, sizeof (sa_info->saddr.a4)); -+ { -+ if (!(sa->saddr = nl_addr_build (sa_info->family, &sa_info->saddr.a4, sizeof (sa_info->saddr.a4)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } -+ } - else -- sa->saddr = nl_addr_build (sa_info->family, &sa_info->saddr.a6, sizeof (sa_info->saddr.a6)); -+ { -+ if (!(sa->saddr = nl_addr_build (sa_info->family, &sa_info->saddr.a6, sizeof (sa_info->saddr.a6)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } -+ } - sa->ce_mask |= XFRM_SA_ATTR_SADDR; - - sa->lft->soft_byte_limit = sa_info->lft.soft_byte_limit; -@@ -865,9 +906,19 @@ - sa->encap->encap_sport = ntohs(encap->encap_sport); - sa->encap->encap_dport = ntohs(encap->encap_dport); - if (sa_info->family == AF_INET) -- sa->encap->encap_oa = nl_addr_build (sa_info->family, &encap->encap_oa.a4, sizeof (encap->encap_oa.a4)); -+ { -+ if (!(sa->encap->encap_oa = nl_addr_build (sa_info->family, &encap->encap_oa.a4, sizeof (encap->encap_oa.a4)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } -+ } - else -- sa->encap->encap_oa = nl_addr_build (sa_info->family, &encap->encap_oa.a6, sizeof (encap->encap_oa.a6)); -+ { -+ if (!(sa->encap->encap_oa = nl_addr_build (sa_info->family, &encap->encap_oa.a6, sizeof (encap->encap_oa.a6)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } -+ } - sa->ce_mask |= XFRM_SA_ATTR_ENCAP; - } - -@@ -879,13 +930,19 @@ - if (tb[XFRMA_COADDR]) { - if (sa_info->family == AF_INET) - { -- sa->coaddr = nl_addr_build(sa_info->family, nla_data(tb[XFRMA_COADDR]), -- sizeof (uint32_t)); -+ if (!(sa->coaddr = nl_addr_build( -+ sa_info->family, nla_data(tb[XFRMA_COADDR]), sizeof (uint32_t)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } - } - else - { -- sa->coaddr = nl_addr_build(sa_info->family, nla_data(tb[XFRMA_COADDR]), -- sizeof (uint32_t) * 4); -+ if (!(sa->coaddr = nl_addr_build( -+ sa_info->family, nla_data(tb[XFRMA_COADDR]), sizeof (uint32_t) * 4))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } - } - sa->ce_mask |= XFRM_SA_ATTR_COADDR; - } -diff -urN libnl-3.7.0/lib/xfrm/sp.c libnl-3.7.0_new/lib/xfrm/sp.c ---- libnl-3.7.0/lib/xfrm/sp.c 2022-05-24 16:55:12.000000000 +0800 -+++ libnl-3.7.0_new/lib/xfrm/sp.c 2024-06-03 17:43:31.464119900 +0800 -@@ -324,6 +324,7 @@ - char dst[INET6_ADDRSTRLEN+5], src[INET6_ADDRSTRLEN+5]; - time_t add_time, use_time; - struct tm *add_time_tm, *use_time_tm; -+ struct tm tm_buf; - - nl_addr2str(xfrmnl_sel_get_saddr (sp->sel), src, sizeof(src)); - nl_addr2str (xfrmnl_sel_get_daddr (sp->sel), dst, sizeof (dst)); -@@ -384,7 +385,7 @@ - if (sp->curlft.add_time != 0) - { - add_time = sp->curlft.add_time; -- add_time_tm = gmtime (&add_time); -+ add_time_tm = gmtime_r (&add_time, &tm_buf); - strftime (dst, INET6_ADDRSTRLEN+5, "%Y-%m-%d %H-%M-%S", add_time_tm); - } - else -@@ -395,7 +396,7 @@ - if (sp->curlft.use_time != 0) - { - use_time = sp->curlft.use_time; -- use_time_tm = gmtime (&use_time); -+ use_time_tm = gmtime_r (&use_time, &tm_buf); - strftime (src, INET6_ADDRSTRLEN+5, "%Y-%m-%d %H-%M-%S", use_time_tm); - } - else -@@ -557,19 +558,43 @@ - } - - if (sp_info->sel.family == AF_INET) -- addr = nl_addr_build (sp_info->sel.family, &sp_info->sel.daddr.a4, sizeof (sp_info->sel.daddr.a4)); -+ { -+ if (!(addr = nl_addr_build (sp_info->sel.family, &sp_info->sel.daddr.a4, sizeof (sp_info->sel.daddr.a4)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } -+ } - else -- addr = nl_addr_build (sp_info->sel.family, &sp_info->sel.daddr.a6, sizeof (sp_info->sel.daddr.a6)); -+ { -+ if (!(addr = nl_addr_build (sp_info->sel.family, &sp_info->sel.daddr.a6, sizeof (sp_info->sel.daddr.a6)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } -+ } - nl_addr_set_prefixlen (addr, sp_info->sel.prefixlen_d); - xfrmnl_sel_set_daddr (sp->sel, addr); -+ /* Drop the reference count from the above set operation */ -+ nl_addr_put(addr); - xfrmnl_sel_set_prefixlen_d (sp->sel, sp_info->sel.prefixlen_d); - - if (sp_info->sel.family == AF_INET) -- addr = nl_addr_build (sp_info->sel.family, &sp_info->sel.saddr.a4, sizeof (sp_info->sel.saddr.a4)); -+ { -+ if (!(addr = nl_addr_build (sp_info->sel.family, &sp_info->sel.saddr.a4, sizeof (sp_info->sel.saddr.a4)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } -+ } - else -- addr = nl_addr_build (sp_info->sel.family, &sp_info->sel.saddr.a6, sizeof (sp_info->sel.saddr.a6)); -+ { -+ if (!(addr = nl_addr_build (sp_info->sel.family, &sp_info->sel.saddr.a6, sizeof (sp_info->sel.saddr.a6)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } -+ } - nl_addr_set_prefixlen (addr, sp_info->sel.prefixlen_s); - xfrmnl_sel_set_saddr (sp->sel, addr); -+ /* Drop the reference count from the above set operation */ -+ nl_addr_put(addr); - xfrmnl_sel_set_prefixlen_s (sp->sel, sp_info->sel.prefixlen_s); - - xfrmnl_sel_set_dport (sp->sel, ntohs (sp_info->sel.dport)); -@@ -628,13 +653,13 @@ - - if (tb[XFRMA_TMPL]) { - struct xfrm_user_tmpl* tmpl = nla_data(tb[XFRMA_TMPL]); -- struct xfrmnl_user_tmpl* sputmpl; - uint32_t i; - uint32_t num_tmpls = nla_len(tb[XFRMA_TMPL]) / sizeof (*tmpl); - struct nl_addr* addr; - - for (i = 0; (i < num_tmpls) && (tmpl); i ++, tmpl++) - { -+ _nl_auto_xfrmnl_user_tmpl struct xfrmnl_user_tmpl *sputmpl = NULL; - if ((sputmpl = xfrmnl_user_tmpl_alloc ()) == NULL) - { - err = -NLE_NOMEM; -@@ -642,19 +667,43 @@ - } - - if (tmpl->family == AF_INET) -- addr = nl_addr_build(tmpl->family, &tmpl->id.daddr.a4, sizeof (tmpl->id.daddr.a4)); -+ { -+ if (!(addr = nl_addr_build(tmpl->family, &tmpl->id.daddr.a4, sizeof (tmpl->id.daddr.a4)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } -+ } - else -- addr = nl_addr_build(tmpl->family, &tmpl->id.daddr.a6, sizeof (tmpl->id.daddr.a6)); -+ { -+ if (!(addr = nl_addr_build(tmpl->family, &tmpl->id.daddr.a6, sizeof (tmpl->id.daddr.a6)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } -+ } - xfrmnl_user_tmpl_set_daddr (sputmpl, addr); -+ /* Drop the reference count from the above set operation */ -+ nl_addr_put(addr); - xfrmnl_user_tmpl_set_spi (sputmpl, ntohl(tmpl->id.spi)); - xfrmnl_user_tmpl_set_proto (sputmpl, tmpl->id.proto); - xfrmnl_user_tmpl_set_family (sputmpl, tmpl->family); - - if (tmpl->family == AF_INET) -- addr = nl_addr_build(tmpl->family, &tmpl->saddr.a4, sizeof (tmpl->saddr.a4)); -+ { -+ if (!(addr = nl_addr_build(tmpl->family, &tmpl->saddr.a4, sizeof (tmpl->saddr.a4)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } -+ } - else -- addr = nl_addr_build(tmpl->family, &tmpl->saddr.a6, sizeof (tmpl->saddr.a6)); -+ { -+ if (!(addr = nl_addr_build(tmpl->family, &tmpl->saddr.a6, sizeof (tmpl->saddr.a6)))) { -+ err = -NLE_NOMEM; -+ goto errout; -+ } -+ } - xfrmnl_user_tmpl_set_saddr (sputmpl, addr); -+ /* Drop the reference count from the above set operation */ -+ nl_addr_put(addr); - - xfrmnl_user_tmpl_set_reqid (sputmpl, tmpl->reqid); - xfrmnl_user_tmpl_set_mode (sputmpl, tmpl->mode); -@@ -663,7 +712,7 @@ - xfrmnl_user_tmpl_set_aalgos (sputmpl, tmpl->aalgos); - xfrmnl_user_tmpl_set_ealgos (sputmpl, tmpl->ealgos); - xfrmnl_user_tmpl_set_calgos (sputmpl, tmpl->calgos); -- xfrmnl_sp_add_usertemplate (sp, sputmpl); -+ xfrmnl_sp_add_usertemplate (sp, _nl_steal_pointer(&sputmpl)); - - sp->ce_mask |= XFRM_SP_ATTR_TMPL; - } -@@ -1316,6 +1365,8 @@ - if (sp->ce_mask & XFRM_SP_ATTR_TMPL) { - sp->nr_user_tmpl--; - nl_list_del(&utmpl->utmpl_list); -+ if (sp->nr_user_tmpl == 0) -+ sp->ce_mask &= ~XFRM_SP_ATTR_TMPL; - } - } - -diff -urN libnl-3.7.0/tests/params.h libnl-3.7.0_new/tests/params.h ---- libnl-3.7.0/tests/params.h 1970-01-01 08:00:00.000000000 +0800 -+++ libnl-3.7.0_new/tests/params.h 2024-06-03 16:23:13.861244200 +0800 -@@ -0,0 +1,5 @@ -+#define DST_ADDR "addr" -+#define IP "ip" -+#define NEXTHOP "dev=1,via=2" -+#define DEV_NAME "dev_name" -+ -diff -urN libnl-3.7.0/tests/test-add-delete-addr.c libnl-3.7.0_new/tests/test-add-delete-addr.c ---- libnl-3.7.0/tests/test-add-delete-addr.c 1970-01-01 08:00:00.000000000 +0800 -+++ libnl-3.7.0_new/tests/test-add-delete-addr.c 2024-06-03 16:23:13.861244200 +0800 -@@ -0,0 +1,39 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+int main(int argc, char *argv[]) -+{ -+ struct nl_sock *sock; -+ struct rtnl_addr *addr; -+ struct nl_cache *link_cache; -+ int err = 0, nlflags = NLM_F_CREATE; -+ -+ sock = nl_cli_alloc_socket(); -+ nl_cli_connect(sock, NETLINK_ROUTE); -+ link_cache = nl_cli_link_alloc_cache(sock); -+ addr = nl_cli_addr_alloc(); -+ -+ nl_cli_addr_parse_local(addr, IP); -+ nl_cli_addr_parse_dev(addr, link_cache, DEV_NAME); -+ -+ if ((err = rtnl_addr_add(sock, addr, nlflags)) < 0) { -+ printf("Unable to add route: %s", nl_geterror(err)); -+ goto END; -+ } -+ -+ if ((err = rtnl_addr_delete(sock, addr, nlflags)) < 0) { -+ printf("Unable to add route: %s", nl_geterror(err)); -+ goto END; -+ } -+ -+END: -+ rtnl_addr_put(addr); -+ nl_cache_put(link_cache); -+ nl_socket_free(sock); -+ return err; -+} -diff -urN libnl-3.7.0/tests/test-add-delete-class.c libnl-3.7.0_new/tests/test-add-delete-class.c ---- libnl-3.7.0/tests/test-add-delete-class.c 1970-01-01 08:00:00.000000000 +0800 -+++ libnl-3.7.0_new/tests/test-add-delete-class.c 2024-06-03 16:23:13.876242300 +0800 -@@ -0,0 +1,142 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+static int default_yes = 0, deleted = 0, interactive = 0; -+static struct nl_sock *sk; -+ -+static int test_add_class() -+{ -+ struct rtnl_class *class; -+ struct rtnl_tc *tc; -+ struct nl_cache *link_cache; -+ struct nl_cli_tc_module *tm; -+ struct rtnl_tc_ops *ops; -+ int err = 0, flags = NLM_F_CREATE | NLM_F_EXCL; -+ char kind[] = "htb"; -+ char *rate[] = {DEV_NAME, "root", "htb", "--rate=100mbit"}; -+ -+ sk = nl_cli_alloc_socket(); -+ nl_cli_connect(sk, NETLINK_ROUTE); -+ link_cache = nl_cli_link_alloc_cache(sk); -+ class = nl_cli_class_alloc(); -+ tc = (struct rtnl_tc *) class; -+ -+ nl_cli_tc_parse_dev(tc, link_cache, DEV_NAME); -+ nl_cli_tc_parse_parent(tc, "root"); -+ if (!rtnl_tc_get_ifindex(tc)) { -+ printf("You must specify a network device (--dev=XXX)\n"); -+ err = -1; -+ goto END; -+ } -+ if (!rtnl_tc_get_parent(tc)) { -+ printf("You must specify a parent (--parent=XXX)\n"); -+ err = -1; -+ goto END; -+ } -+ -+ rtnl_tc_set_kind(tc, kind); -+ if (!(ops = rtnl_tc_get_ops(tc))) { -+ printf("Unknown class \"%s\"\n", kind); -+ err = -1; -+ goto END; -+ } -+ if (!(tm = nl_cli_tc_lookup(ops))) { -+ printf("class type \"%s\" not supported.\n", kind); -+ err = -1; -+ goto END; -+ } -+ tm->tm_parse_argv(tc, 4, rate); -+ -+ if ((err = rtnl_class_add(sk, class, flags)) < 0) { -+ printf("Unable to add class: %s\n", nl_geterror(err)); -+ goto END; -+ } -+ -+END: -+ nl_cache_mngt_unprovide(link_cache); -+ nl_cache_put(link_cache); -+ rtnl_class_put(class); -+ nl_socket_free(sk); -+ return err; -+} -+ -+ -+static void delete_cb(struct nl_object *obj, void *arg) -+{ -+ struct rtnl_class *class = nl_object_priv(obj); -+ struct nl_dump_params params = { -+ .dp_type = NL_DUMP_LINE, -+ .dp_fd = stdout, -+ }; -+ int err; -+ -+ if (interactive && !nl_cli_confirm(obj, ¶ms, default_yes)) -+ return; -+ -+ if ((err = rtnl_class_delete(sk, class)) < 0) -+ nl_cli_fatal(err, "Unable to delete class: %s\n", nl_geterror(err)); -+ -+ deleted++; -+} -+ -+static int test_delete_class() -+{ -+ struct rtnl_class *class; -+ struct rtnl_tc *tc; -+ struct nl_cache *link_cache, *class_cache; -+ struct nl_cli_tc_module *tm; -+ struct rtnl_tc_ops *ops; -+ char kind[] = "htb"; -+ int err = 0; -+ -+ sk = nl_cli_alloc_socket(); -+ nl_cli_connect(sk, NETLINK_ROUTE); -+ link_cache = nl_cli_link_alloc_cache(sk); -+ class = nl_cli_class_alloc(); -+ tc = (struct rtnl_tc *) class; -+ -+ nl_cli_tc_parse_dev(tc, link_cache, DEV_NAME); -+ nl_cli_tc_parse_parent(tc, "root"); -+ if (!rtnl_tc_get_ifindex(tc)) { -+ printf("You must specify a network device (--dev=XXX)\n"); -+ err = -1; -+ goto END; -+ } -+ if (!rtnl_tc_get_parent(tc)) { -+ printf("You must specify a parent (--parent=XXX)\n"); -+ err = -1; -+ goto END; -+ } -+ rtnl_tc_set_kind(tc, kind); -+ if (!(ops = rtnl_tc_get_ops(tc))) { -+ printf("Unknown class \"%s\"\n", kind); -+ err = -1; -+ goto END; -+ } -+ class_cache = nl_cli_class_alloc_cache(sk, rtnl_tc_get_ifindex(tc)); -+ nl_cache_foreach_filter(class_cache, OBJ_CAST(class), delete_cb, NULL); -+ -+END: -+ nl_cache_put(link_cache); -+ nl_socket_free(sk); -+ rtnl_class_put(class); -+ return err; -+} -+ -+int main(int argc, char *argv[]) -+{ -+ int err = 0; -+ if ((err = test_add_class()) < 0) { -+ printf("Unable to add class\n"); -+ } -+ if ((err = test_delete_class()) < 0) { -+ printf("Unable to delete class"); -+ } -+ return err; -+} -diff -urN libnl-3.7.0/tests/test-add-delete-neigh.c libnl-3.7.0_new/tests/test-add-delete-neigh.c ---- libnl-3.7.0/tests/test-add-delete-neigh.c 1970-01-01 08:00:00.000000000 +0800 -+++ libnl-3.7.0_new/tests/test-add-delete-neigh.c 2024-06-03 16:23:13.876242300 +0800 -@@ -0,0 +1,41 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+int main(int argc, char *argv[]) -+{ -+ struct nl_sock *sk; -+ struct rtnl_neigh *neigh; -+ struct nl_cache *link_cache; -+ int err = 0, ok = 0, nlflags = NLM_F_REPLACE | NLM_F_CREATE; -+ char lladdr[] = "AA:BB:CC:DD:EE:FF"; -+ -+ sk = nl_cli_alloc_socket(); -+ nl_cli_connect(sk, NETLINK_ROUTE); -+ link_cache = nl_cli_link_alloc_cache(sk); -+ neigh = nl_cli_neigh_alloc(); -+ -+ nl_cli_neigh_parse_dst(neigh, DST_ADDR); -+ nl_cli_neigh_parse_lladdr(neigh, lladdr); -+ nl_cli_neigh_parse_dev(neigh, link_cache, DEV_NAME); -+ -+ if ((err = rtnl_neigh_add(sk, neigh, nlflags)) < 0){ -+ printf("Unable to add neighbour: %s\n",nl_geterror(err)); -+ goto END; -+ } -+ -+ if ((err = rtnl_neigh_delete(sk, neigh, nlflags)) < 0){ -+ printf("Unable to add neighbour: %s\n",nl_geterror(err)); -+ goto END; -+ } -+ -+END: -+ nl_socket_free(sk); -+ nl_cache_put(link_cache); -+ rtnl_neigh_put(neigh); -+ return err; -+} -diff -urN libnl-3.7.0/tests/test-add-delete-qdisc.c libnl-3.7.0_new/tests/test-add-delete-qdisc.c ---- libnl-3.7.0/tests/test-add-delete-qdisc.c 1970-01-01 08:00:00.000000000 +0800 -+++ libnl-3.7.0_new/tests/test-add-delete-qdisc.c 2024-06-03 16:23:13.892190500 +0800 -@@ -0,0 +1,156 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+static int default_yes = 0, deleted = 0, interactive = 0; -+static struct nl_sock *sk; -+ -+static void delete_cb(struct nl_object *obj, void *arg) -+{ -+ struct rtnl_qdisc *qdisc = nl_object_priv(obj); -+ struct nl_dump_params params = { -+ .dp_type = NL_DUMP_LINE, -+ .dp_fd = stdout, -+ }; -+ int err; -+ -+ /* Ignore default qdiscs, unable to delete */ -+ if (rtnl_tc_get_handle((struct rtnl_tc *) qdisc) == 0) -+ return; -+ -+ if (interactive && !nl_cli_confirm(obj, ¶ms, default_yes)) -+ return; -+ -+ if ((err = rtnl_qdisc_delete(sk, qdisc)) < 0) { -+ nl_cli_fatal(err, "Unable to delete qdisc: %s\n", nl_geterror(err)); -+ } -+ deleted++; -+} -+ -+static int test_delete_qdisc() -+{ -+ struct rtnl_qdisc *qdisc; -+ struct rtnl_tc *tc; -+ struct nl_cache *link_cache, *qdisc_cache; -+ struct nl_cli_tc_module *tm; -+ struct rtnl_tc_ops *ops; -+ char kind[] = "htb"; -+ int err = 0; -+ -+ sk = nl_cli_alloc_socket(); -+ nl_cli_connect(sk, NETLINK_ROUTE); -+ link_cache = nl_cli_link_alloc_cache(sk); -+ qdisc_cache = nl_cli_qdisc_alloc_cache(sk); -+ qdisc = nl_cli_qdisc_alloc(); -+ tc = (struct rtnl_tc *) qdisc; -+ nl_cli_tc_parse_dev(tc, link_cache, DEV_NAME); -+ nl_cli_tc_parse_parent(tc, "root"); -+ -+ if (!rtnl_tc_get_ifindex(tc)) { -+ printf("You must specify a network device (--dev=XXX)"); -+ goto END; -+ } -+ -+ if (!rtnl_tc_get_parent(tc)) { -+ printf("You must specify a parent"); -+ goto END; -+ } -+ -+ rtnl_tc_set_kind(tc, kind); -+ if (!(ops = rtnl_tc_get_ops(tc))) { -+ printf("Unknown qdisc \"%s\"", kind); -+ goto END; -+ } -+ -+ if (!(tm = nl_cli_tc_lookup(ops))) { -+ nl_cli_fatal(ENOTSUP, "Qdisc type \"%s\" not supported.", kind); -+ goto END; -+ } -+ -+ -+ nl_cache_foreach_filter(qdisc_cache, OBJ_CAST(qdisc), delete_cb, NULL); -+ -+END: -+ nl_cache_put(link_cache); -+ nl_cache_put(qdisc_cache); -+ rtnl_qdisc_put(qdisc); -+ nl_socket_free(sk); -+ return err; -+} -+ -+static int test_add_qdisc() -+{ -+ struct rtnl_qdisc *qdisc; -+ struct rtnl_tc *tc; -+ struct nl_cache *link_cache; -+ struct nl_cli_tc_module *tm; -+ struct rtnl_tc_ops *ops; -+ char kind[] = "htb"; -+ int err = 0, flags = NLM_F_CREATE | NLM_F_EXCL; -+ -+ if (!(sk = nl_socket_alloc())){ -+ printf("Unable to allocate netlink socket\n"); -+ return -1; -+ } -+ if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { -+ printf("Unable to connect netlink socket: %s\n", nl_geterror(err)); -+ nl_socket_free(sk); -+ return -1; -+ } -+ link_cache = nl_cli_link_alloc_cache(sk); -+ qdisc = nl_cli_qdisc_alloc(); -+ tc = (struct rtnl_tc *) qdisc; -+ -+ nl_cli_tc_parse_dev(tc, link_cache, DEV_NAME); -+ nl_cli_tc_parse_parent(tc, "root"); -+ -+ if (!rtnl_tc_get_ifindex(tc)){ -+ printf("You must specify a network device (--dev=XXX)\n"); -+ goto END; -+ } -+ -+ if (!rtnl_tc_get_parent(tc)){ -+ printf("You must specify a parent\n"); -+ goto END; -+ } -+ -+ rtnl_tc_set_kind(tc, kind); -+ if (!(ops = rtnl_tc_get_ops(tc))){ -+ printf("Unknown qdisc \"%s\"\n", kind); -+ goto END; -+ } -+ if (!(tm = nl_cli_tc_lookup(ops))){ -+ nl_cli_fatal(ENOTSUP, "Qdisc type \"%s\" not supported.\n", kind); -+ goto END; -+ } -+ -+ if ((err = rtnl_qdisc_add(sk, qdisc, flags)) < 0){ -+ printf("Unable to add qdisc: %s\n", nl_geterror(err)); -+ goto END; -+ } -+ -+END: -+ nl_cache_mngt_unprovide(link_cache); -+ nl_cache_put(link_cache); -+ rtnl_qdisc_put(qdisc); -+ nl_socket_free(sk); -+ return err; -+} -+ -+int main(int args, char *argv[]) -+{ -+ int err = 0; -+ if ((err = test_add_qdisc()) < 0) { -+ printf("Unable to add qdisc:%s", nl_geterror(err)); -+ } -+ if ((err = test_delete_qdisc()) < 0) { -+ printf("Unable to delete qdisc:%s", nl_geterror(err)); -+ } -+ return err; -+} -diff -urN libnl-3.7.0/tests/test-add-delete-route.c libnl-3.7.0_new/tests/test-add-delete-route.c ---- libnl-3.7.0/tests/test-add-delete-route.c 1970-01-01 08:00:00.000000000 +0800 -+++ libnl-3.7.0_new/tests/test-add-delete-route.c 2024-06-03 16:23:13.892190500 +0800 -@@ -0,0 +1,43 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+int main(int argc, char argv[]) -+{ -+ struct nl_sock *sk; -+ struct rtnl_route *route; -+ struct nl_cache *link_cache, *route_cache; -+ char dst_addr[] = DST_ADDR; -+ char nexthop[] = NEXTHOP; -+ int err = 0; -+ -+ sk = nl_cli_alloc_socket(); -+ nl_cli_connect(sk, NETLINK_ROUTE); -+ link_cache = nl_cli_link_alloc_cache(sk); -+ route_cache = nl_cli_route_alloc_cache(sk, 0); -+ route = nl_cli_route_alloc(); -+ -+ nl_cli_route_parse_dst(route, dst_addr); -+ nl_cli_route_parse_nexthop(route, nexthop, link_cache); -+ -+ if ((err = rtnl_route_add(sk, route, NLM_F_EXCL)) < 0) { -+ printf("Unable to add route: %s", nl_geterror(err)); -+ goto END; -+ } -+ -+ if ((err = rtnl_route_delete(sk, route, NLM_F_EXCL)) < 0) { -+ printf("Unable to delete route: %s", nl_geterror(err)); -+ goto END; -+ } -+ -+END: -+ rtnl_route_put(route); -+ nl_cache_put(link_cache); -+ nl_cache_put(route_cache); -+ nl_socket_free(sk); -+ return err; -+} -diff -urN libnl-3.7.0/tests/test-genl-connect.c libnl-3.7.0_new/tests/test-genl-connect.c ---- libnl-3.7.0/tests/test-genl-connect.c 1970-01-01 08:00:00.000000000 +0800 -+++ libnl-3.7.0_new/tests/test-genl-connect.c 2024-06-03 16:23:13.892190500 +0800 -@@ -0,0 +1,37 @@ -+#include -+#include -+#include -+#include -+ -+ -+int main(int argc, char *argv[]) -+{ -+ struct nl_sock *sk; -+ struct nl_cache *family_cache; -+ struct nl_dump_params params = { -+ .dp_type = NL_DUMP_LINE, -+ .dp_fd = stdout, -+ }; -+ int err = 0; -+ -+ sk = nl_socket_alloc(); -+ if ((err = genl_connect(sk)) < 0) { -+ printf("Unable create socket: %s\n", nl_geterror(err)); -+ goto END; -+ } -+ nl_socket_enable_auto_ack(sk); -+ -+ if (nl_socket_get_fd(sk) < 0) { -+ printf("vaild socket\n"); -+ err = -1; -+ goto END; -+ } -+ nl_socket_set_buffer_size(sk, 32655, 32655); -+ family_cache = nl_cli_alloc_cache(sk, "generic netlink family", genl_ctrl_alloc_cache); -+ nl_cache_dump(family_cache, ¶ms); -+ -+END: -+ nl_socket_free(sk); -+ nl_cache_put(family_cache); -+ return err; -+} -diff -urN libnl-3.7.0/tests/test-link.c libnl-3.7.0_new/tests/test-link.c ---- libnl-3.7.0/tests/test-link.c 1970-01-01 08:00:00.000000000 +0800 -+++ libnl-3.7.0_new/tests/test-link.c 2024-06-03 18:34:22.314718700 +0800 -@@ -0,0 +1,68 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+static int self_def_cb = NL_CB_DEBUG; -+ -+ -+int main(int argc, char *argv[]) -+{ -+ struct nl_sock *sk; -+ struct nl_cache *link_cache; -+ struct rtnl_link *link; -+ struct nl_addr *addr; -+ struct nl_cb *cb; -+ int err = 0, ifindex, pid; -+ char *buf; -+ -+ cb = nl_cb_alloc(self_def_cb); -+ pid = getpid(); -+ -+ if (!(sk = nl_socket_alloc_cb(cb))) { -+ nl_cli_fatal(ENOBUFS, "Unable to allocate netlink socket\n"); -+ } -+ nl_cli_connect(sk, NETLINK_ROUTE); -+ nl_socket_disable_seq_check(sk); -+ nl_socket_disable_auto_ack(sk); -+ nl_socket_set_local_port(sk, pid); -+ nl_join_groups(sk, pid); -+ nl_socket_drop_membership(sk, pid); -+ nl_socket_set_peer_port(sk, 0); -+ -+ link_cache = nl_cli_link_alloc_cache(sk); -+ link = nl_cli_link_alloc(); -+ -+ if (err = nl_socket_get_peer_port(sk)){ -+ printf("peer_port %d\n", err); -+ goto END; -+ } -+ if (err = nl_socket_use_seq(sk)) -+ printf("sk->s_seq_next %d\n", err); -+ -+ if ((ifindex = rtnl_link_get_ifindex(link)) == 0){ -+ printf("ifindex is not set, %d\n", ifindex); -+ rtnl_link_set_ifindex(link, 1); -+ }; -+ -+ if (rtnl_link_get(link_cache, 1)){ -+ printf("now link is cached\n"); -+ }else{ -+ nl_cache_add(link_cache, (struct nl_object *)link); -+ }; -+ -+ if ((err = rtnl_link_add(sk, link, AF_INET)) < 0){ -+ printf("Unable to add link %s\n", nl_geterror(err)); -+ goto END; -+ } -+ -+END: -+ nl_cb_put(cb); -+ nl_socket_free(sk); -+ nl_cache_put(link_cache); -+ rtnl_link_put(link); -+ return err; -+} diff --git a/solve-oh-compile-problem.patch b/solve-oh-compile-problem.patch deleted file mode 100644 index 3f064e2..0000000 --- a/solve-oh-compile-problem.patch +++ /dev/null @@ -1,73 +0,0 @@ -diff -urN libnl-3.7.0/include/netlink-private/utils.h libnl-3.7.0-new/include/netlink-private/utils.h ---- libnl-3.7.0/include/netlink-private/utils.h 2022-05-24 16:55:12.000000000 +0800 -+++ libnl-3.7.0-new/include/netlink-private/utils.h 2024-01-13 18:22:20.218754000 +0800 -@@ -114,10 +114,8 @@ - - #define _nl_assert_addr_family(addr_family) \ - do { \ -- typeof(addr_family) _addr_family = (addr_family); \ -- \ -- _nl_assert(_addr_family == AF_INET || \ -- _addr_family == AF_INET6); \ -+ _nl_assert(addr_family == AF_INET || \ -+ addr_family == AF_INET6); \ - } while (0) - - /*****************************************************************************/ -diff -urN libnl-3.7.0/lib/route/link/vrf.c libnl-3.7.0-new/lib/route/link/vrf.c ---- libnl-3.7.0/lib/route/link/vrf.c 2022-05-24 16:55:12.000000000 +0800 -+++ libnl-3.7.0-new/lib/route/link/vrf.c 2024-01-15 17:14:53.121653400 +0800 -@@ -27,7 +27,6 @@ - #include - - #include --#include - - #define VRF_TABLE_ID_MAX RT_TABLE_MAX - -diff -urN libnl-3.7.0/lib/route/mdb.c libnl-3.7.0-new/lib/route/mdb.c ---- libnl-3.7.0/lib/route/mdb.c 2022-05-24 16:55:12.000000000 +0800 -+++ libnl-3.7.0-new/lib/route/mdb.c 2024-01-13 18:23:23.811971400 +0800 -@@ -7,7 +7,7 @@ - #include - #include - #include --#include -+#include - - /** @cond SKIP */ - #define MDB_ATTR_IFINDEX 0x000001 -diff -urN libnl-3.7.0/lib/utils.c libnl-3.7.0-new/lib/utils.c ---- libnl-3.7.0/lib/utils.c 2022-07-06 23:21:11.000000000 +0800 -+++ libnl-3.7.0-new/lib/utils.c 2024-01-13 18:24:36.832909100 +0800 -@@ -219,8 +219,8 @@ - - int nl_rate2str(unsigned long long rate, int type, char *buf, size_t len) - { -- char *unit; -- double frac; -+ char *unit = NULL; -+ double frac = 0.0; - - switch (type) { - case NL_BYTE_RATE: -@@ -367,6 +367,7 @@ - } - - BUG(); -+ return buf; - } - - /** -diff -urN libnl-3.7.0/src/lib/utils.c libnl-3.7.0-new/src/lib/utils.c ---- libnl-3.7.0/src/lib/utils.c 2022-05-24 16:55:12.000000000 +0800 -+++ libnl-3.7.0-new/src/lib/utils.c 2024-01-15 16:17:20.093386000 +0800 -@@ -24,6 +24,8 @@ - #include - #endif - -+#define PKGLIBDIR "/usr/local/lib" -+ - /** - * Parse a text based 32 bit unsigned integer argument - * @arg arg Integer in text form.