1
0
Fork 0

Adding upstream version 10.1.1.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-05 10:03:58 +01:00
parent abd9495f94
commit 3124f89aed
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
7332 changed files with 1661972 additions and 0 deletions

227
.clang-format Normal file
View file

@ -0,0 +1,227 @@
# SPDX-License-Identifier: GPL-2.0
# clang-format configuration file. Intended for clang-format >= 11.
# If the version is changed also check that CI tool frrbot is updated.
#
# For more information, see:
#
# Documentation/process/clang-format.rst
# https://clang.llvm.org/docs/ClangFormat.html
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
#
---
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
# FRR: Right
AlignEscapedNewlines: Right
AlignOperands: Align
# FRR: true
AlignTrailingComments: true
# FRR: true
AlignConsecutiveMacros: true
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: false
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 80
# Linux: CommentPragmas: '^ IWYU pragma:'
CommentPragmas: '\$(FRR|clippy)'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
# Some taken from:
# git grep -h '^#define [^[:space:]]*frr_(each|with)[^[:space:]]*(' ./ \
# | sed "s,^#define \([^[:space:]]*frr_(each|with)[^[:space:]]*\)(.*$, - '\1'," \
# | LC_ALL=C sort -u
# and
# git grep -h '^#define [^[:space:]]*FOREACH[^[:space:]]*(' ./
# | sed "s,^#define \([^[:space:]]*FOREACH[^)]*\)(.*, - '\1',"
# | LC_ALL=C sort -u
ForEachMacros:
# lib: outliers:
- 'FOR_ALL_INTERFACES'
# libyang outliers:
- 'LY_FOR_KEYS'
- 'LY_LIST_FOR'
- 'LYD_LIST_FOR_INST'
- 'LYD_LIST_FOR_INST_SAFE'
- 'LY_TREE_FOR'
- 'LY_TREE_DFS_BEGIN'
- 'LYD_TREE_DFS_BEGIN'
# ospfd outliers:
- 'LSDB_LOOP'
# first git grep
- 'darr_foreach_p'
- 'darr_foreach_i'
- 'frr_each'
- 'frr_each_safe'
- 'frr_each_from'
- 'frr_rev_each'
- 'frr_rev_each_safe'
- 'frr_rev_each_from'
- 'frr_with_mutex'
- 'frr_with_privs'
# second git grep
- 'AF_FOREACH'
- 'FOREACH_ADAPTER_IN_LIST'
- 'FOREACH_AFI_SAFI'
- 'FOREACH_AFI_SAFI_NSF'
- 'FOREACH_BE_APPLY_BATCH_IN_LIST'
- 'FOREACH_BE_CLIENT_BITS'
- 'FOREACH_BE_TXN_BATCH_IN_LIST'
- 'FOREACH_BE_TXN_IN_LIST'
- 'FOREACH_CMT_REC'
- 'FOREACH_MGMTD_BE_CLIENT_ID'
- 'FOREACH_MGMTD_DS_ID'
- 'FOREACH_SAFI'
- 'FOREACH_SESSION_IN_LIST'
- 'FOREACH_TXN_CFG_BATCH_IN_LIST'
- 'FOREACH_TXN_IN_LIST'
- 'FOREACH_TXN_REQ_IN_LIST'
- 'JSON_FOREACH'
- 'LIST_FOREACH'
- 'LIST_FOREACH_SAFE'
- 'RB_FOREACH'
- 'RB_FOREACH_REVERSE'
- 'RB_FOREACH_REVERSE_SAFE'
- 'RB_FOREACH_SAFE'
- 'RE_DEST_FOREACH_ROUTE'
- 'RE_DEST_FOREACH_ROUTE_SAFE'
- 'RNODE_FOREACH_RE'
- 'RNODE_FOREACH_RE_SAFE'
- 'SIMPLEQ_FOREACH'
- 'SIMPLEQ_FOREACH_SAFE'
- 'SLIST_FOREACH'
- 'SLIST_FOREACH_PREVPTR'
- 'SLIST_FOREACH_SAFE'
- 'SPLAY_FOREACH'
- 'STAILQ_FOREACH'
- 'STAILQ_FOREACH_SAFE'
- 'SUBGRP_FOREACH_ADJ'
- 'SUBGRP_FOREACH_ADJ_SAFE'
- 'SUBGRP_FOREACH_PEER'
- 'SUBGRP_FOREACH_PEER_SAFE'
- 'TAILQ_FOREACH'
- 'TAILQ_FOREACH_REVERSE'
- 'TAILQ_FOREACH_REVERSE_SAFE'
- 'TAILQ_FOREACH_SAFE'
- 'UPDGRP_FOREACH_SUBGRP'
- 'UPDGRP_FOREACH_SUBGRP_SAFE'
- 'XSIMPLEQ_FOREACH'
- 'XSIMPLEQ_FOREACH_SAFE'
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^(<|lib)'
Priority: 0
## New: XXX whats it mean?
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
IndentGotoLabels: false
IndentPPDirectives: None
IndentWidth: 8
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
## Linux: MaxEmptyLinesToKeep: 1
MaxEmptyLinesToKeep: 2
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 8
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
## Lowest Penalty Value wins. Values are used by clang-format to influence
## the brak decisions, it's a bit of voodoo magic though.
## Originally from linux which was "Taken from git's rules"
PenaltyBreakAssignment: 30
PenaltyBreakComment: 10
PenaltyBreakFirstLessLess: 0
# Don't break a string into multi-string-fragments
PenaltyBreakString: 1000
# Allow going past the ColumnLimit to keep function arguments aligned
# with the open parenthesis.
PenaltyBreakBeforeFirstCallParameter: 1000
# Try and stay under ColumnLimit, but not at the cost of incomprehensible code.
PenaltyExcessCharacter: 30
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: false
SortIncludes: false
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp03
TabWidth: 8
UseTab: Always
WhitespaceSensitiveMacros:
- "DEFPY"
- "DEFPY_HIDDEN"
- "DEFPY_NOSH"
- "DEFPY_YANG"
- "DEFPY_YANG_HIDDEN"
- "DEFPY_YANG_NOSH"
- "DEFSH"
- "DEFSH_HIDDEN"
- "DEFUN"
- "DEFUN_HIDDEN"
- "DEFUN_NOSH"
- "DEFUN_YANG"
- "DEFUN_YANG_HIDDEN"
- "DEFUN_YANG_NOSH"
- "DEFUNSH"
- "DEFUNSH_HIDDEN"
...

9
.dockerignore Normal file
View file

@ -0,0 +1,9 @@
.git
**/*.a
**/*.o
**/*.la
**/*.lo
**/*.so
**/.libs
docker/alpine/pkgs
docker/centos/pkgs

3
.flake8 Normal file
View file

@ -0,0 +1,3 @@
[flake8]
max-line-length = 88
extend-ignore = E203

24
.git-blame-ignore-revs Normal file
View file

@ -0,0 +1,24 @@
# Following revs are all whitespace changes; use with
# git blame --ignore-revs-file .git-blame-ignore-revs <...>
# or to make it permanent
# git config blame.ignoreRevsFile .git-blame-ignore-revs
9fa6ec14737b94fdfb41539d96c7e4f84f3514b6
701a01920eee5431d2052aad92aefbdf50ac2139
bf2394f08bdc91a6cbd3784a1bfa3af3247bb06f
0157c327715ca367d13b7f02b2981f3484ccdeeb
787e762445d50ca5b52fafcf8dd6de08ab90916f
ac2914d3261a78cf78eec7a6e20ebbe42bb57150
ac4d0be5874fafd14212d6007fff7495edc9b152
d62a17aedeb0eebdba98238874bb13d62c48dbf9
c14777c6bfd0a446c85243d3a9835054a259c276
996c93142d3abfab0f6d6c800474e22a8cfbdbc5
# require semicolon after macro XYZ
67b0f40c98aeb9bbc95370fe2be29e56a00a8748
80413c2073a20774b264ab04f7a4ea4515699790
960b9a53837d1aefa16bd531c7087f800dbe147b
96244aca23adec551c29b78f26605f8af8eea53e
8451921b70044a2c1075e7ba391f095fabee2550
bf8d3d6aca3f20255a621ed1c148fd05b3a8ae5c
96941f80927ce31a41f7d1905717f099187be723
# apply `black` python formatting for all tests/topotests
1a1c2a9f84d0ad1bdadc0cb47d6175d4ccc32544

78
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View file

@ -0,0 +1,78 @@
name: Bug report
description: Report a bug in the FRRouting software
labels: triage
body:
- type: markdown
attributes:
value: >
**This form is only for reporting a bug in the FRRouting software.**
If you need help troubleshooting your configuration, have a problem
building or installing the software, or want to ask a question or
discuss the project, learn how to [connect with the FRRouting
community](https://frrouting.org/community/).
**Do not include sensitive information in this report.** IP addresses
should be masked (example: 192.XXX.XXX.32/24).
- type: textarea
id: description
attributes:
label: Description
description: Provide a clear and concise description of the bug.
validations:
required: true
- type: textarea
id: version
attributes:
label: Version
description: >
Run the `show version` command in the VTY shell, and provide the output
here. (If possible, test the current development version of FRRouting
for this bug.)
render: text
validations:
required: true
- type: textarea
id: how-to-reproduce
attributes:
label: How to reproduce
description: >
Give a list of steps that someone else can follow to observe
the bug. Be as descriptive as possible, including any relevant
configuration files and commands used. Topology diagrams are
helpful when the bug involves more than one router.
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: Expected behavior
description: >
What do you expect to happen when following the steps above?
validations:
required: true
- type: textarea
id: actual-behavior
attributes:
label: Actual behavior
description: >
What actually happens when following the steps above? Include
screenshots, log file snippets, and/or platform routing tables
as appropriate. If a crash occurs, provide a backtrace.
validations:
required: true
- type: textarea
id: additional-context
attributes:
label: Additional context
description: >
Include any other relevant information about this bug here.
- type: checkboxes
id: checklist
attributes:
label: Checklist
options:
- label: I have searched the open issues for this bug.
required: true
- label: I have not included sensitive information in this report.
required: true

8
.github/PULL_REQUEST_TEMPLATE/pr.md vendored Normal file
View file

@ -0,0 +1,8 @@
### Summary
[fill here]
### Related Issue
[fill here if applicable]
### Components
[bgpd, build, doc, ripd, ospfd, eigrpd, isisd, etc. etc.]

46
.github/commitlint.config.js vendored Normal file
View file

@ -0,0 +1,46 @@
module.exports = {
rules: {
'header-max-length': [2, 'always', 72],
'type-case': [2, 'always', 'lower-case'],
'type-empty': [2, 'never'],
'type-enum': [
2,
'always',
[
'babeld',
'bfdd',
'bgpd',
'build',
'doc',
'docker',
'eigrpd',
'fpm',
'isisd',
'ldpd',
'lib',
'mgmtd',
'multi',
'nhrpd',
'ospf6d',
'ospfd',
'pathd',
'pbrd',
'pimd',
'pim6d',
'ripd',
'ripngd',
'sharpd',
'staticd',
'tests',
'tools',
'vtysh',
'vrrpd',
'yang',
'zebra',
'all',
],
],
'subject-empty': [2, 'never'],
'subject-full-stop': [2, 'never', '.'],
},
};

20
.github/workflows/base-branch-label.yml vendored Normal file
View file

@ -0,0 +1,20 @@
name: Add base branch label
on:
pull_request_target:
types:
- opened
- reopened
jobs:
label:
if: github.repository == 'frrouting/frr'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions-ecosystem/action-add-labels@v1
with:
labels: |
${{ github.event.pull_request.base.ref }}

28
.github/workflows/behind-base.yml vendored Normal file
View file

@ -0,0 +1,28 @@
name: Add rebase label if the branch is > 50 commits behind
on:
pull_request_target:
types: [synchronize, opened, reopened, labeled, unlabeled]
jobs:
behind:
if: github.repository == 'frrouting/frr'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Set custom variables
id: vars
run: |
echo "behind_by=$(git log --oneline origin/${{ github.base_ref }} ^${{ github.event.pull_request.head.sha }} | wc -l)" >> $GITHUB_OUTPUT
- name: Add rebase label if needed
if: ${{ steps.vars.outputs.behind_by > 50 }}
uses: actions-ecosystem/action-add-labels@v1
with:
labels: rebase

163
.github/workflows/build-test-docker.yml vendored Normal file
View file

@ -0,0 +1,163 @@
name: build-test
on:
pull_request:
push:
branches:
- 'master'
- 'stable/**'
defaults:
run:
shell: bash
jobs:
build-docker:
name: Build the ubuntu 22.04 docker image
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Build docker image
run: |
docker build -t frr-ubuntu22 -f docker/ubuntu-ci/Dockerfile .
docker save --output /tmp/frr-ubuntu22.tar frr-ubuntu22
- name: Upload docker image artifact
uses: actions/upload-artifact@v4
with:
name: ubuntu-image
path: /tmp/frr-ubuntu22.tar
- name: Clear any previous results
# So if all jobs are re-run then all tests will be re-run
run: |
rm -rf test-results*
mkdir -p test-results
touch test-results/cleared-results.txt
- name: Save cleared previous results
uses: actions/upload-artifact@v4
with:
name: test-results
path: test-results
overwrite: true
- name: Cleanup
if: ${{ always() }}
run: rm -rf test-results* /tmp/frr-ubuntu22.tar
test-docker:
name: Test ubuntu docker image
needs: build-docker
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Fetch docker image artifact
uses: actions/download-artifact@v4
with:
name: ubuntu-image
path: /tmp
- name: Fetch previous results
if: ${{ github.run_attempt > 1 }}
uses: actions/download-artifact@v4
with:
name: test-results
path: test-results
- name: Run topotests
run: |
uname -a
MODPKGVER=$(uname -r)
sudo apt-get update -y
# Github is running old kernels but installing newer packages :(
sudo apt-get install -y linux-modules-extra-azure linux-modules-${MODPKGVER} linux-modules-extra-${MODPKGVER} python3-xmltodict
sudo modprobe vrf || true
sudo modprobe mpls-iptunnel
sudo modprobe mpls-router
docker load --input /tmp/frr-ubuntu22.tar
if ! grep CONFIG_IP_MROUTE_MULTIPLE_TABLES=y /boot/config*; then
ADD_DOCKER_ENV+="-e MROUTE_VRF_MISSING=1"
fi
echo "ADD_DOCKER_ENV: ${ADD_DOCKER_ENV}"
if [ -f test-results/topotests.xml ]; then
./tests/topotests/analyze.py -r test-results
ls -l test-results/topotests.xml
run_tests=$(./tests/topotests/analyze.py -r test-results | cut -f1 -d: | sort -u)
else
echo "No test results dir"
run_tests=""
fi
rm -rf test-results* /tmp/topotests
echo RUN_TESTS: $run_tests
if docker run --init -i --privileged --name frr-ubuntu-cont ${ADD_DOCKER_ENV} -v /lib/modules:/lib/modules frr-ubuntu22 \
bash -c 'cd ~/frr/tests/topotests ; sudo -E pytest -n$(($(nproc) * 5 / 2)) --dist=loadfile '$run_tests; then
echo "All tests passed."
exit 0
fi
# Grab the results from the container
if ! ./tests/topotests/analyze.py -Ar test-results -C frr-ubuntu-cont; then
if [ ! -d test-results ]; then
echo "ERROR: Basic failure in docker run, no test results directory available." >&2
exit 1;
fi
if [ ! -f test-results/topotests.xml ]; then
# In this case we may be missing topotests.xml
echo "ERROR: No topotests.xml available perhaps docker run aborted?" >&2
exit 1;
fi
echo "WARNING: analyyze.py returned error but grabbed results anyway." >&2
fi
# Save some information useful for debugging
cp /boot/config* test-results/
sysctl -a > test-results/sysctl.out 2> /dev/null
# Now get the failed tests (if any) from the archived results directory.
rerun_tests=$(./tests/topotests/analyze.py -r test-results | cut -f1 -d: | sort -u)
if [ -z "$rerun_tests" ]; then
echo "All tests passed during parallel run."
exit 0
fi
echo "ERROR: Some tests failed during parallel run, rerunning serially." >&2
echo RERUN_TESTS: $rerun_tests >&2
docker stop frr-ubuntu-cont
docker rm frr-ubuntu-cont
mv test-results test-results-initial
if docker run --init -i --privileged --name frr-ubuntu-cont ${ADD_DOCKER_ENV} -v /lib/modules:/lib/modules frr-ubuntu22 \
bash -c 'cd ~/frr/tests/topotests ; sudo -E pytest '$rerun_tests; then
echo "All rerun tests passed."
exit 0
fi
echo "Some rerun tests still failed."
exit 1
- name: Gather results
if: ${{ always() }}
run: |
if [ ! -d test-results ]; then
if ! ./tests/topotests/analyze.py -Ar test-results -C frr-ubuntu-cont; then
echo "ERROR: gathering results produced an error, perhaps due earlier run cancellation." >&2
fi
fi
- name: Upload test results
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: test-results
path: |
test-results
test-results-initial
overwrite: true
- name: Cleanup
if: ${{ always() }}
run: |
rm -rf test-results* /tmp/frr-ubuntu22.tar
docker stop frr-ubuntu-cont || true
docker rm frr-ubuntu-cont || true

30
.github/workflows/commitlint.yml vendored Normal file
View file

@ -0,0 +1,30 @@
name: commitlint
on:
pull_request:
types:
- opened
- reopened
- synchronize
- labeled
- unlabeled
jobs:
commitlint:
if: ${{ github.repository == 'frrouting/frr' }} && ${{ github.base_ref == 'refs/heads/master' }}
name: Check if the commits meet the requirements of the guidelines
permissions:
contents: read
pull-requests: read
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Check Commit
uses: wagoid/commitlint-github-action@v5
with:
configFile: .github/commitlint.config.js
helpURL: 'https://docs.frrouting.org/projects/dev-guide/en/latest/workflow.html#submitting-patches-and-enhancements'

21
.github/workflows/conflicts.yml vendored Normal file
View file

@ -0,0 +1,21 @@
name: Add a conflict label if PR needs to rebase
on:
pull_request_target:
types: [opened, reopened, synchronize]
jobs:
conflicts:
if: github.repository == 'frrouting/frr'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Check if PRs need a rebase (have some conflicts)
uses: eps1lon/actions-label-merge-conflict@releases/2.x
with:
dirtyLabel: "conflicts"
removeOnDirtyLabel: "no_conflicts"
repoToken: "${{ secrets.GITHUB_TOKEN }}"
commentOnDirty: "This pull request has conflicts, please resolve those before we can evaluate the pull request."

View file

@ -0,0 +1,53 @@
name: Build daily 'master' images for Docker
on:
schedule:
- cron: '59 23 * * *'
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
jobs:
docker_daily_master:
if: github.repository == 'frrouting/frr'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Custom variables
id: vars
run: |
# To package a specific git commit, the date of the commit gets
# appended to the latest release, e.g. 1.0.0_git20180204.
# This is the requirement by APKBUILD (abuild).
# More details: https://wiki.alpinelinux.org/wiki/APKBUILD_Reference.
echo ::set-output name=date::$(date +'%Y%m%d')
- name: Checkout
uses: actions/checkout@v3
with:
ref: master
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
registry: quay.io
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_ROBOT_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v3
with:
context: .
file: ./docker/alpine/Dockerfile
push: true
tags: quay.io/frrouting/frr:master
build-args: PKGVER=${{ steps.vars.outputs.date }}
platforms: linux/amd64,linux/arm64,linux/arm/v7

17
.github/workflows/freeze.yml vendored Normal file
View file

@ -0,0 +1,17 @@
name: Warn before merging if a "freeze" or "do not merge" label exists
on:
pull_request_target:
types: [synchronize, opened, reopened, labeled, unlabeled]
jobs:
freeze_warning:
if: ${{ contains(github.event.*.labels.*.name, 'freeze') || contains(github.event.*.labels.*.name, 'do not merge') }}
name: Warn before merging if a "freeze" or "do not merge" label exists
runs-on: ubuntu-latest
steps:
- name: Check for "freeze" label
run: |
echo "Pull request is labeled as 'freeze' or 'do not merge'"
echo "This workflow fails so that the pull request cannot be merged."
exit 1

View file

@ -0,0 +1,22 @@
name: Mergifyio backport
on: [issue_comment]
jobs:
mergifyio_backport:
if: github.repository == 'frrouting/frr'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-ecosystem/action-regex-match@v2
id: regex-match
with:
text: ${{ github.event.comment.body }}
regex: '[Mm]ergifyio backport '
- uses: actions-ecosystem/action-add-labels@v1
if: ${{ steps.regex-match.outputs.match != '' }}
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
labels: backport

26
.github/workflows/size-label.yml vendored Normal file
View file

@ -0,0 +1,26 @@
name: Add PRs size label
on: pull_request_target
jobs:
size-label:
if: github.repository == 'frrouting/frr'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: size-label
uses: "pascalgn/size-label-action@v0.4.2"
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
with:
sizes: >
{
"0": "XS",
"20": "S",
"50": "M",
"200": "L",
"800": "XL",
"2000": "XXL"
}

29
.github/workflows/stale.yml vendored Normal file
View file

@ -0,0 +1,29 @@
name: Mark stale issues
on:
workflow_dispatch:
schedule:
- cron: "30 1 * * *"
permissions:
contents: read
jobs:
stale:
if: github.repository == 'frrouting/frr'
permissions:
issues: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue is stale because it has been open 180 days with no activity. Comment or remove the `autoclose` label in order to avoid having this issue closed.'
stale-issue-label: autoclose
stale-pr-message: 'This PR is stale because it has been open 180 days with no activity. Comment or remove the `autoclose` label in order to avoid having this PR closed.'
stale-pr-label: autoclose
days-before-stale: 180
days-before-close: 194
days-before-pr-stale: 180
days-before-pr-close: 194

121
.gitignore vendored Normal file
View file

@ -0,0 +1,121 @@
### autoconf/automake root stuff
/compile
/config.log
/config.h
/config.cache
/config.status
/config.guess
/config.sub
/config.version
/ltmain.sh
/stamp-h
/stamp-h[0-9]*
*-stamp
/INSTALL
/depcomp
/missing
/install-sh
/mkinstalldirs
/ylwrap
/autom4te*.cache
/configure.lineno
/configure
/config.h.in
/confdefs.h
/conftest
/conftest.err
/aclocal.m4
/libtool
/libtool.orig
/test-driver
/test-suite.log
/Makefile
/Makefile.in
/symalyzer_report.html
/jquery-3.4.1.min.js
/jquery-3.4.1.min.js.tmp
### autoconf/automake subdir stuff
.deps
.libs
### build outputs
*.o
*.lo
*.a
*.la
*.so
*.loT
*.pb.h
*.pb-c.h
*.pb-c.c
*.pb.cc
*_clippy.c
*.bc
*.ll
*.cg.json
*.cg.dot
*.cg.svg
*.xref
*_tsexpand.h
### gcov outputs
*.gcno
*.gcov
*.gcda
### dist
*.tar.?z
*.tar.?z.asc
*.tar.asc
*.deb
*.ddeb
*.dsc
*.changes
### other garbage
.nfs*
.arch-inventory
.arch-ids
{arch}
build
.cache
.dir-locals.el
.msg
.rebase-*
*~
*.bak
*.swp
*.pyc
*.dmp
__pycache__
*.patch
*.diff
cscope.*
TAGS
tags
GTAGS
GSYMS
GRTAGS
GPATH
compile_commands.json
.ccls
.ccls-cache
.dirstamp
refix
.vscode
.kitchen
.emacs.desktop*
/test-suite.log
pceplib/test/*.log
pceplib/test/*.trs
/tests/topotests/lib/mgmt_pb2.py

2
.isort.cfg Normal file
View file

@ -0,0 +1,2 @@
[settings]
profile = black

9
.pylintrc Normal file
View file

@ -0,0 +1,9 @@
[MASTER]
init-hook="import sys; sys.path.insert(0, '..')"
signature-mutators=common_config.retry,retry
[FORMAT]
max-line-length = 88
[MESSAGES CONTROL]
disable=I,C,R,W

40
.travis.yml Normal file
View file

@ -0,0 +1,40 @@
dist: focal
os: linux
language: c
services:
- docker
jobs:
include:
- script:
- docker/centos-7/build.sh
- docker images
name: centos7
- script:
- docker/centos-8/build.sh
- docker images
name: centos8
- script:
- sudo apt install -y linux-modules-extra-$(uname -r)
- docker build -t frr-ubuntu18:latest -f docker/ubuntu18-ci/Dockerfile .
- docker images
- uname -a
- docker run -d --privileged --name frr-ubuntu18 --mount type=bind,source=/lib/modules,target=/lib/modules frr-ubuntu18:latest
- docker ps
- docker exec frr-ubuntu18 bash -c 'cd ~/frr ; make check'
- docker exec frr-ubuntu18 bash -c 'ps agxu ; lsmod | grep mpls || true'
- docker exec frr-ubuntu18 bash -c 'cd ~/frr/tests/topotests/ospf_topo1 ; sudo pytest test_ospf_topo1.py'
- docker exec frr-ubuntu18 bash -c 'cd ~/frr/tests/topotests/bgp_l3vpn_to_bgp_vrf ; sudo pytest test_bgp_l3vpn_to_bgp_vrf.py'
name: ubuntu18+minimalCI
- script:
- sudo apt install -y linux-modules-extra-$(uname -r)
- docker build -t frr-ubuntu20:latest -f docker/ubuntu20-ci/Dockerfile .
- docker images
- uname -a
- docker run -d --privileged --name frr-ubuntu20 --mount type=bind,source=/lib/modules,target=/lib/modules frr-ubuntu20:latest
- docker ps
- docker exec frr-ubuntu20 bash -c 'cd ~/frr ; make check'
- docker exec frr-ubuntu20 bash -c 'ps agxu ; lsmod | grep mpls || true'
- docker exec frr-ubuntu20 bash -c 'cd ~/frr/tests/topotests/ospf_topo1 ; sudo pytest test_ospf_topo1.py'
- docker exec frr-ubuntu20 bash -c 'cd ~/frr/tests/topotests/bgp_l3vpn_to_bgp_vrf ; sudo pytest test_bgp_l3vpn_to_bgp_vrf.py'
name: ubuntu20+minimalCI

16
COPYING Normal file
View file

@ -0,0 +1,16 @@
The FRRouting project consists of parts with various licenses. Any particular
file's license should be indicated at the top of the file with an SPDX License
identifier.
The full text of all licenses used can be found in doc/licenses.
The composite work (binary) resulting from compiling FRR is thought to always
be distributable under GPLv2 or later. However, please note that this is
simply an expression of the community's best-effort understanding, it is not a
legal statement, guarantee, or advice of any kind. If necessary, please
familiarize yourself with the specifics and/or consult a lawyer.
Also please be advised that FRR's documentation is, for historical reasons,
licensed under a custom (but relatively permissive) license. This license
dates back to the GNU Zebra project and cannot easily be changed into something
more common, short of rewriting the entire documentation.

363
Makefile.am Normal file
View file

@ -0,0 +1,363 @@
## Process this file with automake to produce Makefile.in.
AUTOMAKE_OPTIONS = subdir-objects 1.12
ACLOCAL_AMFLAGS = -I m4 -Wall,no-override
AM_CFLAGS = \
$(AC_CFLAGS) \
$(LIBYANG_CFLAGS) \
$(SQLITE3_CFLAGS) \
$(UNWIND_CFLAGS) \
$(SAN_FLAGS) \
$(WERROR) \
# end
AM_CXXFLAGS = \
$(AC_CXXFLAGS) \
$(LIBYANG_CFLAGS) \
$(WERROR) \
# end
# CPPFLAGS_BASE does not contain the include path for overriding assert.h,
# therefore should be used in tools that do *not* link libfrr or do not want
# assert() overridden
CPPFLAGS_BASE = \
-I$(top_srcdir) -I$(top_srcdir)/include -I$(top_srcdir)/lib \
-I$(top_builddir) \
$(LUA_INCLUDE) \
# end
AM_CPPFLAGS = \
-I$(top_srcdir)/lib/assert \
$(CPPFLAGS_BASE) \
# end
# AM_LDFLAGS is used for executables (daemons). LDFLAGS can be left alone,
# but if it is changed it should include $(AM_LDFLAGS)
AM_LDFLAGS = \
-export-dynamic \
$(AC_LDFLAGS) \
$(AC_LDFLAGS_EXEC) \
$(SAN_FLAGS) \
# end
# libraries need to use libxxx_LDFLAGS = $(LIB_LDFLAGS) -version-info X:Y:Z
LIB_LDFLAGS = \
-export-dynamic \
$(AC_LDFLAGS) \
$(SAN_FLAGS) \
# end
# modules need to use xxx_LDFLAGS = $(MODULE_LDFLAGS)
MODULE_LDFLAGS = \
-export-dynamic \
-avoid-version \
-module \
-shared \
$(AC_LDFLAGS) \
$(SAN_FLAGS) \
# end
DEFS = @DEFS@ -DCONFDATE=$(CONFDATE)
AR_FLAGS = @AR_FLAGS@
ARFLAGS = @ARFLAGS@
RANLIB = @RANLIB@
# these two targets are provided to easily grab autoconf/Makefile variables
# you can use either:
# eval `make VARFD=3 shvar-CFLAGS 3>&1 1>&2`
# CFLAGS="`make VARFD=3 var-CFLAGS 3>&1 1>&2`"
# where the former can be used to set several variables at once. Note the
# fd redirections -- this is to prevent garbage from make rebuilding other
# targets from causing issues.
.PHONY: shvar-% var-%
VARFD ?= 1
shvar-%:
@echo "$*=\"$($*)\"" >&$(VARFD)
var-%:
@echo "$($*)" >&$(VARFD)
if ONLY_CLIPPY
.DEFAULT_GOAL := clippy-only
endif
clippy-only: Makefile lib/clippy config.h
.PHONY: clippy-only
# overwriting these vars breaks cross-compilation. let's be helpful and warn.
#
# note: "#AUTODERP# " will be removed from Makefile by configure. These are
# GNU make directives & automake will f*ck them up by trying to process them
# as automake directives.
#
#AUTODERP# null=
#AUTODERP# SPACE=$(null) $(null)
#AUTODERP# mkcheck_CC = $(findstring $(SPACE)CC=, $(SPACE)$(MAKEOVERRIDES))
#AUTODERP# mkcheck_CFLAGS = $(findstring $(SPACE)CFLAGS=, $(SPACE)$(MAKEOVERRIDES))
#AUTODERP# mkcheck_CPPFLAGS = $(findstring $(SPACE)CPPFLAGS=,$(SPACE)$(MAKEOVERRIDES))
#AUTODERP# mkcheck_CCLD = $(findstring $(SPACE)CCLD=, $(SPACE)$(MAKEOVERRIDES))
#AUTODERP# mkcheck_LD = $(findstring $(SPACE)LD=, $(SPACE)$(MAKEOVERRIDES))
#AUTODERP# mkcheck_LDFLAGS = $(findstring $(SPACE)LDFLAGS=, $(SPACE)$(MAKEOVERRIDES))
#AUTODERP# #
#AUTODERP# ifneq ($(mkcheck_CC),)
#AUTODERP# $(warning WARNING: you have overwritten the "CC" variable on the make command line.)
#AUTODERP# endif
#AUTODERP# ifneq ($(mkcheck_CFLAGS),)
#AUTODERP# $(warning WARNING: you have overwritten the "CFLAGS" variable on the make command line.)
#AUTODERP# endif
#AUTODERP# ifneq ($(mkcheck_CPPFLAGS),)
#AUTODERP# $(warning WARNING: you have overwritten the "CPPFLAGS" variable on the make command line.)
#AUTODERP# endif
#AUTODERP# ifneq ($(mkcheck_CCLD),)
#AUTODERP# $(warning WARNING: you have overwritten the "CCLD" variable on the make command line.)
#AUTODERP# endif
#AUTODERP# ifneq ($(mkcheck_LD),)
#AUTODERP# $(warning WARNING: you have overwritten the "LD" variable on the make command line.)
#AUTODERP# endif
#AUTODERP# ifneq ($(mkcheck_LDFLAGS),)
#AUTODERP# $(warning WARNING: you have overwritten the "LDFLAGS" variable on the make command line.)
#AUTODERP# endif
#AUTODERP# #
#AUTODERP# ifneq ($(mkcheck_CC)$(mkcheck_CFLAGS)$(mkcheck_CPPFLAGS)$(mkcheck_CCLD)$(mkcheck_LD)$(mkcheck_LDFLAGS),)
#AUTODERP# $(warning ------)
#AUTODERP# $(warning While overwriting these variables works most of the time, it is not recommended and can cause confusing build errors.)
#AUTODERP# $(warning This is especially problematic when cross-compiling, since tools that run on the build system during the build process will not be compiled correctly.)
#AUTODERP# $(warning All of these variables should be supplied to 'configure', and they will be remembered and correctly applied during 'make'.)
#AUTODERP# $(warning ------)
#AUTODERP# endif
EXTRA_DIST =
EXTRA_PROGRAMS =
BUILT_SOURCES =
CLEANFILES =
DISTCLEANFILES =
SUFFIXES =
bin_PROGRAMS =
sbin_PROGRAMS =
sbin_SCRIPTS =
noinst_PROGRAMS =
noinst_HEADERS =
noinst_LIBRARIES =
nodist_noinst_DATA =
lib_LTLIBRARIES =
module_LTLIBRARIES =
pkginclude_HEADERS =
nodist_pkginclude_HEADERS =
dist_yangmodels_DATA =
man_MANS =
vtysh_daemons =
clippy_scan =
## libtool, the self-made GNU scourge
## ... this should fix relinking
## ... and AUTOMAKE_DUMMY is needed to prevent automake from treating this
## as overriding the normal targets...
$(AUTOMAKE_DUMMY)install-moduleLTLIBRARIES: install-libLTLIBRARIES
$(AUTOMAKE_DUMMY)install-binPROGRAMS: install-libLTLIBRARIES
$(AUTOMAKE_DUMMY)install-sbinPROGRAMS: install-libLTLIBRARIES
# Include default rules to compile protobuf message sources
SUFFIXES += .proto .pb-c.c .pb-c.h
# Rules
AM_V_PROTOC_C = $(am__v_PROTOC_C_$(V))
am__v_PROTOC_C_ = $(am__v_PROTOC_C_$(AM_DEFAULT_VERBOSITY))
am__v_PROTOC_C_0 = @echo " PROTOC_C" $@;
am__v_PROTOC_C_1 =
%.pb-c.c %.pb-c.h : %.proto
$(AM_V_PROTOC_C)$(PROTOC_C) -I$(top_srcdir) --c_out=$(top_builddir) $^
$(AM_V_GEN)$(SED) -i -e '1i\
#include "config.h"' $@
include doc/subdir.am
include doc/user/subdir.am
include doc/manpages/subdir.am
include doc/developer/subdir.am
include include/subdir.am
include lib/subdir.am
include mlag/subdir.am
include zebra/subdir.am
include watchfrr/subdir.am
include qpb/subdir.am
include fpm/subdir.am
include grpc/subdir.am
include tools/subdir.am
include mgmtd/subdir.am
include bgpd/subdir.am
include bgpd/rfp-example/librfp/subdir.am
include bgpd/rfp-example/rfptest/subdir.am
include ripd/subdir.am
include ripngd/subdir.am
include ospfd/subdir.am
include ospf6d/subdir.am
include ospfclient/subdir.am
include isisd/subdir.am
include nhrpd/subdir.am
include ldpd/subdir.am
include babeld/subdir.am
include eigrpd/subdir.am
include sharpd/subdir.am
include pimd/subdir.am
include pbrd/subdir.am
include staticd/subdir.am
include bfdd/subdir.am
include yang/subdir.am
include yang/libyang_plugins/subdir.am
include vrrpd/subdir.am
include pceplib/subdir.am
include pceplib/test/subdir.am
include pathd/subdir.am
include vtysh/subdir.am
include tests/subdir.am
include tests/topotests/subdir.am
if PKGSRC
rcdir=@pkgsrcrcdir@
rc_SCRIPTS = \
pkgsrc/bgpd.sh \
pkgsrc/ospf6d.sh \
pkgsrc/ospfd.sh \
pkgsrc/ripd.sh \
pkgsrc/ripngd.sh \
pkgsrc/zebra.sh \
pkgsrc/mgmtd.sh \
# end
endif
EXTRA_DIST += \
aclocal.m4 \
README.md \
m4/README.txt \
m4/libtool-whole-archive.patch \
config.version \
\
python/clidef.py \
python/clippy/__init__.py \
python/clippy/elf.py \
python/clippy/uidhash.py \
python/makevars.py \
python/makefile.py \
python/tiabwarfo.py \
python/xrelfo.py \
python/xref2vtysh.py \
python/test_xrelfo.py \
python/runtests.py \
\
python/xrefstructs.json \
\
tools/etc/logrotate.d/frr \
redhat/frr.pam \
redhat/frr.spec \
\
snapcraft/snapcraft.yaml \
snapcraft/README.snap_build.md \
snapcraft/README.usage.md \
snapcraft/extra_version_info.txt \
snapcraft/scripts \
snapcraft/defaults \
snapcraft/helpers \
snapcraft/snap \
babeld/Makefile \
mgmtd/Makefile \
bgpd/Makefile \
bgpd/rfp-example/librfp/Makefile \
bgpd/rfp-example/rfptest/Makefile \
doc/Makefile \
doc/developer/Makefile \
doc/manpages/Makefile \
doc/user/Makefile \
eigrpd/Makefile \
fpm/Makefile \
grpc/Makefile \
isisd/Makefile \
ldpd/Makefile \
lib/Makefile \
nhrpd/Makefile \
ospf6d/Makefile \
ospfclient/Makefile \
ospfd/Makefile \
pbrd/Makefile \
pimd/Makefile \
qpb/Makefile \
ripd/Makefile \
ripngd/Makefile \
staticd/Makefile \
tests/Makefile \
tools/Makefile \
vtysh/Makefile \
watchfrr/Makefile \
zebra/Makefile \
vrrpd/Makefile \
# end
AM_V_LLVM_BC = $(am__v_LLVM_BC_$(V))
am__v_LLVM_BC_ = $(am__v_LLVM_BC_$(AM_DEFAULT_VERBOSITY))
am__v_LLVM_BC_0 = @echo " LLVM.BC " $@;
am__v_LLVM_BC_1 =
AM_V_LLVM_LD = $(am__v_LLVM_LD_$(V))
am__v_LLVM_LD_ = $(am__v_LLVM_LD_$(AM_DEFAULT_VERBOSITY))
am__v_LLVM_LD_0 = @echo " LLVM.LD " $@;
am__v_LLVM_LD_1 =
SUFFIXES += .lo.bc .o.bc
.o.o.bc:
$(AM_V_LLVM_BC)$(COMPILE) -emit-llvm -c -o $@ $(patsubst %.o,%.c,$<)
.lo.lo.bc:
$(AM_V_LLVM_BC)$(COMPILE) -emit-llvm -c -o $@ $(patsubst %.lo,%.c,$<)
%.cg.json: %.bc tools/frr-llvm-cg
tools/frr-llvm-cg -o $@ $<
%.cg.dot: %.cg.json
$(PYTHON) $(top_srcdir)/python/callgraph-dot.py $< $@
%.cg.svg: %.cg.dot
@echo if the following command fails, you need to install graphviz.
@echo also, the output is nondeterministic. run it multiple times and use the nicest output.
@echo tuning parameters may yield nicer looking graphs as well.
fdp -GK=0.7 -Gstart=42231337 -Gmaxiter=2000 -Elen=2 -Gnodesep=1.5 -Tsvg -o$@ $<
# don't delete intermediaries
.PRECIOUS: %.cg.json %.cg.dot
# <lib>.la.bc, <lib>.a.bc and <daemon>.bc targets are generated by
# python/makefile.py
LLVM_LINK = llvm-link-$(llvm_version)
clean-local: clean-python clean-llvm-bitcode
.PHONY: clean-python clean-llvm-bitcode
clean-python:
find . -name __pycache__ -o -name .pytest_cache | xargs rm -rf
find . -name "*.pyc" -o -name "*_clippy.c" | xargs rm -f
clean-llvm-bitcode:
find . -name "*.bc" -o -name "*.cg.json" -o -name "*.cg.dot" -o -name "*.cg.svg" | xargs rm -f
redistclean:
$(MAKE) distclean CONFIG_CLEAN_FILES="$(filter-out $(EXTRA_DIST), $(CONFIG_CLEAN_FILES))"
indent:
tools/indent.py `find sharpd bgpd mgmtd eigrpd include isisd lib nhrpd ospf6d ospfd pimd qpb ripd vtysh zebra -name '*.[ch]' | grep -v include/linux`
if HAVE_GCOV
coverage: check
@ find . -name '*.o' -exec gcov {} \;
yorn:
@ echo "OK to upload coverage to https://coverage.io [y/N]:"
@ read yn; test "$$yn" = "y"
upload-check-coverage:
@ if [ "x${COMMIT}" = "x" ]; then echo "COMMIT required"; exit 1; fi
@ if [ "x${TOKEN}" = "x" ]; then echo "TOKEN required"; exit 1; fi
curl -s https://codecov.io/bash | bash -s - -C ${COMMIT} -t ${TOKEN}
force-check-coverage: coverage upload-check-coverage
check-coverage: coverage yorn upload-check-coverage
endif

83
README.md Normal file
View file

@ -0,0 +1,83 @@
<p align="center">
<img src="http://docs.frrouting.org/en/latest/_static/frr-icon.svg" alt="Icon" width="20%"/>
</p>
FRRouting
=========
FRR is free software that implements and manages various IPv4 and IPv6 routing
protocols. It runs on nearly all distributions of Linux and BSD and
supports all modern CPU architectures.
FRR currently supports the following protocols:
* BGP
* OSPFv2
* OSPFv3
* RIPv1
* RIPv2
* RIPng
* IS-IS
* PIM-SM/MSDP
* LDP
* BFD
* Babel
* PBR
* OpenFabric
* VRRP
* EIGRP (alpha)
* NHRP (alpha)
Installation & Use
------------------
For source tarballs, see the
[releases page](https://github.com/FRRouting/frr/releases).
For Debian and its derivatives, use the APT repository at
[https://deb.frrouting.org/](https://deb.frrouting.org/).
Instructions on building and installing from source for supported platforms may
be found in the
[developer docs](http://docs.frrouting.org/projects/dev-guide/en/latest/building.html).
Once installed, please refer to the [user guide](http://docs.frrouting.org/)
for instructions on use.
Community
---------
The FRRouting email list server is located
[here](https://lists.frrouting.org/listinfo) and offers the following public
lists:
| Topic | List |
|-------------------|------------------------------|
| Development | dev@lists.frrouting.org |
| Users & Operators | frog@lists.frrouting.org |
| Announcements | announce@lists.frrouting.org |
For chat, we currently use [Slack](https://frrouting.slack.com). You can join
by clicking the "Slack" link under the
[Participate](https://frrouting.org/community) section of our website.
Contributing
------------
FRR maintains [developer's documentation](http://docs.frrouting.org/projects/dev-guide/en/latest/index.html)
which contains the [project workflow](http://docs.frrouting.org/projects/dev-guide/en/latest/workflow.html)
and expectations for contributors. Some technical documentation on project
internals is also available.
We welcome and appreciate all contributions, no matter how small!
Security
--------
To report security issues, please use our security mailing list:
```
security [at] lists.frrouting.org
```

1
alpine/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/APKBUILD

70
alpine/APKBUILD.in Normal file
View file

@ -0,0 +1,70 @@
# Maintainer: Arthur Jones <arthur.jones@riverbed.com>
pkgname=frr
arch="all"
pkgver=@VERSION@
pkgrel=0
pkgdesc="FRRouting is a fork of quagga"
url="https://frrouting.org/"
license="GPL-2.0"
depends="json-c c-ares iproute2 python3 bash"
makedepends="ncurses-dev net-snmp-dev gawk texinfo perl
acct autoconf automake bash binutils bison bsd-compat-headers build-base
c-ares c-ares-dev ca-certificates cryptsetup-libs curl device-mapper-libs
expat fakeroot flex fortify-headers gdbm git gmp json-c-dev kmod
lddtree libacl libatomic libattr libblkid libburn libbz2 libc-dev
libcap-dev libcurl libedit libffi libgcc libgomp libisoburn libisofs
libltdl openssl libssh2 libstdc++ libtool libuuid
linux-headers lzip lzo m4 make mkinitfs mpc1 mpfr4 mtools musl-dev
ncurses-libs ncurses-terminfo ncurses-terminfo-base patch pax-utils pcre2
perl pkgconf python3 python3-dev readline readline-dev sqlite-libs pcre2-dev
squashfs-tools sudo tar texinfo xorriso xz-libs py-pip rtrlib rtrlib-dev
py3-sphinx elfutils elfutils-dev protobuf-c-compiler protobuf-c-dev
lua5.3-dev lua5.3 gzip"
checkdepends="pytest py-setuptools"
install="$pkgname.pre-install $pkgname.pre-deinstall $pkgname.post-deinstall"
subpackages="$pkgname-dev $pkgname-doc $pkgname-dbg"
source="$pkgname-$pkgver.tar.gz"
builddir="$srcdir"/$pkgname-$pkgver
_sysconfdir=/etc
_sbindir=/usr/lib/frr
_libdir=/usr/lib
_user=frr
build() {
cd "$builddir"
./configure \
--prefix=/usr \
--sysconfdir=$_sysconfdir \
--localstatedir=/var \
--sbindir=$_sbindir \
--libdir=$_libdir \
--enable-rpki \
--enable-vtysh \
--enable-multipath=64 \
--enable-vty-group=frrvty \
--enable-user=$_user \
--enable-group=$_user \
--enable-pcre2posix \
--enable-scripting
make -j $(nproc)
}
check() {
cd "$builddir"
make -j 1 check
}
package() {
cd "$builddir"
make DESTDIR="$pkgdir" install
install -d $pkgdir/$_sysconfdir/frr
install -m 0644 tools/etc/frr/daemons $pkgdir/$_sysconfdir/frr/daemons
install -d $pkgdir/$_sysconfdir/init.d
ln -s ${_sbindir}/frr $pkgdir/$_sysconfdir/init.d/frr
}

6
alpine/frr.post-deinstall Executable file
View file

@ -0,0 +1,6 @@
#!/bin/sh
getent passwd frr > /dev/null && deluser frr
getent group frrvty > /dev/null && delgroup frrvty
getent group frr > /dev/null && delgroup frr
exit 0

4
alpine/frr.pre-deinstall Executable file
View file

@ -0,0 +1,4 @@
#!/bin/sh
/etc/init.d/frr stop
exit 0

10
alpine/frr.pre-install Executable file
View file

@ -0,0 +1,10 @@
#!/bin/sh
for g in frr frrvty; do
! getent group $g > /dev/null && addgroup -S $g
done
! getent passwd frr > /dev/null && \
adduser -S -D -h /var/run/frr -s /sbin/nologin -G frr -g frr frr
adduser frr frrvty

8
babeld/.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
*
!*.c
!*.h
!LICENCE
!Makefile
!subdir.am
!.gitignore
*_clippy.c

10
babeld/Makefile Normal file
View file

@ -0,0 +1,10 @@
all: ALWAYS
@$(MAKE) -s -C .. babeld/babeld
%: ALWAYS
@$(MAKE) -s -C .. babeld/$@
Makefile:
#nothing
ALWAYS:
.PHONY: ALWAYS makefiles
.SUFFIXES:

48
babeld/babel_errors.c Normal file
View file

@ -0,0 +1,48 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Babel-specific error messages.
* Copyright (C) 2018 Cumulus Networks, Inc.
* Donald Sharp
*/
#include <zebra.h>
#include "lib/ferr.h"
#include "babel_errors.h"
/* clang-format off */
static struct log_ref ferr_babel_err[] = {
{
.code = EC_BABEL_MEMORY,
.title = "BABEL Memory Errors",
.description = "Babel has failed to allocate memory, the system is about to run out of memory",
.suggestion = "Find the process that is causing memory shortages, remediate that process and restart FRR"
},
{
.code = EC_BABEL_PACKET,
.title = "BABEL Packet Error",
.description = "Babel has detected a packet encode/decode problem",
.suggestion = "Collect relevant log files and file an Issue"
},
{
.code = EC_BABEL_CONFIG,
.title = "BABEL Configuration Error",
.description = "Babel has detected a configuration error of some sort",
.suggestion = "Ensure that the configuration is correct"
},
{
.code = EC_BABEL_ROUTE,
.title = "BABEL Route Error",
.description = "Babel has detected a routing error and has an inconsistent state",
.suggestion = "Gather data for filing an Issue and then restart FRR"
},
{
.code = END_FERR,
}
};
/* clang-format on */
void babel_error_init(void)
{
log_ref_add(ferr_babel_err);
}

22
babeld/babel_errors.h Normal file
View file

@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Babel-specific error messages.
* Copyright (C) 2018 Cumulus Networks, Inc.
* Donald Sharp
*/
#ifndef __BABEL_ERRORS_H__
#define __BABEL_ERRORS_H__
#include "lib/ferr.h"
enum babel_log_refs {
EC_BABEL_MEMORY = BABEL_FERR_START,
EC_BABEL_PACKET,
EC_BABEL_CONFIG,
EC_BABEL_ROUTE,
};
extern void babel_error_init(void);
#endif

90
babeld/babel_filter.c Normal file
View file

@ -0,0 +1,90 @@
// SPDX-License-Identifier: MIT
/*
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "babel_filter.h"
#include "vty.h"
#include "filter.h"
#include "log.h"
#include "plist.h"
#include "distribute.h"
#include "util.h"
int
babel_filter(int output, const unsigned char *prefix, unsigned short plen,
unsigned int ifindex)
{
struct interface *ifp = if_lookup_by_index(ifindex, VRF_DEFAULT);
babel_interface_nfo *babel_ifp = ifp ? babel_get_if_nfo(ifp) : NULL;
struct prefix p;
struct distribute *dist = NULL;
struct access_list *alist;
struct prefix_list *plist;
int distribute;
struct babel *babel;
afi_t family;
p.family = v4mapped(prefix) ? AF_INET : AF_INET6;
p.prefixlen = v4mapped(prefix) ? plen - 96 : plen;
if (p.family == AF_INET) {
uchar_to_inaddr(&p.u.prefix4, prefix);
distribute = output ? DISTRIBUTE_V4_OUT : DISTRIBUTE_V4_IN;
family = AFI_IP;
} else {
uchar_to_in6addr(&p.u.prefix6, prefix);
distribute = output ? DISTRIBUTE_V6_OUT : DISTRIBUTE_V6_IN;
family = AFI_IP6;
}
if (babel_ifp != NULL && babel_ifp->list[distribute]) {
if (access_list_apply (babel_ifp->list[distribute], &p)
== FILTER_DENY) {
debugf(BABEL_DEBUG_FILTER,
"%pFX filtered by distribute %s",
&p, output ? "out" : "in");
return INFINITY;
}
}
if (babel_ifp != NULL && babel_ifp->prefix[distribute]) {
if (prefix_list_apply (babel_ifp->prefix[distribute], &p)
== PREFIX_DENY) {
debugf(BABEL_DEBUG_FILTER, "%pFX filtered by distribute %s",
&p, output ? "out" : "in");
return INFINITY;
}
}
/* All interface filter check. */
babel = babel_lookup();
if (babel)
dist = distribute_lookup (babel->distribute_ctx, NULL);
if (dist) {
if (dist->list[distribute]) {
alist = access_list_lookup (family, dist->list[distribute]);
if (alist) {
if (access_list_apply (alist, &p) == FILTER_DENY) {
debugf(BABEL_DEBUG_FILTER,"%pFX filtered by distribute %s",
&p, output ? "out" : "in");
return INFINITY;
}
}
}
if (dist->prefix[distribute]) {
plist = prefix_list_lookup (family, dist->prefix[distribute]);
if (plist) {
if (prefix_list_apply (plist, &p) == PREFIX_DENY) {
debugf(BABEL_DEBUG_FILTER,"%pFX filtered by distribute %s",
&p, output ? "out" : "in");
return INFINITY;
}
}
}
}
return 0;
}

16
babeld/babel_filter.h Normal file
View file

@ -0,0 +1,16 @@
// SPDX-License-Identifier: MIT
/*
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
*/
#ifndef BABELD_BABEL_FILTER_H
#define BABELD_BABEL_FILTER_H
#include <zebra.h>
#include "prefix.h"
#include "babel_interface.h"
int babel_filter(int output, const unsigned char *prefix, unsigned short plen,
unsigned int index);
#endif /* BABELD_BABEL_FILTER_H */

1357
babeld/babel_interface.c Normal file

File diff suppressed because it is too large Load diff

120
babeld/babel_interface.h Normal file
View file

@ -0,0 +1,120 @@
// SPDX-License-Identifier: MIT
/*
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
*/
#ifndef BABEL_INTERFACE_H
#define BABEL_INTERFACE_H
#include <zebra.h>
#include "zclient.h"
#include "vty.h"
#include "distribute.h"
#define CONFIG_DEFAULT 0
#define CONFIG_NO 1
#define CONFIG_YES 2
/* babeld interface information */
struct babel_interface {
unsigned short flags; /* see below */
unsigned short cost;
int channel;
struct timeval hello_timeout;
struct timeval update_timeout;
struct timeval flush_timeout;
struct timeval update_flush_timeout;
unsigned char *ipv4;
int buffered;
int bufsize;
/* Relative position of the Hello message in the send buffer, or
(-1) if there is none. */
int buffered_hello;
char have_buffered_id;
char have_buffered_nh;
char have_buffered_prefix;
unsigned char buffered_id[8];
unsigned char buffered_nh[4];
unsigned char buffered_prefix[16];
unsigned char *sendbuf;
struct buffered_update *buffered_updates;
int num_buffered_updates;
int update_bufsize;
time_t bucket_time;
unsigned int bucket;
time_t last_update_time;
unsigned short hello_seqno;
unsigned hello_interval;
unsigned update_interval;
/* A higher value means we forget old RTT samples faster. Must be
between 1 and 256, inclusive. */
unsigned int rtt_decay;
/* Parameters for computing the cost associated to RTT. */
unsigned int rtt_min;
unsigned int rtt_max;
unsigned int max_rtt_penalty;
/* For filter type slot. */
struct access_list *list[DISTRIBUTE_MAX]; /* Access-list. */
struct prefix_list *prefix[DISTRIBUTE_MAX]; /* Prefix-list. */
};
typedef struct babel_interface babel_interface_nfo;
static inline babel_interface_nfo* babel_get_if_nfo(struct interface *ifp)
{
return ((babel_interface_nfo*) ifp->info);
}
/* babel_interface_nfo flags */
#define BABEL_IF_IS_UP (1 << 0)
#define BABEL_IF_WIRED (1 << 1)
#define BABEL_IF_SPLIT_HORIZON (1 << 2)
#define BABEL_IF_LQ (1 << 3)
#define BABEL_IF_FARAWAY (1 << 4)
#define BABEL_IF_TIMESTAMPS (1 << 5)
/* Only INTERFERING can appear on the wire. */
#define BABEL_IF_CHANNEL_UNKNOWN 0
#define BABEL_IF_CHANNEL_INTERFERING 255
#define BABEL_IF_CHANNEL_NONINTERFERING -2
static inline int
if_up(struct interface *ifp)
{
return (if_is_operative(ifp) &&
CHECK_FLAG(babel_get_if_nfo(ifp)->flags, BABEL_IF_IS_UP));
}
struct buffered_update {
unsigned char id[8];
unsigned char prefix[16];
unsigned char plen;
unsigned char pad[3];
};
/* init function */
void babel_if_init(void);
/* Callback functions for zebra client */
int babel_interface_up (int, struct zclient *, zebra_size_t, vrf_id_t);
int babel_interface_down (int, struct zclient *, zebra_size_t, vrf_id_t);
int babel_interface_add (int, struct zclient *, zebra_size_t, vrf_id_t);
int babel_interface_delete (int, struct zclient *, zebra_size_t, vrf_id_t);
int babel_interface_address_add (int, struct zclient *, zebra_size_t, vrf_id_t);
int babel_interface_address_delete (int, struct zclient *, zebra_size_t, vrf_id_t);
int babel_ifp_create(struct interface *ifp);
int babel_ifp_up(struct interface *ifp);
int babel_ifp_down(struct interface *ifp);
int babel_ifp_destroy(struct interface *ifp);
unsigned jitter(babel_interface_nfo *, int);
unsigned update_jitter(babel_interface_nfo *babel_ifp, int urgent);
/* return "true" if "address" is one of our ipv6 addresses */
int is_interface_ll_address(struct interface *ifp, const unsigned char *address);
/* Send retraction to all, and reset all interfaces statistics. */
void babel_interface_close_all(void);
extern int babel_enable_if_config_write (struct vty *);
#endif

377
babeld/babel_main.c Normal file
View file

@ -0,0 +1,377 @@
// SPDX-License-Identifier: MIT
/*
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
*/
/* include zebra library */
#include <zebra.h>
#include <fcntl.h>
#include "getopt.h"
#include "if.h"
#include "log.h"
#include "frrevent.h"
#include "privs.h"
#include "sigevent.h"
#include "lib/version.h"
#include "command.h"
#include "vty.h"
#include "memory.h"
#include "libfrr.h"
#include "lib_errors.h"
#include "babel_main.h"
#include "babeld.h"
#include "util.h"
#include "kernel.h"
#include "babel_interface.h"
#include "neighbour.h"
#include "route.h"
#include "xroute.h"
#include "message.h"
#include "resend.h"
#include "babel_zebra.h"
#include "babel_errors.h"
static void babel_fail(void);
static void babel_init_random(void);
static void babel_exit_properly(void);
static void babel_save_state_file(void);
struct event_loop *master; /* quagga's threads handler */
struct timeval babel_now; /* current time */
unsigned char myid[8]; /* unique id (mac address of an interface) */
int debug = 0;
int resend_delay = -1;
const unsigned char zeroes[16] = {0};
const unsigned char ones[16] =
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
static char state_file[1024];
unsigned char protocol_group[16]; /* babel's link-local multicast address */
int protocol_port; /* babel's port */
int protocol_socket = -1; /* socket: communicate with others babeld */
static char *babel_vty_addr = NULL;
static int babel_vty_port = BABEL_VTY_PORT;
/* babeld privileges */
static zebra_capabilities_t _caps_p [] =
{
ZCAP_NET_RAW,
ZCAP_BIND
};
struct zebra_privs_t babeld_privs =
{
#if defined(FRR_USER)
.user = FRR_USER,
#endif
#if defined FRR_GROUP
.group = FRR_GROUP,
#endif
#ifdef VTY_GROUP
.vty_group = VTY_GROUP,
#endif
.caps_p = _caps_p,
.cap_num_p = array_size(_caps_p),
.cap_num_i = 0
};
static void
babel_sigexit(void)
{
zlog_notice("Terminating on signal");
babel_exit_properly();
}
static void
babel_sigusr1 (void)
{
zlog_rotate ();
}
static struct frr_signal_t babel_signals[] =
{
{
.signal = SIGUSR1,
.handler = &babel_sigusr1,
},
{
.signal = SIGINT,
.handler = &babel_sigexit,
},
{
.signal = SIGTERM,
.handler = &babel_sigexit,
},
};
struct option longopts[] =
{
{ 0 }
};
static const struct frr_yang_module_info *const babeld_yang_modules[] = {
&frr_filter_info,
&frr_interface_info,
&frr_vrf_info,
};
/* clang-format off */
FRR_DAEMON_INFO(babeld, BABELD,
.vty_port = BABEL_VTY_PORT,
.proghelp = "Implementation of the BABEL routing protocol.",
.signals = babel_signals,
.n_signals = array_size(babel_signals),
.privs = &babeld_privs,
.yang_modules = babeld_yang_modules,
.n_yang_modules = array_size(babeld_yang_modules),
);
/* clang-format on */
int
main(int argc, char **argv)
{
int rc;
frr_preinit (&babeld_di, argc, argv);
frr_opt_add ("", longopts, "");
babel_init_random();
/* set the Babel's default link-local multicast address and Babel's port */
parse_address("ff02:0:0:0:0:0:1:6", protocol_group, NULL);
protocol_port = 6696;
/* get options */
while(1) {
int opt;
opt = frr_getopt (argc, argv, NULL);
if (opt == EOF)
break;
switch (opt)
{
case 0:
break;
default:
frr_help_exit(1);
}
}
snprintf(state_file, sizeof(state_file), "%s/%s", frr_runstatedir,
"babel-state");
/* create the threads handler */
master = frr_init ();
/* Library inits. */
babel_error_init();
resend_delay = BABEL_DEFAULT_RESEND_DELAY;
change_smoothing_half_life(BABEL_DEFAULT_SMOOTHING_HALF_LIFE);
/* init some quagga's dependencies, and babeld's commands */
hook_register_prio(if_real, 0, babel_ifp_create);
hook_register_prio(if_up, 0, babel_ifp_up);
hook_register_prio(if_down, 0, babel_ifp_down);
hook_register_prio(if_unreal, 0, babel_ifp_destroy);
babeld_quagga_init();
/* init zebra client's structure and it's commands */
/* this replace kernel_setup && kernel_setup_socket */
babelz_zebra_init ();
/* init buffer */
rc = resize_receive_buffer(1500);
if(rc < 0)
babel_fail();
schedule_neighbours_check(5000, 1);
frr_config_fork();
frr_run(master);
return 0;
}
static void
babel_fail(void)
{
exit(1);
}
/* initialize random value, and set 'babel_now' by the way. */
static void
babel_init_random(void)
{
gettime(&babel_now);
int rc;
unsigned int seed;
rc = read_random_bytes(&seed, sizeof(seed));
if(rc < 0) {
flog_err_sys(EC_LIB_SYSTEM_CALL, "read(random): %s",
safe_strerror(errno));
seed = 42;
}
seed ^= (babel_now.tv_sec ^ babel_now.tv_usec);
srandom(seed);
}
/*
Load the state file: check last babeld's running state, usefull in case of
"/etc/init.d/babeld restart"
*/
void
babel_load_state_file(void)
{
int fd;
int rc;
fd = open(state_file, O_RDONLY);
if(fd < 0 && errno != ENOENT)
flog_err_sys(EC_LIB_SYSTEM_CALL, "open(babel-state: %s)",
safe_strerror(errno));
rc = unlink(state_file);
if(fd >= 0 && rc < 0) {
flog_err_sys(EC_LIB_SYSTEM_CALL, "unlink(babel-state): %s",
safe_strerror(errno));
/* If we couldn't unlink it, it's probably stale. */
goto fini;
}
if(fd >= 0) {
char buf[100];
char buf2[100];
int s;
long t;
rc = read(fd, buf, 99);
if(rc < 0) {
flog_err_sys(EC_LIB_SYSTEM_CALL, "read(babel-state): %s",
safe_strerror(errno));
} else {
buf[rc] = '\0';
rc = sscanf(buf, "%99s %d %ld\n", buf2, &s, &t);
if(rc == 3 && s >= 0 && s <= 0xFFFF) {
unsigned char sid[8];
rc = parse_eui64(buf2, sid);
if(rc < 0) {
flog_err(EC_BABEL_CONFIG, "Couldn't parse babel-state.");
} else {
struct timeval realnow;
debugf(BABEL_DEBUG_COMMON,
"Got %s %d %ld from babel-state.",
format_eui64(sid), s, t);
gettimeofday(&realnow, NULL);
if(memcmp(sid, myid, 8) == 0)
myseqno = seqno_plus(s, 1);
else
flog_err(EC_BABEL_CONFIG,
"ID mismatch in babel-state. id=%s; old=%s",
format_eui64(myid),
format_eui64(sid));
}
} else {
flog_err(EC_BABEL_CONFIG, "Couldn't parse babel-state.");
}
}
goto fini;
}
fini:
if (fd >= 0)
close(fd);
return ;
}
static void
babel_exit_properly(void)
{
debugf(BABEL_DEBUG_COMMON, "Exiting...");
usleep(roughly(10000));
gettime(&babel_now);
/* Uninstall and flush all routes. */
debugf(BABEL_DEBUG_COMMON, "Uninstall routes.");
flush_all_routes();
babel_interface_close_all();
babel_zebra_close_connexion();
babel_save_state_file();
debugf(BABEL_DEBUG_COMMON, "Remove pid file.");
debugf(BABEL_DEBUG_COMMON, "Done.");
vrf_terminate();
frr_fini();
exit(0);
}
static void
babel_save_state_file(void)
{
int fd;
int rc;
debugf(BABEL_DEBUG_COMMON, "Save state file.");
fd = open(state_file, O_WRONLY | O_TRUNC | O_CREAT, 0644);
if(fd < 0) {
flog_err_sys(EC_LIB_SYSTEM_CALL, "creat(babel-state): %s",
safe_strerror(errno));
unlink(state_file);
} else {
struct timeval realnow;
char buf[100];
gettimeofday(&realnow, NULL);
rc = snprintf(buf, 100, "%s %d %ld\n",
format_eui64(myid), (int)myseqno,
(long)realnow.tv_sec);
if(rc < 0 || rc >= 100) {
flog_err(EC_BABEL_CONFIG, "write(babel-state): overflow.");
unlink(state_file);
} else {
rc = write(fd, buf, rc);
if(rc < 0) {
flog_err(EC_BABEL_CONFIG, "write(babel-state): %s",
safe_strerror(errno));
unlink(state_file);
}
fsync(fd);
}
close(fd);
}
}
void
show_babel_main_configuration (struct vty *vty)
{
vty_out (vty,
"state file = %s\n"
"configuration file = %s\n"
"protocol information:\n"
" multicast address = %s\n"
" port = %d\n"
"vty address = %s\n"
"vty port = %d\n"
"id = %s\n"
"kernel_metric = %d\n",
state_file,
babeld_di.config_file,
format_address(protocol_group),
protocol_port,
babel_vty_addr ? babel_vty_addr : "None",
babel_vty_port,
format_eui64(myid),
kernel_metric);
}

29
babeld/babel_main.h Normal file
View file

@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
/*
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
*/
#ifndef BABEL_MAIN_H
#define BABEL_MAIN_H
#include "vty.h"
extern struct timeval babel_now; /* current time */
extern struct event_loop *master; /* quagga's threads handler */
extern int debug;
extern int resend_delay;
extern unsigned char myid[8];
extern const unsigned char zeroes[16], ones[16];
extern int protocol_port;
extern unsigned char protocol_group[16];
extern int protocol_socket;
extern int kernel_socket;
extern int max_request_hopcount;
void babel_load_state_file(void);
void show_babel_main_configuration (struct vty *vty);
#endif /* BABEL_MAIN_H */

253
babeld/babel_zebra.c Normal file
View file

@ -0,0 +1,253 @@
// SPDX-License-Identifier: MIT
/*
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
*/
/* FRR's includes */
#include <zebra.h>
#include "command.h"
#include "zclient.h"
#include "stream.h"
/* babel's includes*/
#include "babel_zebra.h"
#include "babel_interface.h"
#include "xroute.h"
#include "util.h"
void babelz_zebra_init(void);
/* we must use a pointer because of zclient.c's functions (new, free). */
struct zclient *zclient;
/* Debug types */
static const struct {
int type;
int str_min_len;
const char *str;
} debug_type[] = {
{BABEL_DEBUG_COMMON, 1, "common"},
{BABEL_DEBUG_KERNEL, 1, "kernel"},
{BABEL_DEBUG_FILTER, 1, "filter"},
{BABEL_DEBUG_TIMEOUT, 1, "timeout"},
{BABEL_DEBUG_IF, 1, "interface"},
{BABEL_DEBUG_ROUTE, 1, "route"},
{BABEL_DEBUG_ALL, 1, "all"},
{0, 0, NULL}
};
/* Zebra route add and delete treatment. */
static int
babel_zebra_read_route (ZAPI_CALLBACK_ARGS)
{
struct zapi_route api;
if (zapi_route_decode(zclient->ibuf, &api) < 0)
return -1;
/* we completely ignore srcdest routes for now. */
if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX))
return 0;
if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) {
babel_route_add(&api);
} else {
babel_route_delete(&api);
}
return 0;
}
/* [Babel Command] */
DEFUN (babel_redistribute_type,
babel_redistribute_type_cmd,
"[no] redistribute <ipv4 " FRR_IP_REDIST_STR_BABELD "|ipv6 " FRR_IP6_REDIST_STR_BABELD ">",
NO_STR
"Redistribute\n"
"Redistribute IPv4 routes\n"
FRR_IP_REDIST_HELP_STR_BABELD
"Redistribute IPv6 routes\n"
FRR_IP6_REDIST_HELP_STR_BABELD)
{
int negate = 0;
int family;
int afi;
int type;
int idx = 0;
if (argv_find(argv, argc, "no", &idx))
negate = 1;
argv_find(argv, argc, "redistribute", &idx);
family = str2family(argv[idx + 1]->text);
if (family < 0)
return CMD_WARNING_CONFIG_FAILED;
afi = family2afi(family);
if (!afi)
return CMD_WARNING_CONFIG_FAILED;
type = proto_redistnum(afi, argv[idx + 2]->text);
if (type < 0) {
vty_out (vty, "Invalid type %s\n", argv[idx + 2]->arg);
return CMD_WARNING_CONFIG_FAILED;
}
if (!negate)
zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, afi, type, 0, VRF_DEFAULT);
else {
zclient_redistribute (ZEBRA_REDISTRIBUTE_DELETE, zclient, afi, type, 0, VRF_DEFAULT);
/* perhaps should we remove xroutes having the same type... */
}
return CMD_SUCCESS;
}
#ifndef NO_DEBUG
/* [Babel Command] */
DEFUN (debug_babel,
debug_babel_cmd,
"debug babel <common|kernel|filter|timeout|interface|route|all>",
"Enable debug messages for specific or all part.\n"
"Babel information\n"
"Common messages (default)\n"
"Kernel messages\n"
"Filter messages\n"
"Timeout messages\n"
"Interface messages\n"
"Route messages\n"
"All messages\n")
{
int i;
for(i = 0; debug_type[i].str != NULL; i++) {
if (strncmp (debug_type[i].str, argv[2]->arg,
debug_type[i].str_min_len) == 0) {
SET_FLAG(debug, debug_type[i].type);
return CMD_SUCCESS;
}
}
vty_out (vty, "Invalid type %s\n", argv[2]->arg);
return CMD_WARNING_CONFIG_FAILED;
}
/* [Babel Command] */
DEFUN (no_debug_babel,
no_debug_babel_cmd,
"no debug babel <common|kernel|filter|timeout|interface|route|all>",
NO_STR
"Disable debug messages for specific or all part.\n"
"Babel information\n"
"Common messages (default)\n"
"Kernel messages\n"
"Filter messages\n"
"Timeout messages\n"
"Interface messages\n"
"Route messages\n"
"All messages\n")
{
int i;
for (i = 0; debug_type[i].str; i++) {
if (strncmp(debug_type[i].str, argv[3]->arg,
debug_type[i].str_min_len) == 0) {
UNSET_FLAG(debug, debug_type[i].type);
return CMD_SUCCESS;
}
}
vty_out (vty, "Invalid type %s\n", argv[3]->arg);
return CMD_WARNING_CONFIG_FAILED;
}
#endif /* NO_DEBUG */
/* Output "debug" statement lines, if necessary. */
int
debug_babel_config_write (struct vty * vty)
{
#ifdef NO_DEBUG
return 0;
#else
int i, lines = 0;
if (debug == BABEL_DEBUG_ALL)
{
vty_out (vty, "debug babel all\n");
lines++;
}
else
{
for (i = 0; debug_type[i].str != NULL; i++)
{
if (debug_type[i].type != BABEL_DEBUG_ALL
&& CHECK_FLAG (debug, debug_type[i].type))
{
vty_out (vty, "debug babel %s\n", debug_type[i].str);
lines++;
}
}
}
if (lines)
{
vty_out (vty, "!\n");
lines++;
}
return lines;
#endif /* NO_DEBUG */
}
DEFUN_NOSH (show_debugging_babel,
show_debugging_babel_cmd,
"show debugging [babel]",
SHOW_STR
DEBUG_STR
"Babel")
{
vty_out(vty, "BABEL debugging status\n");
debug_babel_config_write(vty);
cmd_show_lib_debugs(vty);
return CMD_SUCCESS;
}
static void
babel_zebra_connected (struct zclient *zclient)
{
zclient_send_reg_requests (zclient, VRF_DEFAULT);
}
static zclient_handler *const babel_handlers[] = {
[ZEBRA_INTERFACE_ADDRESS_ADD] = babel_interface_address_add,
[ZEBRA_INTERFACE_ADDRESS_DELETE] = babel_interface_address_delete,
[ZEBRA_REDISTRIBUTE_ROUTE_ADD] = babel_zebra_read_route,
[ZEBRA_REDISTRIBUTE_ROUTE_DEL] = babel_zebra_read_route,
};
void babelz_zebra_init(void)
{
zclient = zclient_new(master, &zclient_options_default, babel_handlers,
array_size(babel_handlers));
zclient_init(zclient, ZEBRA_ROUTE_BABEL, 0, &babeld_privs);
zclient->zebra_connected = babel_zebra_connected;
install_element(BABEL_NODE, &babel_redistribute_type_cmd);
install_element(ENABLE_NODE, &debug_babel_cmd);
install_element(ENABLE_NODE, &no_debug_babel_cmd);
install_element(CONFIG_NODE, &debug_babel_cmd);
install_element(CONFIG_NODE, &no_debug_babel_cmd);
install_element(ENABLE_NODE, &show_debugging_babel_cmd);
}
void
babel_zebra_close_connexion(void)
{
zclient_stop(zclient);
zclient_free(zclient);
}

17
babeld/babel_zebra.h Normal file
View file

@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT
/*
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
*/
#ifndef BABEL_ZEBRA_H
#define BABEL_ZEBRA_H
#include "vty.h"
extern struct zclient *zclient;
void babelz_zebra_init(void);
void babel_zebra_close_connexion(void);
extern int debug_babel_config_write (struct vty *);
#endif

896
babeld/babeld.c Normal file
View file

@ -0,0 +1,896 @@
// SPDX-License-Identifier: MIT
/*
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
*/
#include <zebra.h>
#include "command.h"
#include "prefix.h"
#include "memory.h"
#include "table.h"
#include "distribute.h"
#include "prefix.h"
#include "filter.h"
#include "plist.h"
#include "lib_errors.h"
#include "network.h"
#include "if.h"
#include "babel_main.h"
#include "babeld.h"
#include "util.h"
#include "net.h"
#include "kernel.h"
#include "babel_interface.h"
#include "neighbour.h"
#include "route.h"
#include "message.h"
#include "resend.h"
#include "babel_filter.h"
#include "babel_zebra.h"
#include "babel_errors.h"
#ifndef VTYSH_EXTRACT_PL
#include "babeld/babeld_clippy.c"
#endif
DEFINE_MGROUP(BABELD, "babeld");
DEFINE_MTYPE_STATIC(BABELD, BABEL, "Babel Structure");
static void babel_init_routing_process(struct event *thread);
static void babel_get_myid(void);
static void babel_initial_noise(void);
static void babel_read_protocol(struct event *thread);
static void babel_main_loop(struct event *thread);
static void babel_set_timer(struct timeval *timeout);
static void babel_fill_with_next_timeout(struct timeval *tv);
static void
babel_distribute_update (struct distribute_ctx *ctx, struct distribute *dist);
/* Informations relative to the babel running daemon. */
static struct babel *babel_routing_process = NULL;
static unsigned char *receive_buffer = NULL;
static int receive_buffer_size = 0;
/* timeouts */
struct timeval check_neighbours_timeout;
static time_t expiry_time;
static time_t source_expiry_time;
/* Babel node structure. */
static int babel_config_write (struct vty *vty);
static struct cmd_node cmd_babel_node =
{
.name = "babel",
.node = BABEL_NODE,
.parent_node = CONFIG_NODE,
.prompt = "%s(config-router)# ",
.config_write = babel_config_write,
};
/* print current babel configuration on vty */
static int
babel_config_write (struct vty *vty)
{
int lines = 0;
int afi;
int i;
/* list enabled debug modes */
lines += debug_babel_config_write (vty);
if (!babel_routing_process)
return lines;
vty_out (vty, "router babel\n");
if (diversity_kind != DIVERSITY_NONE)
{
vty_out (vty, " babel diversity\n");
lines++;
}
if (diversity_factor != BABEL_DEFAULT_DIVERSITY_FACTOR)
{
vty_out (vty, " babel diversity-factor %d\n",diversity_factor);
lines++;
}
if (resend_delay != BABEL_DEFAULT_RESEND_DELAY)
{
vty_out (vty, " babel resend-delay %u\n", resend_delay);
lines++;
}
if (smoothing_half_life != BABEL_DEFAULT_SMOOTHING_HALF_LIFE)
{
vty_out (vty, " babel smoothing-half-life %u\n",
smoothing_half_life);
lines++;
}
/* list enabled interfaces */
lines = 1 + babel_enable_if_config_write (vty);
/* list redistributed protocols */
for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
if (i != zclient->redist_default &&
vrf_bitmap_check(&zclient->redist[afi][i], VRF_DEFAULT)) {
vty_out(vty, " redistribute %s %s\n",
(afi == AFI_IP) ? "ipv4" : "ipv6",
zebra_route_string(i));
lines++;
}
}
}
lines += config_write_distribute (vty, babel_routing_process->distribute_ctx);
vty_out (vty, "exit\n");
return lines;
}
static int
babel_create_routing_process (void)
{
assert (babel_routing_process == NULL);
/* Allocaste Babel instance. */
babel_routing_process = XCALLOC(MTYPE_BABEL, sizeof(struct babel));
/* Initialize timeouts */
gettime(&babel_now);
expiry_time = babel_now.tv_sec + roughly(30);
source_expiry_time = babel_now.tv_sec + roughly(300);
/* Make socket for Babel protocol. */
protocol_socket = babel_socket(protocol_port);
if (protocol_socket < 0) {
flog_err_sys(EC_LIB_SOCKET, "Couldn't create link local socket: %s",
safe_strerror(errno));
goto fail;
}
/* Threads. */
event_add_read(master, babel_read_protocol, NULL, protocol_socket,
&babel_routing_process->t_read);
/* wait a little: zebra will announce interfaces, addresses, routes... */
event_add_timer_msec(master, babel_init_routing_process, NULL, 200L,
&babel_routing_process->t_update);
/* Distribute list install. */
babel_routing_process->distribute_ctx = distribute_list_ctx_create (vrf_lookup_by_id(VRF_DEFAULT));
distribute_list_add_hook (babel_routing_process->distribute_ctx, babel_distribute_update);
distribute_list_delete_hook (babel_routing_process->distribute_ctx, babel_distribute_update);
return 0;
fail:
XFREE(MTYPE_BABEL, babel_routing_process);
return -1;
}
/* thread reading entries form others babel daemons */
static void babel_read_protocol(struct event *thread)
{
int rc;
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
struct interface *ifp = NULL;
struct sockaddr_in6 sin6;
assert(babel_routing_process != NULL);
assert(protocol_socket >= 0);
rc = babel_recv(protocol_socket,
receive_buffer, receive_buffer_size,
(struct sockaddr*)&sin6, sizeof(sin6));
if(rc < 0) {
if(errno != EAGAIN && errno != EINTR) {
flog_err_sys(EC_LIB_SOCKET, "recv: %s", safe_strerror(errno));
}
} else {
FOR_ALL_INTERFACES(vrf, ifp) {
if(!if_up(ifp))
continue;
if(ifp->ifindex == (ifindex_t)sin6.sin6_scope_id) {
parse_packet((unsigned char*)&sin6.sin6_addr, ifp,
receive_buffer, rc);
break;
}
}
}
/* re-add thread */
event_add_read(master, &babel_read_protocol, NULL, protocol_socket,
&babel_routing_process->t_read);
}
/* Zebra will give some information, especially about interfaces. This function
must be call with a litte timeout wich may give zebra the time to do his job,
making these inits have sense. */
static void babel_init_routing_process(struct event *thread)
{
myseqno = (frr_weak_random() & 0xFFFF);
babel_get_myid();
babel_load_state_file();
debugf(BABEL_DEBUG_COMMON, "My ID is : %s.", format_eui64(myid));
babel_initial_noise();
babel_main_loop(thread);/* this function self-add to the t_update thread */
}
/* fill "myid" with an unique id (only if myid != {0}). */
static void
babel_get_myid(void)
{
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
struct interface *ifp = NULL;
int rc;
int i;
/* if we already have an id (from state file), we return. */
if (memcmp(myid, zeroes, 8) != 0) {
return;
}
FOR_ALL_INTERFACES(vrf, ifp) {
/* ifp->ifindex is not necessarily valid at this point */
int ifindex = if_nametoindex(ifp->name);
if(ifindex > 0) {
unsigned char eui[8];
rc = if_eui64(ifindex, eui);
if(rc < 0)
continue;
memcpy(myid, eui, 8);
return;
}
}
/* We failed to get a global EUI64 from the interfaces we were given.
Let's try to find an interface with a MAC address. */
for(i = 1; i < 256; i++) {
char buf[IFNAMSIZ], *ifname;
unsigned char eui[8];
ifname = if_indextoname(i, buf);
if(ifname == NULL)
continue;
rc = if_eui64(i, eui);
if(rc < 0)
continue;
memcpy(myid, eui, 8);
return;
}
flog_err(EC_BABEL_CONFIG, "Couldn't find router id -- using random value.");
rc = read_random_bytes(myid, 8);
if(rc < 0) {
flog_err(EC_BABEL_CONFIG, "read(random): %s (cannot assign an ID)",
safe_strerror(errno));
exit(1);
}
/* Clear group and global bits */
UNSET_FLAG (myid[0], 3);
}
/* Make some noise so that others notice us, and send retractions in
case we were restarted recently */
static void
babel_initial_noise(void)
{
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
struct interface *ifp = NULL;
FOR_ALL_INTERFACES(vrf, ifp) {
if(!if_up(ifp))
continue;
/* Apply jitter before we send the first message. */
usleep(roughly(10000));
gettime(&babel_now);
send_hello(ifp);
send_wildcard_retraction(ifp);
}
FOR_ALL_INTERFACES(vrf, ifp) {
if(!if_up(ifp))
continue;
usleep(roughly(10000));
gettime(&babel_now);
send_hello(ifp);
send_wildcard_retraction(ifp);
send_self_update(ifp);
send_request(ifp, NULL, 0);
flushupdates(ifp);
flushbuf(ifp);
}
}
/* Delete all the added babel routes, make babeld only speak to zebra. */
static void
babel_clean_routing_process(void)
{
flush_all_routes();
babel_interface_close_all();
/* cancel events */
event_cancel(&babel_routing_process->t_read);
event_cancel(&babel_routing_process->t_update);
distribute_list_delete(&babel_routing_process->distribute_ctx);
XFREE(MTYPE_BABEL, babel_routing_process);
}
/* Function used with timeout. */
static void babel_main_loop(struct event *thread)
{
struct timeval tv;
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
struct interface *ifp = NULL;
while(1) {
gettime(&babel_now);
/* timeouts --------------------------------------------------------- */
/* get the next timeout */
babel_fill_with_next_timeout(&tv);
/* if there is no timeout, we must wait. */
if(timeval_compare(&tv, &babel_now) > 0) {
timeval_minus(&tv, &tv, &babel_now);
debugf(BABEL_DEBUG_TIMEOUT, "babel main loop : timeout: %lld msecs",
(long long)tv.tv_sec * 1000 + tv.tv_usec / 1000);
/* it happens often to have less than 1 ms, it's bad. */
timeval_add_msec(&tv, &tv, 300);
babel_set_timer(&tv);
return;
}
gettime(&babel_now);
/* update database -------------------------------------------------- */
if(timeval_compare(&check_neighbours_timeout, &babel_now) < 0) {
int msecs;
msecs = check_neighbours();
/* Multiply by 3/2 to allow neighbours to expire. */
msecs = MAX(3 * msecs / 2, 10);
schedule_neighbours_check(msecs, 1);
}
if(babel_now.tv_sec >= expiry_time) {
expire_routes();
expire_resend();
expiry_time = babel_now.tv_sec + roughly(30);
}
if(babel_now.tv_sec >= source_expiry_time) {
expire_sources();
source_expiry_time = babel_now.tv_sec + roughly(300);
}
FOR_ALL_INTERFACES(vrf, ifp) {
babel_interface_nfo *babel_ifp = NULL;
if(!if_up(ifp))
continue;
babel_ifp = babel_get_if_nfo(ifp);
if(timeval_compare(&babel_now, &babel_ifp->hello_timeout) >= 0)
send_hello(ifp);
if(timeval_compare(&babel_now, &babel_ifp->update_timeout) >= 0)
send_update(ifp, 0, NULL, 0);
if(timeval_compare(&babel_now,
&babel_ifp->update_flush_timeout) >= 0)
flushupdates(ifp);
}
if(resend_time.tv_sec != 0) {
if(timeval_compare(&babel_now, &resend_time) >= 0)
do_resend();
}
if(unicast_flush_timeout.tv_sec != 0) {
if(timeval_compare(&babel_now, &unicast_flush_timeout) >= 0)
flush_unicast(1);
}
FOR_ALL_INTERFACES(vrf, ifp) {
babel_interface_nfo *babel_ifp = NULL;
if(!if_up(ifp))
continue;
babel_ifp = babel_get_if_nfo(ifp);
if(babel_ifp->flush_timeout.tv_sec != 0) {
if(timeval_compare(&babel_now, &babel_ifp->flush_timeout) >= 0)
flushbuf(ifp);
}
}
}
assert(0); /* this line should never be reach */
}
static void
printIfMin(struct timeval *tv, int cmd, const char *tag, const char *ifname)
{
static struct timeval curr_tv;
static char buffer[200];
static const char *curr_tag = NULL;
switch (cmd) {
case 0: /* reset timeval */
curr_tv = *tv;
if(ifname != NULL) {
snprintf(buffer, 200L, "interface: %s; %s", ifname, tag);
curr_tag = buffer;
} else {
curr_tag = tag;
}
break;
case 1: /* take the min */
if (tv->tv_sec == 0 && tv->tv_usec == 0) { /* if (tv == ∞) */
break;
}
if (tv->tv_sec < curr_tv.tv_sec ||(tv->tv_sec == curr_tv.tv_sec &&
tv->tv_usec < curr_tv.tv_usec)) {
curr_tv = *tv;
if(ifname != NULL) {
snprintf(buffer, 200L, "interface: %s; %s", ifname, tag);
curr_tag = buffer;
} else {
curr_tag = tag;
}
}
break;
case 2: /* print message */
debugf(BABEL_DEBUG_TIMEOUT, "next timeout due to: %s", curr_tag);
break;
default:
break;
}
}
static void
babel_fill_with_next_timeout(struct timeval *tv)
{
#if (defined NO_DEBUG)
#define printIfMin(a,b,c,d)
#else
#define printIfMin(a, b, c, d) \
if (unlikely(debug & BABEL_DEBUG_TIMEOUT)) { \
printIfMin(a, b, c, d); \
}
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
struct interface *ifp = NULL;
*tv = check_neighbours_timeout;
printIfMin(tv, 0, "check_neighbours_timeout", NULL);
timeval_min_sec(tv, expiry_time);
printIfMin(tv, 1, "expiry_time", NULL);
timeval_min_sec(tv, source_expiry_time);
printIfMin(tv, 1, "source_expiry_time", NULL);
timeval_min(tv, &resend_time);
printIfMin(tv, 1, "resend_time", NULL);
FOR_ALL_INTERFACES (vrf, ifp) {
babel_interface_nfo *babel_ifp = NULL;
if (!if_up(ifp))
continue;
babel_ifp = babel_get_if_nfo(ifp);
timeval_min(tv, &babel_ifp->flush_timeout);
printIfMin(tv, 1, "flush_timeout", ifp->name);
timeval_min(tv, &babel_ifp->hello_timeout);
printIfMin(tv, 1, "hello_timeout", ifp->name);
timeval_min(tv, &babel_ifp->update_timeout);
printIfMin(tv, 1, "update_timeout", ifp->name);
timeval_min(tv, &babel_ifp->update_flush_timeout);
printIfMin(tv, 1, "update_flush_timeout", ifp->name);
}
timeval_min(tv, &unicast_flush_timeout);
printIfMin(tv, 1, "unicast_flush_timeout", NULL);
printIfMin(tv, 2, NULL, NULL);
#undef printIfMin
#endif
}
/* set the t_update thread of the babel routing process to be launch in
'timeout' (approximate at the milisecond) */
static void
babel_set_timer(struct timeval *timeout)
{
long msecs = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
event_cancel(&(babel_routing_process->t_update));
event_add_timer_msec(master, babel_main_loop, NULL, msecs,
&babel_routing_process->t_update);
}
void
schedule_neighbours_check(int msecs, int override)
{
struct timeval timeout;
timeval_add_msec(&timeout, &babel_now, msecs);
if(override)
check_neighbours_timeout = timeout;
else
timeval_min(&check_neighbours_timeout, &timeout);
}
int
resize_receive_buffer(int size)
{
if(size <= receive_buffer_size)
return 0;
if(receive_buffer == NULL) {
receive_buffer = malloc(size);
if(receive_buffer == NULL) {
flog_err(EC_BABEL_MEMORY, "malloc(receive_buffer): %s",
safe_strerror(errno));
return -1;
}
receive_buffer_size = size;
} else {
unsigned char *new;
new = realloc(receive_buffer, size);
if(new == NULL) {
flog_err(EC_BABEL_MEMORY, "realloc(receive_buffer): %s",
safe_strerror(errno));
return -1;
}
receive_buffer = new;
receive_buffer_size = size;
}
return 1;
}
static void
babel_distribute_update (struct distribute_ctx *ctx, struct distribute *dist)
{
struct interface *ifp;
babel_interface_nfo *babel_ifp;
int type;
int family;
if (! dist->ifname)
return;
ifp = if_lookup_by_name (dist->ifname, VRF_DEFAULT);
if (ifp == NULL)
return;
babel_ifp = babel_get_if_nfo(ifp);
for (type = 0; type < DISTRIBUTE_MAX; type++) {
family = type == DISTRIBUTE_V4_IN || type == DISTRIBUTE_V4_OUT ?
AFI_IP : AFI_IP6;
if (dist->list[type])
babel_ifp->list[type] = access_list_lookup (family,
dist->list[type]);
else
babel_ifp->list[type] = NULL;
if (dist->prefix[type])
babel_ifp->prefix[type] = prefix_list_lookup (family,
dist->prefix[type]);
else
babel_ifp->prefix[type] = NULL;
}
}
static void
babel_distribute_update_interface (struct interface *ifp)
{
struct distribute *dist = NULL;
if (babel_routing_process)
dist = distribute_lookup(babel_routing_process->distribute_ctx, ifp->name);
if (dist)
babel_distribute_update (babel_routing_process->distribute_ctx, dist);
}
/* Update all interface's distribute list. */
static void
babel_distribute_update_all (struct prefix_list *notused)
{
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
struct interface *ifp;
FOR_ALL_INTERFACES (vrf, ifp)
babel_distribute_update_interface (ifp);
}
static void
babel_distribute_update_all_wrapper (struct access_list *notused)
{
babel_distribute_update_all(NULL);
}
/* [Command] */
DEFUN_NOSH (router_babel,
router_babel_cmd,
"router babel",
"Enable a routing process\n"
"Make Babel instance command\n")
{
int ret;
vty->node = BABEL_NODE;
if (!babel_routing_process) {
ret = babel_create_routing_process ();
/* Notice to user we couldn't create Babel. */
if (ret < 0) {
zlog_warn ("can't create Babel");
return CMD_WARNING;
}
}
return CMD_SUCCESS;
}
/* [Command] */
DEFUN (no_router_babel,
no_router_babel_cmd,
"no router babel",
NO_STR
"Disable a routing process\n"
"Remove Babel instance command\n")
{
if(babel_routing_process)
babel_clean_routing_process();
return CMD_SUCCESS;
}
/* [Babel Command] */
DEFUN (babel_diversity,
babel_diversity_cmd,
"babel diversity",
"Babel commands\n"
"Enable diversity-aware routing.\n")
{
diversity_kind = DIVERSITY_CHANNEL;
return CMD_SUCCESS;
}
/* [Babel Command] */
DEFUN (no_babel_diversity,
no_babel_diversity_cmd,
"no babel diversity",
NO_STR
"Babel commands\n"
"Disable diversity-aware routing.\n")
{
diversity_kind = DIVERSITY_NONE;
return CMD_SUCCESS;
}
/* [Babel Command] */
DEFPY (babel_diversity_factor,
babel_diversity_factor_cmd,
"[no] babel diversity-factor (1-256)$factor",
NO_STR
"Babel commands\n"
"Set the diversity factor.\n"
"Factor in units of 1/256.\n")
{
diversity_factor = no ? BABEL_DEFAULT_DIVERSITY_FACTOR : factor;
return CMD_SUCCESS;
}
/* [Babel Command] */
DEFPY (babel_set_resend_delay,
babel_set_resend_delay_cmd,
"[no] babel resend-delay (20-655340)$delay",
NO_STR
"Babel commands\n"
"Time before resending a message\n"
"Milliseconds\n")
{
resend_delay = no ? BABEL_DEFAULT_RESEND_DELAY : delay;
return CMD_SUCCESS;
}
/* [Babel Command] */
DEFPY (babel_set_smoothing_half_life,
babel_set_smoothing_half_life_cmd,
"[no] babel smoothing-half-life (0-65534)$seconds",
NO_STR
"Babel commands\n"
"Smoothing half-life\n"
"Seconds (0 to disable)\n")
{
change_smoothing_half_life(no ? BABEL_DEFAULT_SMOOTHING_HALF_LIFE
: seconds);
return CMD_SUCCESS;
}
DEFUN (babel_distribute_list,
babel_distribute_list_cmd,
"distribute-list ACCESSLIST4_NAME <in|out> [WORD]",
"Filter networks in routing updates\n"
"Access-list name\n"
"Filter incoming routing updates\n"
"Filter outgoing routing updates\n"
"Interface name\n")
{
const char *ifname = NULL;
int prefix = (argv[1]->type == WORD_TKN) ? 1 : 0;
if (argv[argc - 1]->type == VARIABLE_TKN)
ifname = argv[argc - 1]->arg;
return distribute_list_parser(babel_routing_process->distribute_ctx,
prefix, true, argv[2 + prefix]->text,
argv[1 + prefix]->arg, ifname);
}
ALIAS (babel_distribute_list,
babel_distribute_list_prefix_cmd,
"distribute-list prefix PREFIXLIST4_NAME <in|out> [WORD]",
"Filter networks in routing updates\n"
"Specify a prefix list\n"
"Prefix-list name\n"
"Filter incoming routing updates\n"
"Filter outgoing routing updates\n"
"Interface name\n")
DEFUN (babel_no_distribute_list,
babel_no_distribute_list_cmd,
"no distribute-list ACCESSLIST4_NAME <in|out> [WORD]",
NO_STR
"Filter networks in routing updates\n"
"Access-list name\n"
"Filter incoming routing updates\n"
"Filter outgoing routing updates\n"
"Interface name\n")
{
const char *ifname = NULL;
int prefix = (argv[2]->type == WORD_TKN) ? 1 : 0;
if (argv[argc - 1]->type == VARIABLE_TKN)
ifname = argv[argc - 1]->arg;
return distribute_list_no_parser(babel_routing_process->distribute_ctx,
vty, prefix, true,
argv[3 + prefix]->text,
argv[2 + prefix]->arg, ifname);
}
ALIAS (babel_no_distribute_list,
babel_no_distribute_list_prefix_cmd,
"no distribute-list prefix PREFIXLIST4_NAME <in|out> [WORD]",
NO_STR
"Filter networks in routing updates\n"
"Specify a prefix list\n"
"Prefix-list name\n"
"Filter incoming routing updates\n"
"Filter outgoing routing updates\n"
"Interface name\n")
DEFUN (babel_ipv6_distribute_list,
babel_ipv6_distribute_list_cmd,
"ipv6 distribute-list ACCESSLIST6_NAME <in|out> [WORD]",
"IPv6\n"
"Filter networks in routing updates\n"
"Access-list name\n"
"Filter incoming routing updates\n"
"Filter outgoing routing updates\n"
"Interface name\n")
{
const char *ifname = NULL;
int prefix = (argv[2]->type == WORD_TKN) ? 1 : 0;
if (argv[argc - 1]->type == VARIABLE_TKN)
ifname = argv[argc - 1]->arg;
return distribute_list_parser(babel_routing_process->distribute_ctx,
prefix, false, argv[3 + prefix]->text,
argv[2 + prefix]->arg, ifname);
}
ALIAS (babel_ipv6_distribute_list,
babel_ipv6_distribute_list_prefix_cmd,
"ipv6 distribute-list prefix PREFIXLIST6_NAME <in|out> [WORD]",
"IPv6\n"
"Filter networks in routing updates\n"
"Specify a prefix list\n"
"Prefix-list name\n"
"Filter incoming routing updates\n"
"Filter outgoing routing updates\n"
"Interface name\n")
DEFUN (babel_no_ipv6_distribute_list,
babel_no_ipv6_distribute_list_cmd,
"no ipv6 distribute-list ACCESSLIST6_NAME <in|out> [WORD]",
NO_STR
"IPv6\n"
"Filter networks in routing updates\n"
"Access-list name\n"
"Filter incoming routing updates\n"
"Filter outgoing routing updates\n"
"Interface name\n")
{
const char *ifname = NULL;
int prefix = (argv[3]->type == WORD_TKN) ? 1 : 0;
if (argv[argc - 1]->type == VARIABLE_TKN)
ifname = argv[argc - 1]->arg;
return distribute_list_no_parser(babel_routing_process->distribute_ctx,
vty, prefix, false,
argv[4 + prefix]->text,
argv[3 + prefix]->arg, ifname);
}
ALIAS (babel_no_ipv6_distribute_list,
babel_no_ipv6_distribute_list_prefix_cmd,
"no ipv6 distribute-list prefix PREFIXLIST6_NAME <in|out> [WORD]",
NO_STR
"IPv6\n"
"Filter networks in routing updates\n"
"Specify a prefix list\n"
"Prefix-list name\n"
"Filter incoming routing updates\n"
"Filter outgoing routing updates\n"
"Interface name\n")
void
babeld_quagga_init(void)
{
install_node(&cmd_babel_node);
install_element(CONFIG_NODE, &router_babel_cmd);
install_element(CONFIG_NODE, &no_router_babel_cmd);
install_default(BABEL_NODE);
install_element(BABEL_NODE, &babel_diversity_cmd);
install_element(BABEL_NODE, &no_babel_diversity_cmd);
install_element(BABEL_NODE, &babel_diversity_factor_cmd);
install_element(BABEL_NODE, &babel_set_resend_delay_cmd);
install_element(BABEL_NODE, &babel_set_smoothing_half_life_cmd);
install_element(BABEL_NODE, &babel_distribute_list_cmd);
install_element(BABEL_NODE, &babel_distribute_list_prefix_cmd);
install_element(BABEL_NODE, &babel_no_distribute_list_cmd);
install_element(BABEL_NODE, &babel_no_distribute_list_prefix_cmd);
install_element(BABEL_NODE, &babel_ipv6_distribute_list_cmd);
install_element(BABEL_NODE, &babel_ipv6_distribute_list_prefix_cmd);
install_element(BABEL_NODE, &babel_no_ipv6_distribute_list_cmd);
install_element(BABEL_NODE, &babel_no_ipv6_distribute_list_prefix_cmd);
vrf_cmd_init(NULL);
babel_if_init();
/* Access list install. */
access_list_init ();
access_list_add_hook (babel_distribute_update_all_wrapper);
access_list_delete_hook (babel_distribute_update_all_wrapper);
/* Prefix list initialize.*/
prefix_list_init ();
prefix_list_add_hook (babel_distribute_update_all);
prefix_list_delete_hook (babel_distribute_update_all);
}
/* Stubs to adapt Babel's filtering calls to Quagga's infrastructure. */
int
input_filter(const unsigned char *id,
const unsigned char *prefix, unsigned short plen,
const unsigned char *neigh, unsigned int ifindex)
{
return babel_filter(0, prefix, plen, ifindex);
}
int
output_filter(const unsigned char *id, const unsigned char *prefix,
unsigned short plen, unsigned int ifindex)
{
return babel_filter(1, prefix, plen, ifindex);
}
/* There's no redistribute filter in Quagga -- the zebra daemon does its
own filtering. */
int
redistribute_filter(const unsigned char *prefix, unsigned short plen,
unsigned int ifindex, int proto)
{
return 0;
}
struct babel *babel_lookup(void)
{
return babel_routing_process;
}

102
babeld/babeld.h Normal file
View file

@ -0,0 +1,102 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
*/
#ifndef BABEL_BABELD_H
#define BABEL_BABELD_H
#include <zebra.h>
#include "vty.h"
#define INFINITY ((unsigned short)(~0))
#ifndef RTPROT_BABEL
#define RTPROT_BABEL 42
#endif
#define RTPROT_BABEL_LOCAL -2
#undef MAX
#undef MIN
#define MAX(x,y) ((x)<=(y)?(y):(x))
#define MIN(x,y) ((x)<=(y)?(x):(y))
#if defined(__GNUC__) && (__GNUC__ >= 3)
#define ATTRIBUTE(x) __attribute__ (x)
#else
#define ATTRIBUTE(x) /**/
#endif
#if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3)
#define COLD __attribute__ ((cold))
#else
#define COLD /**/
#endif
#ifndef IF_NAMESIZE
#include <sys/socket.h>
#include <net/if.h>
#endif
#ifdef HAVE_VALGRIND
#include <valgrind/memcheck.h>
#else
#ifndef VALGRIND_MAKE_MEM_UNDEFINED
#define VALGRIND_MAKE_MEM_UNDEFINED(a, b) do {} while(0)
#endif
#ifndef VALGRIND_CHECK_MEM_IS_DEFINED
#define VALGRIND_CHECK_MEM_IS_DEFINED(a, b) do {} while(0)
#endif
#endif
#define BABEL_DEFAULT_CONFIG "babeld.conf"
/* Values in milliseconds */
#define BABEL_DEFAULT_HELLO_INTERVAL 4000
#define BABEL_DEFAULT_UPDATE_INTERVAL 16000
#define BABEL_DEFAULT_RESEND_DELAY 2000
#define BABEL_DEFAULT_RTT_DECAY 42
/* Values in microseconds */
#define BABEL_DEFAULT_RTT_MIN 10000
#define BABEL_DEFAULT_RTT_MAX 120000
/* In units of seconds */
#define BABEL_DEFAULT_SMOOTHING_HALF_LIFE 4
/* In units of 1/256. */
#define BABEL_DEFAULT_DIVERSITY_FACTOR 256
#define BABEL_DEFAULT_RXCOST_WIRED 96
#define BABEL_DEFAULT_RXCOST_WIRELESS 256
#define BABEL_DEFAULT_MAX_RTT_PENALTY 150
/* Babel structure. */
struct babel
{
/* Babel threads. */
struct event *t_read; /* on Babel protocol's socket */
struct event *t_update; /* timers */
/* distribute_ctx */
struct distribute_ctx *distribute_ctx;
};
extern struct zebra_privs_t babeld_privs;
extern void babeld_quagga_init(void);
extern int input_filter(const unsigned char *id,
const unsigned char *prefix, unsigned short plen,
const unsigned char *neigh, unsigned int ifindex);
extern int output_filter(const unsigned char *id, const unsigned char *prefix,
unsigned short plen, unsigned int ifindex);
extern int redistribute_filter(const unsigned char *prefix, unsigned short plen,
unsigned int ifindex, int proto);
extern int resize_receive_buffer(int size);
extern void schedule_neighbours_check(int msecs, int override);
extern struct babel *babel_lookup(void);
#endif /* BABEL_BABELD_H */

239
babeld/kernel.c Normal file
View file

@ -0,0 +1,239 @@
// SPDX-License-Identifier: MIT
/*
Copyright 2007, 2008 by Grégoire Henry, Julien Cristau and Juliusz Chroboczek
Copyright 2011, 2012 by Matthieu Boutier and Juliusz Chroboczek
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/time.h>
#include <sys/param.h>
#include <time.h>
#include <fcntl.h>
#include "babeld.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <zebra.h>
#include "prefix.h"
#include "zclient.h"
#include "kernel.h"
#include "privs.h"
#include "command.h"
#include "vty.h"
#include "memory.h"
#include "frrevent.h"
#include "nexthop.h"
#include "util.h"
#include "babel_interface.h"
#include "babel_zebra.h"
static int
zebra_route(int add, int familt, const unsigned char *pref, unsigned short plen,
const unsigned char *gate, int ifindex, unsigned int metric);
int
kernel_interface_operational(struct interface *interface)
{
return if_is_operative(interface);
}
int
kernel_interface_mtu(struct interface *interface)
{
return MIN(interface->mtu, interface->mtu6);
}
int
kernel_interface_wireless(struct interface *interface)
{
return 0;
}
int
kernel_route(enum babel_kernel_routes operation, const unsigned char *pref,
unsigned short plen, const unsigned char *gate, int ifindex,
unsigned int metric, const unsigned char *newgate, int newifindex,
unsigned int newmetric)
{
int rc;
int family;
/* Check that the protocol family is consistent. */
if(plen >= 96 && v4mapped(pref)) {
if(!v4mapped(gate)) {
errno = EINVAL;
return -1;
}
family = AF_INET;
} else {
if(v4mapped(gate)) {
errno = EINVAL;
return -1;
}
family = AF_INET6;
}
switch (operation) {
case ROUTE_ADD:
return zebra_route(1, family, pref, plen, gate, ifindex, metric);
case ROUTE_FLUSH:
return zebra_route(0, family, pref, plen, gate, ifindex, metric);
case ROUTE_MODIFY:
if(newmetric == metric && memcmp(newgate, gate, 16) == 0 &&
newifindex == ifindex)
return 0;
debugf(BABEL_DEBUG_ROUTE, "Modify route: delete old; add new.");
rc = zebra_route(0, family, pref, plen, gate, ifindex, metric);
if (rc < 0)
return -1;
rc = zebra_route(1, family, pref, plen, newgate, newifindex,
newmetric);
return rc;
}
return 0;
}
static int
zebra_route(int add, int family, const unsigned char *pref, unsigned short plen,
const unsigned char *gate, int ifindex, unsigned int metric)
{
struct zapi_route api; /* quagga's communication system */
struct prefix quagga_prefix; /* quagga's prefix */
union g_addr babel_prefix_addr; /* babeld's prefix addr */
struct zapi_nexthop *api_nh; /* next router to go - no ECMP */
api_nh = &api.nexthops[0];
/* convert to be understandable by quagga */
/* convert given addresses */
switch (family) {
case AF_INET:
uchar_to_inaddr(&babel_prefix_addr.ipv4, pref);
break;
case AF_INET6:
uchar_to_in6addr(&babel_prefix_addr.ipv6, pref);
break;
}
/* make prefix structure */
memset (&quagga_prefix, 0, sizeof(quagga_prefix));
quagga_prefix.family = family;
switch (family) {
case AF_INET:
IPV4_ADDR_COPY (&quagga_prefix.u.prefix4, &babel_prefix_addr.ipv4);
/* our plen is for v4mapped's addr */
quagga_prefix.prefixlen = plen - 96;
break;
case AF_INET6:
IPV6_ADDR_COPY (&quagga_prefix.u.prefix6, &babel_prefix_addr.ipv6);
quagga_prefix.prefixlen = plen;
break;
}
apply_mask(&quagga_prefix);
memset(&api, 0, sizeof(api));
api.type = ZEBRA_ROUTE_BABEL;
api.safi = SAFI_UNICAST;
api.vrf_id = VRF_DEFAULT;
api.prefix = quagga_prefix;
if(metric >= KERNEL_INFINITY) {
zapi_route_set_blackhole(&api, BLACKHOLE_REJECT);
} else {
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
api.nexthop_num = 1;
api_nh->ifindex = ifindex;
api_nh->vrf_id = VRF_DEFAULT;
switch (family) {
case AF_INET:
uchar_to_inaddr(&api_nh->gate.ipv4, gate);
if (IPV4_ADDR_SAME(&api_nh->gate.ipv4, &quagga_prefix.u.prefix4)
&& quagga_prefix.prefixlen == IPV4_MAX_BITLEN) {
api_nh->type = NEXTHOP_TYPE_IFINDEX;
} else {
api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
}
break;
case AF_INET6:
uchar_to_in6addr(&api_nh->gate.ipv6, gate);
/* difference to IPv4: always leave the linklocal as nexthop */
api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
break;
}
SET_FLAG(api.message, ZAPI_MESSAGE_METRIC);
api.metric = metric;
}
debugf(BABEL_DEBUG_ROUTE, "%s route (%s) to zebra",
add ? "adding" : "removing",
(family == AF_INET) ? "ipv4" : "ipv6");
return zclient_route_send (add ? ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE,
zclient, &api);
}
int
if_eui64(int ifindex, unsigned char *eui)
{
struct interface *ifp = if_lookup_by_index(ifindex, VRF_DEFAULT);
if (ifp == NULL) {
return -1;
}
uint8_t len = (uint8_t)ifp->hw_addr_len;
char *tmp = (void*) ifp->hw_addr;
if (len == 8) {
memcpy(eui, tmp, 8);
eui[0] ^= 2;
} else if (len == 6) {
memcpy(eui, tmp, 3);
eui[3] = 0xFF;
eui[4] = 0xFE;
memcpy(eui+5, tmp+3, 3);
} else {
return -1;
}
return 0;
}
/* Like gettimeofday, but returns monotonic time. If POSIX clocks are not
available, falls back to gettimeofday but enforces monotonicity. */
void
gettime(struct timeval *tv)
{
monotime(tv);
}
/* If /dev/urandom doesn't exist, this will fail with ENOENT, which the
caller will deal with gracefully. */
int
read_random_bytes(void *buf, size_t len)
{
int fd;
int rc;
fd = open("/dev/urandom", O_RDONLY);
if(fd < 0) {
rc = -1;
} else {
rc = read(fd, buf, len);
if(rc < 0 || (unsigned) rc < len)
rc = -1;
close(fd);
}
return rc;
}

32
babeld/kernel.h Normal file
View file

@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
*/
#ifndef BABEL_KERNEL_H
#define BABEL_KERNEL_H
#include <netinet/in.h>
#include "babel_main.h"
#include "if.h"
#define KERNEL_INFINITY 0xFFFF
enum babel_kernel_routes {
ROUTE_FLUSH,
ROUTE_ADD,
ROUTE_MODIFY,
};
int kernel_interface_operational(struct interface *interface);
int kernel_interface_mtu(struct interface *interface);
int kernel_interface_wireless(struct interface *interface);
int kernel_route(enum babel_kernel_routes operation, const unsigned char *dest,
unsigned short plen, const unsigned char *gate, int ifindex,
unsigned int metric, const unsigned char *newgate,
int newifindex, unsigned int newmetric);
int if_eui64(int ifindex, unsigned char *eui);
void gettime(struct timeval *tv);
int read_random_bytes(void *buf, size_t len);
#endif /* BABEL_KERNEL_H */

1922
babeld/message.c Normal file

File diff suppressed because it is too large Load diff

86
babeld/message.h Normal file
View file

@ -0,0 +1,86 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
*/
#ifndef BABEL_MESSAGE_H
#define BABEL_MESSAGE_H
#include "babel_interface.h"
#define MAX_BUFFERED_UPDATES 200
#define BUCKET_TOKENS_MAX 200
#define BUCKET_TOKENS_PER_SEC 40
/* A registry of assigned TLV and sub-TLV types is available at
http://www.pps.univ-paris-diderot.fr/~jch/software/babel/babel-tlv-registry.text
*/
#define MESSAGE_PAD1 0
#define MESSAGE_PADN 1
#define MESSAGE_ACK_REQ 2
#define MESSAGE_ACK 3
#define MESSAGE_HELLO 4
#define MESSAGE_IHU 5
#define MESSAGE_ROUTER_ID 6
#define MESSAGE_NH 7
#define MESSAGE_UPDATE 8
#define MESSAGE_REQUEST 9
#define MESSAGE_MH_REQUEST 10
#define MESSAGE_MAX 10
/* Protocol extension through sub-TLVs. */
#define SUBTLV_PAD1 0
#define SUBTLV_PADN 1
#define SUBTLV_DIVERSITY 2 /* Also known as babelz. */
#define SUBTLV_TIMESTAMP 3 /* Used to compute RTT. */
#define SUBTLV_SOURCE_PREFIX 128 /* Source-specific routing. */
#define SUBTLV_MANDATORY 0x80
extern unsigned short myseqno;
extern int broadcast_ihu;
extern int split_horizon;
extern struct neighbour *unicast_neighbour;
extern struct timeval unicast_flush_timeout;
void parse_packet(const unsigned char *from, struct interface *ifp,
const unsigned char *packet, int packetlen);
void flushbuf(struct interface *ifp);
void flushupdates(struct interface *ifp);
void send_ack(struct neighbour *neigh, unsigned short nonce,
unsigned short interval);
void send_hello_noupdate(struct interface *ifp, unsigned interval);
void send_hello(struct interface *ifp);
void flush_unicast(int dofree);
void send_update(struct interface *ifp, int urgent,
const unsigned char *prefix, unsigned char plen);
void send_update_resend(struct interface *ifp,
const unsigned char *prefix, unsigned char plen);
void send_wildcard_retraction(struct interface *ifp);
void update_myseqno(void);
void send_self_update(struct interface *ifp);
void send_ihu(struct neighbour *neigh, struct interface *ifp);
void send_marginal_ihu(struct interface *ifp);
void send_request(struct interface *ifp,
const unsigned char *prefix, unsigned char plen);
void send_unicast_request(struct neighbour *neigh,
const unsigned char *prefix, unsigned char plen);
void send_multihop_request(struct interface *ifp,
const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id,
unsigned short hop_count);
void
send_unicast_multihop_request(struct neighbour *neigh,
const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id,
unsigned short hop_count);
void send_request_resend(struct neighbour *neigh,
const unsigned char *prefix, unsigned char plen,
unsigned short seqno, unsigned char *id);
void handle_request(struct neighbour *neigh, const unsigned char *prefix,
unsigned char plen, unsigned char hop_count,
unsigned short seqno, const unsigned char *id);
#endif

343
babeld/neighbour.c Normal file
View file

@ -0,0 +1,343 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/time.h>
#include <time.h>
#include <zebra.h>
#include "if.h"
#include "babel_main.h"
#include "babeld.h"
#include "util.h"
#include "babel_interface.h"
#include "neighbour.h"
#include "source.h"
#include "route.h"
#include "message.h"
#include "resend.h"
#include "babel_errors.h"
struct neighbour *neighs = NULL;
static struct neighbour *
find_neighbour_nocreate(const unsigned char *address, struct interface *ifp)
{
struct neighbour *neigh;
FOR_ALL_NEIGHBOURS(neigh) {
if(memcmp(address, neigh->address, 16) == 0 && neigh->ifp == ifp)
return neigh;
}
return NULL;
}
void flush_neighbour(struct neighbour *neigh)
{
debugf(BABEL_DEBUG_COMMON,"Flushing neighbour %s (reach 0x%04x)",
format_address(neigh->address), neigh->reach);
flush_neighbour_routes(neigh);
if(unicast_neighbour == neigh)
flush_unicast(1);
flush_resends(neigh);
if(neighs == neigh) {
neighs = neigh->next;
} else {
struct neighbour *previous = neighs;
while(previous->next != neigh)
previous = previous->next;
previous->next = neigh->next;
}
free(neigh);
}
struct neighbour *
find_neighbour(const unsigned char *address, struct interface *ifp)
{
struct neighbour *neigh;
const struct timeval zero = {0, 0};
neigh = find_neighbour_nocreate(address, ifp);
if(neigh)
return neigh;
debugf(BABEL_DEBUG_COMMON,"Creating neighbour %s on %s.",
format_address(address), ifp->name);
neigh = malloc(sizeof(struct neighbour));
if(neigh == NULL) {
flog_err(EC_BABEL_MEMORY, "malloc(neighbour): %s",
safe_strerror(errno));
return NULL;
}
neigh->hello_seqno = -1;
memcpy(neigh->address, address, 16);
neigh->reach = 0;
neigh->txcost = INFINITY;
neigh->ihu_time = babel_now;
neigh->hello_time = zero;
neigh->hello_interval = 0;
neigh->ihu_interval = 0;
neigh->hello_send_us = 0;
neigh->hello_rtt_receive_time = zero;
neigh->rtt = 0;
neigh->rtt_time = zero;
neigh->ifp = ifp;
neigh->next = neighs;
neighs = neigh;
send_hello(ifp);
return neigh;
}
/* Recompute a neighbour's rxcost. Return true if anything changed. */
int update_neighbour(struct neighbour *neigh, int hello, int hello_interval)
{
int missed_hellos;
int rc = 0;
if(hello < 0) {
if(neigh->hello_interval == 0)
return rc;
missed_hellos =
((int)timeval_minus_msec(&babel_now, &neigh->hello_time) -
neigh->hello_interval * 7) /
(neigh->hello_interval * 10);
if(missed_hellos <= 0)
return rc;
timeval_add_msec(&neigh->hello_time, &neigh->hello_time,
missed_hellos * neigh->hello_interval * 10);
} else {
if(neigh->hello_seqno >= 0 && neigh->reach > 0) {
missed_hellos = seqno_minus(hello, neigh->hello_seqno) - 1;
if(missed_hellos < -8) {
/* Probably a neighbour that rebooted and lost its seqno.
Reboot the universe. */
neigh->reach = 0;
missed_hellos = 0;
rc = 1;
} else if(missed_hellos < 0) {
if(hello_interval > neigh->hello_interval) {
/* This neighbour has increased its hello interval,
and we didn't notice. */
neigh->reach <<= -missed_hellos;
missed_hellos = 0;
} else {
/* Late hello. Probably due to the link layer buffering
packets during a link outage. Ignore it, but reset
the expected seqno. */
neigh->hello_seqno = hello;
hello = -1;
missed_hellos = 0;
}
rc = 1;
}
} else {
missed_hellos = 0;
}
neigh->hello_time = babel_now;
neigh->hello_interval = hello_interval;
}
if(missed_hellos > 0) {
neigh->reach >>= missed_hellos;
neigh->hello_seqno = seqno_plus(neigh->hello_seqno, missed_hellos);
rc = 1;
}
if(hello >= 0) {
neigh->hello_seqno = hello;
neigh->reach >>= 1;
SET_FLAG(neigh->reach, 0x8000);
if(CHECK_FLAG(neigh->reach, 0xFC00) != 0xFC00)
rc = 1;
}
/* Make sure to give neighbours some feedback early after association */
if(CHECK_FLAG(neigh->reach, 0xBF00) == 0x8000) {
/* A new neighbour */
send_hello(neigh->ifp);
} else {
/* Don't send hellos, in order to avoid a positive feedback loop. */
int a = CHECK_FLAG(neigh->reach, 0xC000);
int b = CHECK_FLAG(neigh->reach, 0x3000);
if((a == 0xC000 && b == 0) || (a == 0 && b == 0x3000)) {
/* Reachability is either 1100 or 0011 */
send_self_update(neigh->ifp);
}
}
if(CHECK_FLAG(neigh->reach, 0xFC00) == 0xC000) {
/* This is a newish neighbour, let's request a full route dump.
We ought to avoid this when the network is dense */
send_unicast_request(neigh, NULL, 0);
send_ihu(neigh, NULL);
}
return rc;
}
static int reset_txcost(struct neighbour *neigh)
{
unsigned delay;
delay = timeval_minus_msec(&babel_now, &neigh->ihu_time);
if(neigh->ihu_interval > 0 && delay < neigh->ihu_interval * 10U * 3U)
return 0;
/* If we're losing a lot of packets, we probably lost an IHU too */
if (delay >= 180000 || CHECK_FLAG(neigh->reach, 0xFFF0) == 0 ||
(neigh->ihu_interval > 0 && delay >= neigh->ihu_interval * 10U * 10U)) {
neigh->txcost = INFINITY;
neigh->ihu_time = babel_now;
return 1;
}
return 0;
}
unsigned neighbour_txcost(struct neighbour *neigh)
{
return neigh->txcost;
}
unsigned check_neighbours(void)
{
struct neighbour *neigh;
int changed, rc;
unsigned msecs = 50000;
debugf(BABEL_DEBUG_COMMON,"Checking neighbours.");
neigh = neighs;
while(neigh) {
changed = update_neighbour(neigh, -1, 0);
if(neigh->reach == 0 ||
neigh->hello_time.tv_sec > babel_now.tv_sec || /* clock stepped */
timeval_minus_msec(&babel_now, &neigh->hello_time) > 300000) {
struct neighbour *old = neigh;
neigh = neigh->next;
flush_neighbour(old);
continue;
}
rc = reset_txcost(neigh);
changed = changed || rc;
update_neighbour_metric(neigh, changed);
if(neigh->hello_interval > 0)
msecs = MIN(msecs, neigh->hello_interval * 10U);
if(neigh->ihu_interval > 0)
msecs = MIN(msecs, neigh->ihu_interval * 10U);
neigh = neigh->next;
}
return msecs;
}
unsigned neighbour_rxcost(struct neighbour *neigh)
{
unsigned delay;
unsigned short reach = neigh->reach;
delay = timeval_minus_msec(&babel_now, &neigh->hello_time);
if(CHECK_FLAG(reach, 0xFFF0) == 0 || delay >= 180000) {
return INFINITY;
} else if (CHECK_FLAG(babel_get_if_nfo(neigh->ifp)->flags, BABEL_IF_LQ)) {
int sreach =
(CHECK_FLAG(reach, 0x8000) >> 2) +
(CHECK_FLAG(reach, 0x4000) >> 1) +
CHECK_FLAG(reach, 0x3FFF);
/* 0 <= sreach <= 0x7FFF */
int cost = (0x8000 * babel_get_if_nfo(neigh->ifp)->cost) / (sreach + 1);
/* cost >= interface->cost */
if(delay >= 40000)
cost = (cost * (delay - 20000) + 10000) / 20000;
return MIN(cost, INFINITY);
} else {
/* To lose one hello is a misfortune, to lose two is carelessness. */
if (CHECK_FLAG(reach, 0xC000) == 0xC000)
return babel_get_if_nfo(neigh->ifp)->cost;
else if (CHECK_FLAG(reach, 0xC000) == 0)
return INFINITY;
else if (CHECK_FLAG(reach, 0x2000))
return babel_get_if_nfo(neigh->ifp)->cost;
else
return INFINITY;
}
}
unsigned neighbour_rttcost(struct neighbour *neigh)
{
struct interface *ifp = neigh->ifp;
babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
if(!babel_ifp->max_rtt_penalty || !valid_rtt(neigh))
return 0;
/* Function: linear behaviour between rtt_min and rtt_max. */
if(neigh->rtt <= babel_ifp->rtt_min) {
return 0;
} else if(neigh->rtt <= babel_ifp->rtt_max) {
unsigned long long tmp =
(unsigned long long)babel_ifp->max_rtt_penalty *
(neigh->rtt - babel_ifp->rtt_min) /
(babel_ifp->rtt_max - babel_ifp->rtt_min);
assert(CHECK_FLAG(tmp, 0x7FFFFFFF) == tmp);
return tmp;
} else {
return babel_ifp->max_rtt_penalty;
}
}
unsigned neighbour_cost(struct neighbour *neigh)
{
unsigned a, b, cost;
if(!if_up(neigh->ifp))
return INFINITY;
a = neighbour_txcost(neigh);
if(a >= INFINITY)
return INFINITY;
b = neighbour_rxcost(neigh);
if(b >= INFINITY)
return INFINITY;
if (!CHECK_FLAG(babel_get_if_nfo(neigh->ifp)->flags, BABEL_IF_LQ)
|| (a < 256 && b < 256)) {
cost = a;
} else {
/* a = 256/alpha, b = 256/beta, where alpha and beta are the expected
probabilities of a packet getting through in the direct and reverse
directions. */
a = MAX(a, 256);
b = MAX(b, 256);
/* 1/(alpha * beta), which is just plain ETX. */
/* Since a and b are capped to 16 bits, overflow is impossible. */
cost = (a * b + 128) >> 8;
}
cost += neighbour_rttcost(neigh);
return MIN(cost, INFINITY);
}
int valid_rtt(struct neighbour *neigh)
{
return (timeval_minus_msec(&babel_now, &neigh->rtt_time) < 180000) ? 1 : 0;
}

47
babeld/neighbour.h Normal file
View file

@ -0,0 +1,47 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
*/
#ifndef BABEL_NEIGHBOUR_H
#define BABEL_NEIGHBOUR_H
struct neighbour {
struct neighbour *next;
/* This is -1 when unknown, so don't make it unsigned */
int hello_seqno;
unsigned char address[16];
unsigned short reach;
unsigned short txcost;
struct timeval hello_time;
struct timeval ihu_time;
unsigned short hello_interval; /* in centiseconds */
unsigned short ihu_interval; /* in centiseconds */
/* Used for RTT estimation. */
/* Absolute time (modulo 2^32) at which the Hello was sent,
according to remote clock. */
unsigned int hello_send_us;
struct timeval hello_rtt_receive_time;
unsigned int rtt;
struct timeval rtt_time;
struct interface *ifp;
};
extern struct neighbour *neighs;
#define FOR_ALL_NEIGHBOURS(_neigh) \
for(_neigh = neighs; _neigh; _neigh = _neigh->next)
int neighbour_valid(struct neighbour *neigh);
void flush_neighbour(struct neighbour *neigh);
struct neighbour *find_neighbour(const unsigned char *address,
struct interface *ifp);
int update_neighbour(struct neighbour *neigh, int hello, int hello_interval);
unsigned check_neighbours(void);
unsigned neighbour_txcost(struct neighbour *neigh);
unsigned neighbour_rxcost(struct neighbour *neigh);
unsigned neighbour_rttcost(struct neighbour *neigh);
unsigned neighbour_cost(struct neighbour *neigh);
int valid_rtt(struct neighbour *neigh);
#endif /* BABEL_NEIGHBOUR_H */

205
babeld/net.c Normal file
View file

@ -0,0 +1,205 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <errno.h>
#include "babeld.h"
#include "util.h"
#include "net.h"
#include "sockopt.h"
int
babel_socket(int port)
{
struct sockaddr_in6 sin6;
int s, rc;
int saved_errno;
int one = 1, zero = 0;
s = socket(PF_INET6, SOCK_DGRAM, 0);
if(s < 0)
return -1;
rc = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
if(rc < 0)
goto fail;
rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if(rc < 0)
goto fail;
rc = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
&zero, sizeof(zero));
if(rc < 0)
goto fail;
rc = setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
&one, sizeof(one));
if(rc < 0)
goto fail;
rc = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
&one, sizeof(one));
if(rc < 0)
goto fail;
setsockopt_ipv6_tclass (s, IPTOS_PREC_INTERNETCONTROL);
rc = fcntl(s, F_GETFL, 0);
if(rc < 0)
goto fail;
rc = fcntl(s, F_SETFL, (rc | O_NONBLOCK));
if(rc < 0)
goto fail;
rc = fcntl(s, F_GETFD, 0);
if(rc < 0)
goto fail;
rc = fcntl(s, F_SETFD, rc | FD_CLOEXEC);
if(rc < 0)
goto fail;
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_port = htons(port);
rc = bind(s, (struct sockaddr*)&sin6, sizeof(sin6));
if(rc < 0)
goto fail;
return s;
fail:
saved_errno = errno;
close(s);
errno = saved_errno;
return -1;
}
int
babel_recv(int s, void *buf, int buflen, struct sockaddr *sin, int slen)
{
struct iovec iovec;
struct msghdr msg;
int rc;
memset(&msg, 0, sizeof(msg));
iovec.iov_base = buf;
iovec.iov_len = buflen;
msg.msg_name = sin;
msg.msg_namelen = slen;
msg.msg_iov = &iovec;
msg.msg_iovlen = 1;
rc = recvmsg(s, &msg, 0);
return rc;
}
int
babel_send(int s,
void *buf1, int buflen1, void *buf2, int buflen2,
struct sockaddr *sin, int slen)
{
struct iovec iovec[2];
struct msghdr msg;
int rc;
iovec[0].iov_base = buf1;
iovec[0].iov_len = buflen1;
iovec[1].iov_base = buf2;
iovec[1].iov_len = buflen2;
memset(&msg, 0, sizeof(msg));
msg.msg_name = sin;
msg.msg_namelen = slen;
msg.msg_iov = iovec;
msg.msg_iovlen = 2;
again:
rc = sendmsg(s, &msg, 0);
if(rc < 0) {
if(errno == EINTR)
goto again;
else if(errno == EAGAIN) {
int rc2;
rc2 = wait_for_fd(1, s, 5);
if(rc2 > 0)
goto again;
errno = EAGAIN;
}
}
return rc;
}
int
tcp_server_socket(int port, int local)
{
struct sockaddr_in6 sin6;
int s, rc, saved_errno;
int one = 1;
s = socket(PF_INET6, SOCK_STREAM, 0);
if(s < 0)
return -1;
rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if(rc < 0)
goto fail;
rc = fcntl(s, F_GETFL, 0);
if(rc < 0)
goto fail;
rc = fcntl(s, F_SETFL, (rc | O_NONBLOCK));
if(rc < 0)
goto fail;
rc = fcntl(s, F_GETFD, 0);
if(rc < 0)
goto fail;
rc = fcntl(s, F_SETFD, rc | FD_CLOEXEC);
if(rc < 0)
goto fail;
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_port = htons(port);
if(local) {
rc = inet_pton(AF_INET6, "::1", &sin6.sin6_addr);
if(rc < 0)
goto fail;
}
rc = bind(s, (struct sockaddr*)&sin6, sizeof(sin6));
if(rc < 0)
goto fail;
rc = listen(s, 2);
if(rc < 0)
goto fail;
return s;
fail:
saved_errno = errno;
close(s);
errno = saved_errno;
return -1;
}

16
babeld/net.h Normal file
View file

@ -0,0 +1,16 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
*/
#ifndef BABEL_NET_H
#define BABEL_NET_H
int babel_socket(int port);
int babel_recv(int s, void *buf, int buflen, struct sockaddr *sin, int slen);
int babel_send(int s,
void *buf1, int buflen1, void *buf2, int buflen2,
struct sockaddr *sin, int slen);
int tcp_server_socket(int port, int local);
#endif /* BABEL_NET_H */

301
babeld/resend.c Normal file
View file

@ -0,0 +1,301 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/time.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <zebra.h>
#include "if.h"
#include "babel_main.h"
#include "babeld.h"
#include "util.h"
#include "neighbour.h"
#include "resend.h"
#include "message.h"
#include "babel_interface.h"
struct timeval resend_time = {0, 0};
struct resend *to_resend = NULL;
static int
resend_match(struct resend *resend,
int kind, const unsigned char *prefix, unsigned char plen)
{
return (resend->kind == kind &&
resend->plen == plen && memcmp(resend->prefix, prefix, 16) == 0);
}
/* This is called by neigh.c when a neighbour is flushed */
void
flush_resends(struct neighbour *neigh)
{
/* Nothing for now */
}
static struct resend *
find_resend(int kind, const unsigned char *prefix, unsigned char plen,
struct resend **previous_return)
{
struct resend *current, *previous;
previous = NULL;
current = to_resend;
while(current) {
if(resend_match(current, kind, prefix, plen)) {
if(previous_return)
*previous_return = previous;
return current;
}
previous = current;
current = current->next;
}
return NULL;
}
struct resend *
find_request(const unsigned char *prefix, unsigned char plen,
struct resend **previous_return)
{
return find_resend(RESEND_REQUEST, prefix, plen, previous_return);
}
int
record_resend(int kind, const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id,
struct interface *ifp, int delay)
{
struct resend *resend;
unsigned int ifindex = ifp ? ifp->ifindex : 0;
if((kind == RESEND_REQUEST &&
input_filter(NULL, prefix, plen, NULL, ifindex) >= INFINITY) ||
(kind == RESEND_UPDATE &&
output_filter(NULL, prefix, plen, ifindex) >= INFINITY))
return 0;
if(delay >= 0xFFFF)
delay = 0xFFFF;
resend = find_resend(kind, prefix, plen, NULL);
if(resend) {
if(resend->delay && delay)
resend->delay = MIN(resend->delay, delay);
else if(delay)
resend->delay = delay;
resend->time = babel_now;
resend->max = RESEND_MAX;
if(id && memcmp(resend->id, id, 8) == 0 &&
seqno_compare(resend->seqno, seqno) > 0) {
return 0;
}
if(id)
memcpy(resend->id, id, 8);
else
memset(resend->id, 0, 8);
resend->seqno = seqno;
if(resend->ifp != ifp)
resend->ifp = NULL;
} else {
resend = malloc(sizeof(struct resend));
if(resend == NULL)
return -1;
resend->kind = kind;
resend->max = RESEND_MAX;
resend->delay = delay;
memcpy(resend->prefix, prefix, 16);
resend->plen = plen;
resend->seqno = seqno;
if(id)
memcpy(resend->id, id, 8);
else
memset(resend->id, 0, 8);
resend->ifp = ifp;
resend->time = babel_now;
resend->next = to_resend;
to_resend = resend;
}
if(resend->delay) {
struct timeval timeout;
timeval_add_msec(&timeout, &resend->time, resend->delay);
timeval_min(&resend_time, &timeout);
}
return 1;
}
static int
resend_expired(struct resend *resend)
{
switch(resend->kind) {
case RESEND_REQUEST:
return timeval_minus_msec(&babel_now, &resend->time) >= REQUEST_TIMEOUT;
default:
return resend->max <= 0;
}
}
int
unsatisfied_request(const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id)
{
struct resend *request;
request = find_request(prefix, plen, NULL);
if(request == NULL || resend_expired(request))
return 0;
if(memcmp(request->id, id, 8) != 0 ||
seqno_compare(request->seqno, seqno) <= 0)
return 1;
return 0;
}
/* Determine whether a given request should be forwarded. */
int
request_redundant(struct interface *ifp,
const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id)
{
struct resend *request;
request = find_request(prefix, plen, NULL);
if(request == NULL || resend_expired(request))
return 0;
if(memcmp(request->id, id, 8) == 0 &&
seqno_compare(request->seqno, seqno) > 0)
return 0;
if(request->ifp != NULL && request->ifp != ifp)
return 0;
if(request->max > 0)
/* Will be resent. */
return 1;
if(timeval_minus_msec(&babel_now, &request->time) <
(ifp ? MIN(babel_get_if_nfo(ifp)->hello_interval, 1000) : 1000))
/* Fairly recent. */
return 1;
return 0;
}
int
satisfy_request(const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id,
struct interface *ifp)
{
struct resend *request, *previous;
request = find_request(prefix, plen, &previous);
if(request == NULL)
return 0;
if(ifp != NULL && request->ifp != ifp)
return 0;
if(memcmp(request->id, id, 8) != 0 ||
seqno_compare(request->seqno, seqno) <= 0) {
/* We cannot remove the request, as we may be walking the list right
now. Mark it as expired, so that expire_resend will remove it. */
request->max = 0;
request->time.tv_sec = 0;
recompute_resend_time();
return 1;
}
return 0;
}
void
expire_resend(void)
{
struct resend *current, *previous;
int recompute = 0;
previous = NULL;
current = to_resend;
while(current) {
if(resend_expired(current)) {
if(previous == NULL) {
to_resend = current->next;
free(current);
current = to_resend;
} else {
previous->next = current->next;
free(current);
current = previous->next;
}
recompute = 1;
} else {
previous = current;
current = current->next;
}
}
if(recompute)
recompute_resend_time();
}
void
recompute_resend_time(void)
{
struct resend *request;
struct timeval resend = {0, 0};
request = to_resend;
while(request) {
if(!resend_expired(request) && request->delay > 0 && request->max > 0) {
struct timeval timeout;
timeval_add_msec(&timeout, &request->time, request->delay);
timeval_min(&resend, &timeout);
}
request = request->next;
}
resend_time = resend;
}
void
do_resend(void)
{
struct resend *resend;
resend = to_resend;
while(resend) {
if(!resend_expired(resend) && resend->delay > 0 && resend->max > 0) {
struct timeval timeout;
timeval_add_msec(&timeout, &resend->time, resend->delay);
if(timeval_compare(&babel_now, &timeout) >= 0) {
switch(resend->kind) {
case RESEND_REQUEST:
send_multihop_request(resend->ifp,
resend->prefix, resend->plen,
resend->seqno, resend->id, 127);
break;
case RESEND_UPDATE:
send_update(resend->ifp, 1,
resend->prefix, resend->plen);
break;
default: abort();
}
resend->delay = MIN(0xFFFF, resend->delay * 2);
resend->max--;
}
}
resend = resend->next;
}
recompute_resend_time();
}

48
babeld/resend.h Normal file
View file

@ -0,0 +1,48 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
*/
#ifndef BABEL_RESEND_H
#define BABEL_RESEND_H
#define REQUEST_TIMEOUT 65000
#define RESEND_MAX 3
#define RESEND_REQUEST 1
#define RESEND_UPDATE 2
struct resend {
unsigned char kind;
unsigned char max;
unsigned short delay;
struct timeval time;
unsigned char prefix[16];
unsigned char plen;
unsigned short seqno;
unsigned char id[8];
struct interface *ifp;
struct resend *next;
};
extern struct timeval resend_time;
struct resend *find_request(const unsigned char *prefix, unsigned char plen,
struct resend **previous_return);
void flush_resends(struct neighbour *neigh);
int record_resend(int kind, const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id,
struct interface *ifp, int delay);
int unsatisfied_request(const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id);
int request_redundant(struct interface *ifp,
const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id);
int satisfy_request(const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id,
struct interface *ifp);
void expire_resend(void);
void recompute_resend_time(void);
void do_resend(void);
#endif /* BABEL_RESEND_H */

1129
babeld/route.c Normal file

File diff suppressed because it is too large Load diff

112
babeld/route.h Normal file
View file

@ -0,0 +1,112 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
*/
#ifndef BABEL_ROUTE_H
#define BABEL_ROUTE_H
#include "babel_interface.h"
#include "source.h"
enum babel_diversity {
DIVERSITY_NONE,
DIVERSITY_INTERFACE_1,
DIVERSITY_CHANNEL_1,
DIVERSITY_CHANNEL,
};
#define DIVERSITY_HOPS 8
struct babel_route {
struct source *src;
unsigned short refmetric;
unsigned short cost;
unsigned short add_metric;
unsigned short seqno;
struct neighbour *neigh;
unsigned char nexthop[16];
time_t time;
unsigned short hold_time; /* in seconds */
unsigned short smoothed_metric; /* for route selection */
time_t smoothed_metric_time;
short installed;
unsigned char channels[DIVERSITY_HOPS];
struct babel_route *next;
};
struct route_stream;
extern struct babel_route **routes;
extern int kernel_metric;
extern enum babel_diversity diversity_kind;
extern int diversity_factor;
extern int keep_unfeasible;
extern int smoothing_half_life;
static inline int
route_metric(const struct babel_route *route)
{
int m = (int)route->refmetric + route->cost + route->add_metric;
return MIN(m, INFINITY);
}
static inline int
route_metric_noninterfering(const struct babel_route *route)
{
int m =
(int)route->refmetric +
(diversity_factor * route->cost + 128) / 256 +
route->add_metric;
m = MAX(m, route->refmetric + 1);
return MIN(m, INFINITY);
}
struct babel_route *find_route(const unsigned char *prefix, unsigned char plen,
struct neighbour *neigh, const unsigned char *nexthop);
struct babel_route *find_installed_route(const unsigned char *prefix,
unsigned char plen);
int installed_routes_estimate(void);
void flush_route(struct babel_route *route);
void flush_all_routes(void);
void flush_neighbour_routes(struct neighbour *neigh);
void flush_interface_routes(struct interface *ifp, int v4only);
struct route_stream *route_stream(int installed);
struct babel_route *route_stream_next(struct route_stream *stream);
void route_stream_done(struct route_stream *stream);
void install_route(struct babel_route *route);
void uninstall_route(struct babel_route *route);
int route_feasible(struct babel_route *route);
int route_old(struct babel_route *route);
int route_expired(struct babel_route *route);
int route_interferes(struct babel_route *route, struct interface *ifp);
int update_feasible(struct source *src,
unsigned short seqno, unsigned short refmetric);
void change_smoothing_half_life(int half_life);
int route_smoothed_metric(struct babel_route *route);
struct babel_route *find_best_route(const unsigned char *prefix, unsigned char plen,
int feasible, struct neighbour *exclude);
struct babel_route *install_best_route(const unsigned char prefix[16],
unsigned char plen);
void update_neighbour_metric(struct neighbour *neigh, int change);
void update_interface_metric(struct interface *ifp);
void update_route_metric(struct babel_route *route);
struct babel_route *update_route(const unsigned char *id,
const unsigned char *prefix, unsigned char plen,
unsigned short seqno, unsigned short refmetric,
unsigned short interval, struct neighbour *neigh,
const unsigned char *nexthop,
const unsigned char *channels, int channels_len);
void retract_neighbour_routes(struct neighbour *neigh);
void send_unfeasible_request(struct neighbour *neigh, int force,
unsigned short seqno, unsigned short metric,
struct source *src);
void send_triggered_update(struct babel_route *route,
struct source *oldsrc, unsigned oldmetric);
void route_changed(struct babel_route *route,
struct source *oldsrc, unsigned short oldmetric);
void route_lost(struct source *src, unsigned oldmetric);
void expire_routes(void);
#endif

152
babeld/source.c Normal file
View file

@ -0,0 +1,152 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include "babel_main.h"
#include "babeld.h"
#include "util.h"
#include "source.h"
#include "babel_interface.h"
#include "route.h"
#include "babel_errors.h"
struct source *srcs = NULL;
struct source*
find_source(const unsigned char *id, const unsigned char *p, unsigned char plen,
int create, unsigned short seqno)
{
struct source *src;
for(src = srcs; src; src = src->next) {
/* This should really be a hash table. For now, check the
last byte first. */
if(src->id[7] != id[7])
continue;
if(memcmp(src->id, id, 8) != 0)
continue;
if(src->plen != plen)
continue;
if(memcmp(src->prefix, p, 16) == 0)
return src;
}
if(!create)
return NULL;
src = malloc(sizeof(struct source));
if(src == NULL) {
flog_err(EC_BABEL_MEMORY, "malloc(source): %s", safe_strerror(errno));
return NULL;
}
memcpy(src->id, id, 8);
memcpy(src->prefix, p, 16);
src->plen = plen;
src->seqno = seqno;
src->metric = INFINITY;
src->time = babel_now.tv_sec;
src->route_count = 0;
src->next = srcs;
srcs = src;
return src;
}
struct source *
retain_source(struct source *src)
{
assert(src->route_count < 0xffff);
src->route_count++;
return src;
}
void
release_source(struct source *src)
{
assert(src->route_count > 0);
src->route_count--;
}
int
flush_source(struct source *src)
{
if(src->route_count > 0)
/* The source is in use by a route. */
return 0;
if(srcs == src) {
srcs = src->next;
} else {
struct source *previous = srcs;
while(previous->next != src)
previous = previous->next;
previous->next = src->next;
}
free(src);
return 1;
}
void
update_source(struct source *src,
unsigned short seqno, unsigned short metric)
{
if(metric >= INFINITY)
return;
/* If a source is expired, pretend that it doesn't exist and update
it unconditionally. This makes ensures that old data will
eventually be overridden, and prevents us from getting stuck if
a router loses its sequence number. */
if(src->time < babel_now.tv_sec - SOURCE_GC_TIME ||
seqno_compare(src->seqno, seqno) < 0 ||
(src->seqno == seqno && src->metric > metric)) {
src->seqno = seqno;
src->metric = metric;
}
src->time = babel_now.tv_sec;
}
void
expire_sources(void)
{
struct source *src;
src = srcs;
while(src) {
if(src->time > babel_now.tv_sec)
/* clock stepped */
src->time = babel_now.tv_sec;
if(src->time < babel_now.tv_sec - SOURCE_GC_TIME) {
struct source *old = src;
src = src->next;
flush_source(old);
continue;
}
src = src->next;
}
}
void
check_sources_released(void)
{
struct source *src;
for(src = srcs; src; src = src->next) {
if(src->route_count != 0)
fprintf(stderr, "Warning: source %s %s has refcount %d.\n",
format_eui64(src->id),
format_prefix(src->prefix, src->plen),
(int)src->route_count);
}
}

34
babeld/source.h Normal file
View file

@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
*/
#ifndef BABEL_SOURCE_H
#define BABEL_SOURCE_H
#define SOURCE_GC_TIME 200
struct source {
struct source *next;
unsigned char id[8];
unsigned char prefix[16];
unsigned char plen;
unsigned short seqno;
unsigned short metric;
unsigned short route_count;
time_t time;
};
struct source *find_source(const unsigned char *id,
const unsigned char *p,
unsigned char plen,
int create, unsigned short seqno);
struct source *retain_source(struct source *src);
void release_source(struct source *src);
int flush_source(struct source *src);
void update_source(struct source *src,
unsigned short seqno, unsigned short metric);
void expire_sources(void);
void check_sources_released(void);
#endif

50
babeld/subdir.am Normal file
View file

@ -0,0 +1,50 @@
#
# babeld
#
if BABELD
sbin_PROGRAMS += babeld/babeld
vtysh_daemons += babeld
endif
babeld_babeld_SOURCES = \
babeld/babel_errors.c \
babeld/babel_filter.c \
babeld/babel_interface.c \
babeld/babel_main.c \
babeld/babel_zebra.c \
babeld/babeld.c \
babeld/kernel.c \
babeld/message.c \
babeld/neighbour.c \
babeld/net.c \
babeld/resend.c \
babeld/route.c \
babeld/source.c \
babeld/util.c \
babeld/xroute.c \
# end
noinst_HEADERS += \
babeld/babel_errors.h \
babeld/babel_filter.h \
babeld/babel_interface.h \
babeld/babel_main.h \
babeld/babel_zebra.h \
babeld/babeld.h \
babeld/kernel.h \
babeld/message.h \
babeld/neighbour.h \
babeld/net.h \
babeld/resend.h \
babeld/route.h \
babeld/source.h \
babeld/util.h \
babeld/xroute.h \
# end
clippy_scan += \
babeld/babel_interface.c \
babeld/babeld.c
babeld_babeld_LDADD = lib/libfrr.la $(LIBCAP)

429
babeld/util.c Normal file
View file

@ -0,0 +1,429 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "lib/network.h"
#include "babel_main.h"
#include "babeld.h"
#include "util.h"
int
roughly(int value)
{
if(value < 0)
return -roughly(-value);
else if(value <= 1)
return value;
else
return value * 3 / 4 + frr_weak_random() % (value / 2);
}
/* d = s1 - s2 */
void
timeval_minus(struct timeval *d,
const struct timeval *s1, const struct timeval *s2)
{
if(s1->tv_usec >= s2->tv_usec) {
d->tv_usec = s1->tv_usec - s2->tv_usec;
d->tv_sec = s1->tv_sec - s2->tv_sec;
} else {
d->tv_usec = s1->tv_usec + 1000000 - s2->tv_usec;
d->tv_sec = s1->tv_sec - s2->tv_sec - 1;
}
}
unsigned
timeval_minus_msec(const struct timeval *s1, const struct timeval *s2)
{
if(s1->tv_sec < s2->tv_sec)
return 0;
/* Avoid overflow. */
if(s1->tv_sec - s2->tv_sec > 2000000)
return 2000000000;
if(s1->tv_sec > s2->tv_sec)
return
(unsigned)((unsigned)(s1->tv_sec - s2->tv_sec) * 1000 +
((int)s1->tv_usec - s2->tv_usec) / 1000);
if(s1->tv_usec <= s2->tv_usec)
return 0;
return (unsigned)(s1->tv_usec - s2->tv_usec) / 1000u;
}
/* d = s + msecs */
void
timeval_add_msec(struct timeval *d, const struct timeval *s, int msecs)
{
int usecs;
d->tv_sec = s->tv_sec + msecs / 1000;
usecs = s->tv_usec + (msecs % 1000) * 1000;
if(usecs < 1000000) {
d->tv_usec = usecs;
} else {
d->tv_usec = usecs - 1000000;
d->tv_sec++;
}
}
void
set_timeout(struct timeval *timeout, int msecs)
{
timeval_add_msec(timeout, &babel_now, roughly(msecs));
}
/* returns <0 if "s1" < "s2", etc. */
int
timeval_compare(const struct timeval *s1, const struct timeval *s2)
{
if(s1->tv_sec < s2->tv_sec)
return -1;
else if(s1->tv_sec > s2->tv_sec)
return 1;
else if(s1->tv_usec < s2->tv_usec)
return -1;
else if(s1->tv_usec > s2->tv_usec)
return 1;
else
return 0;
}
/* set d at min(d, s) */
/* {0, 0} represents infinity */
void
timeval_min(struct timeval *d, const struct timeval *s)
{
if(s->tv_sec == 0)
return;
if(d->tv_sec == 0 || timeval_compare(d, s) > 0) {
*d = *s;
}
}
/* set d to min(d, x) with x in [secs, secs+1] */
void
timeval_min_sec(struct timeval *d, time_t secs)
{
if(d->tv_sec == 0 || d->tv_sec > secs) {
d->tv_sec = secs;
d->tv_usec = frr_weak_random() % 1000000;
}
}
/* parse a float value in second and return the corresponding mili-seconds.
For example:
parse_msec("12.342345") returns 12342 */
int
parse_msec(const char *string)
{
unsigned int in, fl;
int i, j;
in = fl = 0;
i = 0;
while(string[i] == ' ' || string[i] == '\t')
i++;
while(string[i] >= '0' && string[i] <= '9') {
in = in * 10 + string[i] - '0';
i++;
}
if(string[i] == '.') {
i++;
j = 0;
while(string[i] >= '0' && string[i] <= '9') {
fl = fl * 10 + string[i] - '0';
i++;
j++;
}
while(j > 3) {
fl /= 10;
j--;
}
while(j < 3) {
fl *= 10;
j++;
}
}
while(string[i] == ' ' || string[i] == '\t')
i++;
if(string[i] == '\0')
return in * 1000 + fl;
return -1;
}
/* There's no good name for a positive int in C, call it nat. */
int
parse_nat(const char *string)
{
long l;
char *end;
l = strtol(string, &end, 0);
while(*end == ' ' || *end == '\t')
end++;
if(*end != '\0')
return -1;
if(l < 0 || l > INT_MAX)
return -1;
return (int)l;
}
unsigned char *
mask_prefix(unsigned char *restrict ret,
const unsigned char *restrict prefix, unsigned char plen)
{
if (plen >= IPV6_MAX_BITLEN) {
memcpy(ret, prefix, IPV6_MAX_BYTELEN);
return ret;
}
memset(ret, 0, 16);
memcpy(ret, prefix, plen / 8);
if(plen % 8 != 0)
ret[plen / 8] =
(prefix[plen / 8] & ((0xFF << (8 - (plen % 8))) & 0xFF));
return ret;
}
const unsigned char v4prefix[16] =
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 };
static const unsigned char llprefix[16] =
{0xFE, 0x80};
const char *
format_address(const unsigned char *address)
{
static char buf[4][INET6_ADDRSTRLEN];
static int i = 0;
i = (i + 1) % 4;
if(v4mapped(address))
inet_ntop(AF_INET, address + 12, buf[i], INET6_ADDRSTRLEN);
else
inet_ntop(AF_INET6, address, buf[i], INET6_ADDRSTRLEN);
return buf[i];
}
const char *
format_prefix(const unsigned char *prefix, unsigned char plen)
{
static char buf[4][INET6_ADDRSTRLEN + 4];
static int i = 0;
int n;
i = (i + 1) % 4;
if(plen >= 96 && v4mapped(prefix)) {
inet_ntop(AF_INET, prefix + 12, buf[i], INET6_ADDRSTRLEN);
n = strlen(buf[i]);
snprintf(buf[i] + n, INET6_ADDRSTRLEN + 4 - n, "/%d", plen - 96);
} else {
inet_ntop(AF_INET6, prefix, buf[i], INET6_ADDRSTRLEN);
n = strlen(buf[i]);
snprintf(buf[i] + n, INET6_ADDRSTRLEN + 4 - n, "/%d", plen);
}
return buf[i];
}
const char *
format_eui64(const unsigned char *eui)
{
static char buf[4][28];
static int i = 0;
i = (i + 1) % 4;
snprintf(buf[i], 28, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
eui[0], eui[1], eui[2], eui[3],
eui[4], eui[5], eui[6], eui[7]);
return buf[i];
}
const char *
format_thousands(unsigned int value)
{
static char buf[4][15];
static int i = 0;
i = (i + 1) % 4;
snprintf(buf[i], 15, "%u.%.3u", value / 1000, value % 1000);
return buf[i];
}
int
parse_address(const char *address, unsigned char *addr_r, int *af_r)
{
struct in_addr ina;
struct in6_addr ina6;
int rc;
rc = inet_pton(AF_INET, address, &ina);
if(rc > 0) {
v4tov6(addr_r, (const unsigned char *)&ina);
if(af_r) *af_r = AF_INET;
return 0;
}
rc = inet_pton(AF_INET6, address, &ina6);
if(rc > 0) {
memcpy(addr_r, &ina6, IPV6_MAX_BYTELEN);
if (af_r)
*af_r = AF_INET6;
return 0;
}
return -1;
}
int
parse_eui64(const char *eui, unsigned char *eui_r)
{
int n;
n = sscanf(eui, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
&eui_r[0], &eui_r[1], &eui_r[2], &eui_r[3],
&eui_r[4], &eui_r[5], &eui_r[6], &eui_r[7]);
if(n == 8)
return 0;
n = sscanf(eui, "%02hhx-%02hhx-%02hhx-%02hhx-%02hhx-%02hhx-%02hhx-%02hhx",
&eui_r[0], &eui_r[1], &eui_r[2], &eui_r[3],
&eui_r[4], &eui_r[5], &eui_r[6], &eui_r[7]);
if(n == 8)
return 0;
n = sscanf(eui, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
&eui_r[0], &eui_r[1], &eui_r[2],
&eui_r[5], &eui_r[6], &eui_r[7]);
if(n == 6) {
eui_r[3] = 0xFF;
eui_r[4] = 0xFE;
return 0;
}
return -1;
}
int
wait_for_fd(int direction, int fd, int msecs)
{
fd_set fds;
int rc;
struct timeval tv;
tv.tv_sec = msecs / 1000;
tv.tv_usec = (msecs % 1000) * 1000;
FD_ZERO(&fds);
FD_SET(fd, &fds);
if(direction)
rc = select(fd + 1, NULL, &fds, NULL, &tv);
else
rc = select(fd + 1, &fds, NULL, NULL, &tv);
return rc;
}
int
martian_prefix(const unsigned char *prefix, int plen)
{
return
(plen >= 8 && prefix[0] == 0xFF) ||
(plen >= 10 && prefix[0] == 0xFE && (prefix[1] & 0xC0) == 0x80) ||
(plen >= 128 && memcmp(prefix, zeroes, 15) == 0 &&
(prefix[15] == 0 || prefix[15] == 1)) ||
(plen >= 96 && v4mapped(prefix) &&
((plen >= 104 && (prefix[12] == 127 || prefix[12] == 0)) ||
(plen >= 100 && (prefix[12] & 0xE0) == 0xE0)));
}
int
linklocal(const unsigned char *address)
{
return memcmp(address, llprefix, 8) == 0;
}
int
v4mapped(const unsigned char *address)
{
return memcmp(address, v4prefix, 12) == 0;
}
void
v4tov6(unsigned char *dst, const unsigned char *src)
{
memcpy(dst, v4prefix, 12);
memcpy(dst + 12, src, 4);
}
void
inaddr_to_uchar(unsigned char *dest, const struct in_addr *src)
{
v4tov6(dest, (const unsigned char *)src);
assert(v4mapped(dest));
}
void
uchar_to_inaddr(struct in_addr *dest, const unsigned char *src)
{
assert(v4mapped(src));
memcpy(dest, src + 12, 4);
}
void
in6addr_to_uchar(unsigned char *dest, const struct in6_addr *src)
{
memcpy(dest, src, IPV6_MAX_BYTELEN);
}
void
uchar_to_in6addr(struct in6_addr *dest, const unsigned char *src)
{
memcpy(dest, src, IPV6_MAX_BYTELEN);
}
int
daemonise(void)
{
int rc;
fflush(stdout);
fflush(stderr);
rc = fork();
if(rc < 0)
return -1;
if(rc > 0)
exit(0);
rc = setsid();
if(rc < 0)
return -1;
return 1;
}

139
babeld/util.h Normal file
View file

@ -0,0 +1,139 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
*/
#ifndef BABEL_UTIL_H
#define BABEL_UTIL_H
#include "babeld.h"
#include "babel_main.h"
#include "log.h"
#include "memory.h"
DECLARE_MGROUP(BABELD);
#if defined(i386) || defined(__mc68020__) || defined(__x86_64__)
#define DO_NTOHS(_d, _s) do{ _d = ntohs(*(const unsigned short*)(_s)); }while(0)
#define DO_NTOHL(_d, _s) do{ _d = ntohl(*(const unsigned*)(_s)); } while(0)
#define DO_HTONS(_d, _s) do{ *(unsigned short*)(_d) = htons(_s); } while(0)
#define DO_HTONL(_d, _s) do{ *(unsigned*)(_d) = htonl(_s); } while(0)
/* Some versions of gcc seem to be buggy, and ignore the packed attribute.
Disable this code until the issue is clarified. */
/* #elif defined __GNUC__*/
#else
#define DO_NTOHS(_d, _s) \
do { short _dd; \
memcpy(&(_dd), (_s), 2); \
_d = ntohs(_dd); } while(0)
#define DO_NTOHL(_d, _s) \
do { int _dd; \
memcpy(&(_dd), (_s), 4); \
_d = ntohl(_dd); } while(0)
#define DO_HTONS(_d, _s) \
do { unsigned short _dd; \
_dd = htons(_s); \
memcpy((_d), &(_dd), 2); } while(0)
#define DO_HTONL(_d, _s) \
do { unsigned _dd; \
_dd = htonl(_s); \
memcpy((_d), &(_dd), 4); } while(0)
#endif
static inline int
seqno_compare(unsigned short s1, unsigned short s2)
{
if(s1 == s2)
return 0;
else
return ((s2 - s1) & 0x8000) ? 1 : -1;
}
static inline short
seqno_minus(unsigned short s1, unsigned short s2)
{
return (short)((s1 - s2) & 0xFFFF);
}
static inline unsigned short
seqno_plus(unsigned short s, int plus)
{
return ((s + plus) & 0xFFFF);
}
/* Returns a time in microseconds on 32 bits (thus modulo 2^32,
i.e. about 4295 seconds). */
static inline unsigned int
time_us(const struct timeval t)
{
return (unsigned int) (t.tv_sec * 1000000 + t.tv_usec);
}
int roughly(int value);
void timeval_minus(struct timeval *d,
const struct timeval *s1, const struct timeval *s2);
unsigned timeval_minus_msec(const struct timeval *s1, const struct timeval *s2)
ATTRIBUTE ((pure));
void timeval_add_msec(struct timeval *d, const struct timeval *s, int msecs);
void set_timeout (struct timeval *timeout, int msecs);
int timeval_compare(const struct timeval *s1, const struct timeval *s2)
ATTRIBUTE ((pure));
void timeval_min(struct timeval *d, const struct timeval *s);
void timeval_min_sec(struct timeval *d, time_t secs);
int parse_nat(const char *string) ATTRIBUTE ((pure));
int parse_msec(const char *string) ATTRIBUTE ((pure));
unsigned char *mask_prefix(unsigned char *restrict ret,
const unsigned char *restrict prefix,
unsigned char plen);
const char *format_address(const unsigned char *address);
const char *format_prefix(const unsigned char *address, unsigned char prefix);
const char *format_eui64(const unsigned char *eui);
const char *format_thousands(unsigned int value);
int parse_address(const char *address, unsigned char *addr_r, int *af_r);
int parse_eui64(const char *eui, unsigned char *eui_r);
int wait_for_fd(int direction, int fd, int msecs);
int martian_prefix(const unsigned char *prefix, int plen) ATTRIBUTE ((pure));
int linklocal(const unsigned char *address) ATTRIBUTE ((pure));
int v4mapped(const unsigned char *address) ATTRIBUTE ((pure));
void v4tov6(unsigned char *dst, const unsigned char *src);
void inaddr_to_uchar(unsigned char *dest, const struct in_addr *src);
void uchar_to_inaddr(struct in_addr *dest, const unsigned char *src);
void in6addr_to_uchar(unsigned char *dest, const struct in6_addr *src);
void uchar_to_in6addr(struct in6_addr *dest, const unsigned char *src);
int daemonise(void);
extern const unsigned char v4prefix[16];
static inline bool
is_default(const unsigned char *prefix, int plen)
{
return plen == 0 || (plen == 96 && v4mapped(prefix));
}
/* If debugging is disabled, we want to avoid calling format_address
for every omitted debugging message. So debug is a macro. But
vararg macros are not portable. */
#if defined NO_DEBUG
#define debugf(...) do {} while(0)
#else /* NO_DEBUG */
/* some levels */
#define BABEL_DEBUG_COMMON (1 << 0)
#define BABEL_DEBUG_KERNEL (1 << 1)
#define BABEL_DEBUG_FILTER (1 << 2)
#define BABEL_DEBUG_TIMEOUT (1 << 3)
#define BABEL_DEBUG_IF (1 << 4)
#define BABEL_DEBUG_ROUTE (1 << 5)
#define BABEL_DEBUG_ALL (0xFFFF)
#define debugf(level, ...) \
do { \
if (unlikely(debug & level)) \
zlog_debug(__VA_ARGS__); \
} while (0)
#endif /* NO_DEBUG */
#endif /* BABEL_UTIL_H */

214
babeld/xroute.c Normal file
View file

@ -0,0 +1,214 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
*/
#include <zebra.h>
#include "if.h"
#include "log.h"
#include "babeld.h"
#include "kernel.h"
#include "neighbour.h"
#include "message.h"
#include "route.h"
#include "xroute.h"
#include "util.h"
#include "babel_interface.h"
static int xroute_add_new_route(unsigned char prefix[16], unsigned char plen,
unsigned short metric, unsigned int ifindex,
int proto, int send_updates);
static struct xroute *xroutes;
static int numxroutes = 0, maxxroutes = 0;
/* Add redistributed route to Babel table. */
int
babel_route_add (struct zapi_route *api)
{
unsigned char uchar_prefix[16];
switch (api->prefix.family) {
case AF_INET:
inaddr_to_uchar(uchar_prefix, &api->prefix.u.prefix4);
debugf(BABEL_DEBUG_ROUTE, "Adding new ipv4 route coming from Zebra.");
xroute_add_new_route(uchar_prefix, api->prefix.prefixlen + 96,
api->metric, api->nexthops[0].ifindex, 0, 1);
break;
case AF_INET6:
in6addr_to_uchar(uchar_prefix, &api->prefix.u.prefix6);
debugf(BABEL_DEBUG_ROUTE, "Adding new ipv6 route coming from Zebra.");
xroute_add_new_route(uchar_prefix, api->prefix.prefixlen,
api->metric, api->nexthops[0].ifindex, 0, 1);
break;
}
return 0;
}
/* Remove redistributed route from Babel table. */
int
babel_route_delete (struct zapi_route *api)
{
unsigned char uchar_prefix[16];
struct xroute *xroute = NULL;
switch (api->prefix.family) {
case AF_INET:
inaddr_to_uchar(uchar_prefix, &api->prefix.u.prefix4);
xroute = find_xroute(uchar_prefix, api->prefix.prefixlen + 96);
if (xroute != NULL) {
debugf(BABEL_DEBUG_ROUTE, "Removing ipv4 route (from zebra).");
flush_xroute(xroute);
}
break;
case AF_INET6:
in6addr_to_uchar(uchar_prefix, &api->prefix.u.prefix6);
xroute = find_xroute(uchar_prefix, api->prefix.prefixlen);
if (xroute != NULL) {
debugf(BABEL_DEBUG_ROUTE, "Removing ipv6 route (from zebra).");
flush_xroute(xroute);
}
break;
}
return 0;
}
struct xroute *
find_xroute(const unsigned char *prefix, unsigned char plen)
{
int i;
for(i = 0; i < numxroutes; i++) {
if(xroutes[i].plen == plen &&
memcmp(xroutes[i].prefix, prefix, 16) == 0)
return &xroutes[i];
}
return NULL;
}
void
flush_xroute(struct xroute *xroute)
{
int i;
i = xroute - xroutes;
assert(i >= 0 && i < numxroutes);
if(i != numxroutes - 1)
memcpy(xroutes + i, xroutes + numxroutes - 1, sizeof(struct xroute));
numxroutes--;
VALGRIND_MAKE_MEM_UNDEFINED(xroutes + numxroutes, sizeof(struct xroute));
if(numxroutes == 0) {
free(xroutes);
xroutes = NULL;
maxxroutes = 0;
} else if(maxxroutes > 8 && numxroutes < maxxroutes / 4) {
struct xroute *new_xroutes;
int n = maxxroutes / 2;
new_xroutes = realloc(xroutes, n * sizeof(struct xroute));
if(new_xroutes == NULL)
return;
xroutes = new_xroutes;
maxxroutes = n;
}
}
static int
add_xroute(unsigned char prefix[16], unsigned char plen,
unsigned short metric, unsigned int ifindex, int proto)
{
struct xroute *xroute = find_xroute(prefix, plen);
if(xroute) {
if(xroute->metric <= metric)
return 0;
xroute->metric = metric;
return 1;
}
if(numxroutes >= maxxroutes) {
struct xroute *new_xroutes;
int n = maxxroutes < 1 ? 8 : 2 * maxxroutes;
new_xroutes = xroutes == NULL ?
malloc(n * sizeof(struct xroute)) :
realloc(xroutes, n * sizeof(struct xroute));
if(new_xroutes == NULL)
return -1;
maxxroutes = n;
xroutes = new_xroutes;
}
memcpy(xroutes[numxroutes].prefix, prefix, 16);
xroutes[numxroutes].plen = plen;
xroutes[numxroutes].metric = metric;
xroutes[numxroutes].ifindex = ifindex;
xroutes[numxroutes].proto = proto;
numxroutes++;
return 1;
}
/* Returns an overestimate of the number of xroutes. */
int
xroutes_estimate(void)
{
return numxroutes;
}
struct xroute_stream {
int index;
};
struct
xroute_stream *
xroute_stream(void)
{
struct xroute_stream *stream = malloc(sizeof(struct xroute_stream));
if(stream == NULL)
return NULL;
stream->index = 0;
return stream;
}
struct xroute *
xroute_stream_next(struct xroute_stream *stream)
{
if(stream->index < numxroutes)
return &xroutes[stream->index++];
else
return NULL;
}
void
xroute_stream_done(struct xroute_stream *stream)
{
free(stream);
}
/* add an xroute, verifying some conditions; return 0 if there is no changes */
static int
xroute_add_new_route(unsigned char prefix[16], unsigned char plen,
unsigned short metric, unsigned int ifindex,
int proto, int send_updates)
{
int rc;
if(martian_prefix(prefix, plen))
return 0;
metric = redistribute_filter(prefix, plen, ifindex, proto);
if(metric < INFINITY) {
rc = add_xroute(prefix, plen, metric, ifindex, proto);
if(rc > 0) {
struct babel_route *route;
route = find_installed_route(prefix, plen);
if(route)
uninstall_route(route);
if(send_updates)
send_update(NULL, 0, prefix, plen);
return 1;
}
}
return 0;
}

29
babeld/xroute.h Normal file
View file

@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
*/
#ifndef BABEL_XROUTE_H
#define BABEL_XROUTE_H
struct xroute {
unsigned char prefix[16];
unsigned char plen;
unsigned short metric;
unsigned int ifindex;
int proto;
};
struct xroute_stream;
struct xroute *find_xroute(const unsigned char *prefix, unsigned char plen);
void flush_xroute(struct xroute *xroute);
int babel_route_add (struct zapi_route *api);
int babel_route_delete (struct zapi_route *api);
int xroutes_estimate(void);
struct xroute_stream *xroute_stream(void);
struct xroute *xroute_stream_next(struct xroute_stream *stream);
void xroute_stream_done(struct xroute_stream *stream);
#endif /* BABEL_XROUTE_H */

2
bfdd/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
# ignore binary files
bfdd

10
bfdd/Makefile Normal file
View file

@ -0,0 +1,10 @@
all: ALWAYS
@$(MAKE) -s -C .. bfdd/bfdd
%: ALWAYS
@$(MAKE) -s -C .. bfdd/$@
Makefile:
#nothing
ALWAYS:
.PHONY: ALWAYS makefiles
.SUFFIXES:

2069
bfdd/bfd.c Normal file

File diff suppressed because it is too large Load diff

849
bfdd/bfd.h Normal file
View file

@ -0,0 +1,849 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*********************************************************************
* Copyright 2014,2015,2016,2017 Cumulus Networks, Inc. All rights reserved.
*
* bfd.h: implements the BFD protocol.
*/
#ifndef _BFD_H_
#define _BFD_H_
#include <netinet/in.h>
#include <stdbool.h>
#include <stdarg.h>
#include <stdint.h>
#include "lib/hash.h"
#include "lib/libfrr.h"
#include "lib/qobj.h"
#include "lib/queue.h"
#include "lib/vrf.h"
#include "bfdctl.h"
#ifdef BFD_DEBUG
#define BFDD_JSON_CONV_OPTIONS (JSON_C_TO_STRING_PRETTY)
#else
#define BFDD_JSON_CONV_OPTIONS (0)
#endif
DECLARE_MGROUP(BFDD);
DECLARE_MTYPE(BFDD_CONTROL);
DECLARE_MTYPE(BFDD_NOTIFICATION);
#define BFDD_SOCK_NAME "%s/bfdd.sock", frr_runstatedir
/* bfd Authentication Type. */
#define BFD_AUTH_NULL 0
#define BFD_AUTH_SIMPLE 1
#define BFD_AUTH_CRYPTOGRAPHIC 2
struct bfd_timers {
uint32_t desired_min_tx;
uint32_t required_min_rx;
uint32_t required_min_echo;
};
struct bfd_discrs {
uint32_t my_discr;
uint32_t remote_discr;
};
/*
* Format of control packet. From section 4)
*/
struct bfd_pkt {
union {
uint32_t byteFields;
struct {
uint8_t diag;
uint8_t flags;
uint8_t detect_mult;
uint8_t len;
};
};
struct bfd_discrs discrs;
struct bfd_timers timers;
};
/*
* Format of authentification.
*/
struct bfd_auth {
uint8_t type;
uint8_t length;
};
/*
* Format of Echo packet.
*/
struct bfd_echo_pkt {
union {
uint32_t byteFields;
struct {
uint8_t ver;
uint8_t len;
uint16_t reserved;
};
};
uint32_t my_discr;
uint64_t time_sent_sec;
uint64_t time_sent_usec;
};
/* Macros for manipulating control packets */
#define BFD_VERMASK 0x07
#define BFD_DIAGMASK 0x1F
#define BFD_GETVER(diag) ((diag >> 5) & BFD_VERMASK)
#define BFD_SETVER(diag, val) ((diag) |= (val & BFD_VERMASK) << 5)
#define BFD_VERSION 1
#define BFD_PBIT 0x20
#define BFD_FBIT 0x10
#define BFD_CBIT 0x08
#define BFD_ABIT 0x04
#define BFD_DEMANDBIT 0x02
#define BFD_MBIT 0x01
#define BFD_GETMBIT(flags) (flags & BFD_MBIT)
#define BFD_SETDEMANDBIT(flags, val) \
{ \
if ((val)) \
flags |= BFD_DEMANDBIT; \
}
#define BFD_SETPBIT(flags, val) \
{ \
if ((val)) \
flags |= BFD_PBIT; \
}
#define BFD_GETPBIT(flags) (flags & BFD_PBIT)
#define BFD_SETFBIT(flags, val) \
{ \
if ((val)) \
flags |= BFD_FBIT; \
}
#define BFD_GETFBIT(flags) (flags & BFD_FBIT)
#define BFD_SETSTATE(flags, val) \
{ \
if ((val)) \
flags |= (val & 0x3) << 6; \
}
#define BFD_GETSTATE(flags) ((flags >> 6) & 0x3)
#define BFD_SETCBIT(flags, val) \
{ \
if ((val)) \
flags |= val; \
}
#define BFD_GETCBIT(flags) (flags & BFD_CBIT)
#define BFD_ECHO_VERSION 1
#define BFD_ECHO_PKT_LEN sizeof(struct bfd_echo_pkt)
enum bfd_diagnosticis {
BD_OK = 0,
/* Control Detection Time Expired. */
BD_CONTROL_EXPIRED = 1,
/* Echo Function Failed. */
BD_ECHO_FAILED = 2,
/* Neighbor Signaled Session Down. */
BD_NEIGHBOR_DOWN = 3,
/* Forwarding Plane Reset. */
BD_FORWARDING_RESET = 4,
/* Path Down. */
BD_PATH_DOWN = 5,
/* Concatenated Path Down. */
BD_CONCATPATH_DOWN = 6,
/* Administratively Down. */
BD_ADMIN_DOWN = 7,
/* Reverse Concatenated Path Down. */
BD_REVCONCATPATH_DOWN = 8,
/* 9..31: reserved. */
};
/* BFD session flags */
enum bfd_session_flags {
BFD_SESS_FLAG_NONE = 0,
BFD_SESS_FLAG_ECHO = 1 << 0, /* BFD Echo functionality */
BFD_SESS_FLAG_ECHO_ACTIVE = 1 << 1, /* BFD Echo Packets are being sent
* actively
*/
BFD_SESS_FLAG_MH = 1 << 2, /* BFD Multi-hop session */
BFD_SESS_FLAG_IPV6 = 1 << 4, /* BFD IPv6 session */
BFD_SESS_FLAG_SEND_EVT_ACTIVE = 1 << 5, /* send event timer active */
BFD_SESS_FLAG_SEND_EVT_IGNORE = 1 << 6, /* ignore send event when timer
* expires
*/
BFD_SESS_FLAG_SHUTDOWN = 1 << 7, /* disable BGP peer function */
BFD_SESS_FLAG_CONFIG = 1 << 8, /* Session configured with bfd NB API */
BFD_SESS_FLAG_CBIT = 1 << 9, /* CBIT is set */
BFD_SESS_FLAG_PASSIVE = 1 << 10, /* Passive mode */
BFD_SESS_FLAG_MAC_SET = 1 << 11, /* MAC of peer known */
};
/*
* BFD session hash key.
*
* This structure must not have any padding bytes because their value is
* unspecified after the struct assignment. Even when all fields of two keys
* are the same, if the padding bytes are different, then the calculated hash
* value is different, and the hash lookup will fail.
*
* Currently, the structure fields are correctly aligned, and the "packed"
* attribute is added as a precaution. "family" and "mhop" fields are two-bytes
* to eliminate unaligned memory access to "peer" and "local".
*/
struct bfd_key {
uint16_t family;
uint16_t mhop;
struct in6_addr peer;
struct in6_addr local;
char ifname[IFNAMSIZ];
char vrfname[VRF_NAMSIZ];
} __attribute__((packed));
struct bfd_session_stats {
uint64_t rx_ctrl_pkt;
uint64_t tx_ctrl_pkt;
uint64_t rx_echo_pkt;
uint64_t tx_echo_pkt;
uint64_t session_up;
uint64_t session_down;
uint64_t znotification;
};
/**
* BFD session profile to override default configurations.
*/
struct bfd_profile {
/** Profile name. */
char name[64];
/** Session detection multiplier. */
uint8_t detection_multiplier;
/** Desired transmission interval (in microseconds). */
uint32_t min_tx;
/** Minimum required receive interval (in microseconds). */
uint32_t min_rx;
/** Administrative state. */
bool admin_shutdown;
/** Passive mode. */
bool passive;
/** Minimum expected TTL value. */
uint8_t minimum_ttl;
/** Echo mode (only applies to single hop). */
bool echo_mode;
/** Desired echo transmission interval (in microseconds). */
uint32_t min_echo_tx;
/** Minimum required echo receive interval (in microseconds). */
uint32_t min_echo_rx;
/** Profile list entry. */
TAILQ_ENTRY(bfd_profile) entry;
};
/** Profile list type. */
TAILQ_HEAD(bfdproflist, bfd_profile);
/* bfd_session shortcut label forwarding. */
struct peer_label;
struct bfd_config_timers {
uint32_t desired_min_tx;
uint32_t required_min_rx;
uint32_t desired_min_echo_tx;
uint32_t required_min_echo_rx;
};
#define BFD_RTT_SAMPLE 8
/*
* Session state information
*/
struct bfd_session {
/* protocol state per RFC 5880*/
uint8_t ses_state;
struct bfd_discrs discrs;
uint8_t local_diag;
uint8_t demand_mode;
uint8_t detect_mult;
uint8_t remote_detect_mult;
uint8_t mh_ttl;
uint8_t remote_cbit;
/** BFD profile name. */
char *profile_name;
/** BFD pre configured profile. */
struct bfd_profile *profile;
/** BFD peer configuration (without profile). */
struct bfd_profile peer_profile;
/* Timers */
struct bfd_config_timers timers;
struct bfd_timers cur_timers;
uint64_t detect_TO;
struct event *echo_recvtimer_ev;
struct event *recvtimer_ev;
uint64_t xmt_TO;
uint64_t echo_xmt_TO;
struct event *xmttimer_ev;
struct event *echo_xmttimer_ev;
uint64_t echo_detect_TO;
/* software object state */
uint8_t polling;
/* This and the localDiscr are the keys to state info */
struct bfd_key key;
struct peer_label *pl;
struct bfd_dplane_ctx *bdc;
struct sockaddr_any local_address;
uint8_t peer_hw_addr[ETH_ALEN];
struct interface *ifp;
struct vrf *vrf;
int sock;
/* BFD session flags */
enum bfd_session_flags flags;
struct bfd_session_stats stats;
struct timeval uptime; /* last up time */
struct timeval downtime; /* last down time */
/* Remote peer data (for debugging mostly) */
uint8_t remote_diag;
struct bfd_timers remote_timers;
uint64_t refcount; /* number of pointers referencing this. */
uint8_t rtt_valid; /* number of valid samples */
uint8_t rtt_index; /* last index added */
uint64_t rtt[BFD_RTT_SAMPLE]; /* RRT in usec for echo to be looped */
};
struct peer_label {
TAILQ_ENTRY(peer_label) pl_entry;
struct bfd_session *pl_bs;
char pl_label[MAXNAMELEN];
};
TAILQ_HEAD(pllist, peer_label);
struct bfd_diag_str_list {
const char *str;
int type;
};
struct bfd_state_str_list {
const char *str;
int type;
};
struct bfd_session_observer {
struct bfd_session *bso_bs;
char bso_entryname[MAXNAMELEN];
struct prefix bso_addr;
TAILQ_ENTRY(bfd_session_observer) bso_entry;
};
TAILQ_HEAD(obslist, bfd_session_observer);
/* States defined per 4.1 */
#define PTM_BFD_ADM_DOWN 0
#define PTM_BFD_DOWN 1
#define PTM_BFD_INIT 2
#define PTM_BFD_UP 3
/* Various constants */
/* Retrieved from ptm_timer.h from Cumulus PTM sources. */
#define BFD_DEF_DEMAND 0
#define BFD_DEFDETECTMULT 3
#define BFD_DEFDESIREDMINTX (300 * 1000) /* microseconds. */
#define BFD_DEFREQUIREDMINRX (300 * 1000) /* microseconds. */
#define BFD_DEF_DES_MIN_ECHO_TX (50 * 1000) /* microseconds. */
#define BFD_DEF_REQ_MIN_ECHO_RX (50 * 1000) /* microseconds. */
#define BFD_DEF_SLOWTX (1000 * 1000) /* microseconds. */
/** Minimum multi hop TTL. */
#define BFD_DEF_MHOP_TTL 254
#define BFD_PKT_LEN 24 /* Length of control packet */
#define BFD_TTL_VAL 255
#define BFD_RCV_TTL_VAL 1
#define BFD_TOS_VAL 0xC0
#define BFD_PKT_INFO_VAL 1
#define BFD_IPV6_PKT_INFO_VAL 1
#define BFD_IPV6_ONLY_VAL 1
#define BFD_SRCPORTINIT 49152
#define BFD_SRCPORTMAX 65535
#define BFD_DEFDESTPORT 3784
#define BFD_DEF_ECHO_PORT 3785
#define BFD_DEF_MHOP_DEST_PORT 4784
/*
* control.c
*
* Daemon control code to speak with local consumers.
*/
/* See 'bfdctrl.h' for client protocol definitions. */
struct bfd_control_buffer {
size_t bcb_left;
size_t bcb_pos;
union {
struct bfd_control_msg *bcb_bcm;
uint8_t *bcb_buf;
};
};
struct bfd_control_queue {
TAILQ_ENTRY(bfd_control_queue) bcq_entry;
struct bfd_control_buffer bcq_bcb;
};
TAILQ_HEAD(bcqueue, bfd_control_queue);
struct bfd_notify_peer {
TAILQ_ENTRY(bfd_notify_peer) bnp_entry;
struct bfd_session *bnp_bs;
};
TAILQ_HEAD(bnplist, bfd_notify_peer);
struct bfd_control_socket {
TAILQ_ENTRY(bfd_control_socket) bcs_entry;
int bcs_sd;
struct event *bcs_ev;
struct event *bcs_outev;
struct bcqueue bcs_bcqueue;
/* Notification data */
uint64_t bcs_notify;
struct bnplist bcs_bnplist;
enum bc_msg_version bcs_version;
enum bc_msg_type bcs_type;
/* Message buffering */
struct bfd_control_buffer bcs_bin;
struct bfd_control_buffer *bcs_bout;
};
TAILQ_HEAD(bcslist, bfd_control_socket);
int control_init(const char *path);
void control_shutdown(void);
int control_notify(struct bfd_session *bs, uint8_t notify_state);
int control_notify_config(const char *op, struct bfd_session *bs);
void control_accept(struct event *t);
/*
* bfdd.c
*
* Daemon specific code.
*/
struct bfd_vrf_global {
int bg_shop;
int bg_mhop;
int bg_shop6;
int bg_mhop6;
int bg_echo;
int bg_echov6;
struct vrf *vrf;
struct event *bg_ev[6];
};
/* Forward declaration of data plane context struct. */
struct bfd_dplane_ctx;
TAILQ_HEAD(dplane_queue, bfd_dplane_ctx);
struct bfd_global {
int bg_csock;
struct event *bg_csockev;
struct bcslist bg_bcslist;
struct pllist bg_pllist;
struct obslist bg_obslist;
struct zebra_privs_t bfdd_privs;
/**
* Daemon is exit()ing? Use this to avoid actions that expect a
* running system or to avoid unnecessary operations when quitting.
*/
bool bg_shutdown;
/* Distributed BFD items. */
bool bg_use_dplane;
int bg_dplane_sock;
struct event *bg_dplane_sockev;
struct dplane_queue bg_dplaneq;
/* Debug options. */
/* Show distributed BFD debug messages. */
bool debug_dplane;
/* Show all peer state changes events. */
bool debug_peer_event;
/*
* Show zebra message exchanges:
* - Interface add/delete.
* - Local address add/delete.
* - VRF add/delete.
*/
bool debug_zebra;
/*
* Show network level debug information:
* - Echo packets without session.
* - Unavailable peer sessions.
* - Network system call failures.
*/
bool debug_network;
};
extern struct bfd_global bglobal;
extern const struct bfd_diag_str_list diag_list[];
extern const struct bfd_state_str_list state_list[];
void socket_close(int *s);
/*
* config.c
*
* Contains the code related with loading/reloading configuration.
*/
int parse_config(const char *fname);
int config_request_add(const char *jsonstr);
int config_request_del(const char *jsonstr);
char *config_response(const char *status, const char *error);
char *config_notify(struct bfd_session *bs);
char *config_notify_config(const char *op, struct bfd_session *bs);
typedef int (*bpc_handle)(struct bfd_peer_cfg *, void *arg);
int config_notify_request(struct bfd_control_socket *bcs, const char *jsonstr,
bpc_handle bh);
struct peer_label *pl_new(const char *label, struct bfd_session *bs);
struct peer_label *pl_find(const char *label);
void pl_free(struct peer_label *pl);
/*
* logging - alias to zebra log
*/
#define zlog_fatal(msg, ...) \
do { \
zlog_err(msg, ##__VA_ARGS__); \
assert(!msg); \
abort(); \
} while (0)
/*
* bfd_packet.c
*
* Contains the code related with receiving/seding, packing/unpacking BFD data.
*/
int bp_set_ttlv6(int sd, uint8_t value);
int bp_set_ttl(int sd, uint8_t value);
int bp_set_tosv6(int sd, uint8_t value);
int bp_set_tos(int sd, uint8_t value);
int bp_bind_dev(int sd, const char *dev);
int bp_udp_shop(const struct vrf *vrf);
int bp_udp_mhop(const struct vrf *vrf);
int bp_udp6_shop(const struct vrf *vrf);
int bp_udp6_mhop(const struct vrf *vrf);
int bp_peer_socket(const struct bfd_session *bs);
int bp_peer_socketv6(const struct bfd_session *bs);
int bp_echo_socket(const struct vrf *vrf);
int bp_echov6_socket(const struct vrf *vrf);
void ptm_bfd_snd(struct bfd_session *bfd, int fbit);
void ptm_bfd_echo_snd(struct bfd_session *bfd);
void ptm_bfd_echo_fp_snd(struct bfd_session *bfd);
void bfd_recv_cb(struct event *t);
/*
* event.c
*
* Contains the code related with event loop.
*/
typedef void (*bfd_ev_cb)(struct event *t);
void bfd_recvtimer_update(struct bfd_session *bs);
void bfd_echo_recvtimer_update(struct bfd_session *bs);
void bfd_xmttimer_update(struct bfd_session *bs, uint64_t jitter);
void bfd_echo_xmttimer_update(struct bfd_session *bs, uint64_t jitter);
void bfd_xmttimer_delete(struct bfd_session *bs);
void bfd_echo_xmttimer_delete(struct bfd_session *bs);
void bfd_recvtimer_delete(struct bfd_session *bs);
void bfd_echo_recvtimer_delete(struct bfd_session *bs);
void bfd_recvtimer_assign(struct bfd_session *bs, bfd_ev_cb cb, int sd);
void bfd_echo_recvtimer_assign(struct bfd_session *bs, bfd_ev_cb cb, int sd);
void bfd_xmttimer_assign(struct bfd_session *bs, bfd_ev_cb cb);
void bfd_echo_xmttimer_assign(struct bfd_session *bs, bfd_ev_cb cb);
/*
* bfd.c
*
* BFD protocol specific code.
*/
int bfd_session_enable(struct bfd_session *bs);
void bfd_session_disable(struct bfd_session *bs);
struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc);
int ptm_bfd_sess_del(struct bfd_peer_cfg *bpc);
void ptm_bfd_sess_dn(struct bfd_session *bfd, uint8_t diag);
void ptm_bfd_sess_up(struct bfd_session *bfd);
void ptm_bfd_echo_stop(struct bfd_session *bfd);
void ptm_bfd_echo_start(struct bfd_session *bfd);
void ptm_bfd_xmt_TO(struct bfd_session *bfd, int fbit);
void ptm_bfd_start_xmt_timer(struct bfd_session *bfd, bool is_echo);
struct bfd_session *ptm_bfd_sess_find(struct bfd_pkt *cp,
struct sockaddr_any *peer,
struct sockaddr_any *local,
struct interface *ifp,
vrf_id_t vrfid,
bool is_mhop);
struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc);
int bfd_session_update_label(struct bfd_session *bs, const char *nlabel);
void bfd_set_polling(struct bfd_session *bs);
void bs_state_handler(struct bfd_session *bs, int nstate);
void bs_echo_timer_handler(struct bfd_session *bs);
void bs_final_handler(struct bfd_session *bs);
void bs_set_slow_timers(struct bfd_session *bs);
const char *satostr(const struct sockaddr_any *sa);
const char *diag2str(uint8_t diag);
int strtosa(const char *addr, struct sockaddr_any *sa);
void integer2timestr(uint64_t time, char *buf, size_t buflen);
const char *bs_to_string(const struct bfd_session *bs);
int bs_observer_add(struct bfd_session *bs);
void bs_observer_del(struct bfd_session_observer *bso);
void bs_to_bpc(struct bfd_session *bs, struct bfd_peer_cfg *bpc);
void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer,
struct sockaddr_any *local, bool mhop, const char *ifname,
const char *vrfname);
struct bfd_session *bfd_session_new(void);
struct bfd_session *bs_registrate(struct bfd_session *bs);
void bfd_session_free(struct bfd_session *bs);
const struct bfd_session *bfd_session_next(const struct bfd_session *bs,
bool mhop);
void bfd_sessions_remove_manual(void);
void bfd_profiles_remove(void);
void bfd_rtt_init(struct bfd_session *bfd);
/**
* Set the BFD session echo state.
*
* \param bs the BFD session.
* \param echo the echo operational state.
*/
void bfd_set_echo(struct bfd_session *bs, bool echo);
/**
* Set the BFD session functional state.
*
* \param bs the BFD session.
* \param shutdown the operational value.
*/
void bfd_set_shutdown(struct bfd_session *bs, bool shutdown);
/**
* Set the BFD session passive mode.
*
* \param bs the BFD session.
* \param passive the passive mode.
*/
void bfd_set_passive_mode(struct bfd_session *bs, bool passive);
/**
* Picks the BFD session configuration from the appropriated source:
* if using the default peer configuration prefer profile (if it exists),
* otherwise use session.
*
* \param bs the BFD session.
*/
void bfd_session_apply(struct bfd_session *bs);
/* BFD hash data structures interface */
void bfd_initialize(void);
void bfd_shutdown(void);
void bfd_vrf_init(void);
void bfd_vrf_terminate(void);
struct bfd_vrf_global *bfd_vrf_look_by_session(struct bfd_session *bfd);
struct bfd_session *bfd_id_lookup(uint32_t id);
struct bfd_session *bfd_key_lookup(struct bfd_key key);
struct bfd_session *bfd_id_delete(uint32_t id);
struct bfd_session *bfd_key_delete(struct bfd_key key);
bool bfd_id_insert(struct bfd_session *bs);
bool bfd_key_insert(struct bfd_session *bs);
typedef void (*hash_iter_func)(struct hash_bucket *hb, void *arg);
void bfd_id_iterate(hash_iter_func hif, void *arg);
void bfd_key_iterate(hash_iter_func hif, void *arg);
unsigned long bfd_get_session_count(void);
/* Export callback functions for `event.c`. */
extern struct event_loop *master;
void bfd_recvtimer_cb(struct event *t);
void bfd_echo_recvtimer_cb(struct event *t);
void bfd_xmt_cb(struct event *t);
void bfd_echo_xmt_cb(struct event *t);
extern struct in6_addr zero_addr;
/**
* Creates a new profile entry and insert into the global list.
*
* \param name the BFD profile name.
*
* \returns `NULL` if it already exists otherwise the new entry.
*/
struct bfd_profile *bfd_profile_new(const char *name);
/**
* Search for configured BFD profiles (profile name is case insensitive).
*
* \param name the BFD profile name.
*
* \returns `NULL` if it doesn't exist otherwise the entry.
*/
struct bfd_profile *bfd_profile_lookup(const char *name);
/**
* Removes profile from list and free memory.
*
* \param bp the BFD profile.
*/
void bfd_profile_free(struct bfd_profile *bp);
/**
* Apply a profile configuration to an existing BFD session. The non default
* values will not be overridden.
*
* NOTE: if the profile doesn't exist yet, then the profile will be applied
* once it begins to exist.
*
* \param profile_name the BFD profile name.
* \param bs the BFD session.
*/
void bfd_profile_apply(const char *profname, struct bfd_session *bs);
/**
* Remove any applied profile from session and revert the session
* configuration.
*
* \param bs the BFD session.
*/
void bfd_profile_remove(struct bfd_session *bs);
/**
* Apply new profile values to sessions using it.
*
* \param[in] bp the BFD profile that got updated.
*/
void bfd_profile_update(struct bfd_profile *bp);
/*
* bfdd_vty.c
*
* BFD daemon vty shell commands.
*/
void bfdd_vty_init(void);
/*
* bfdd_cli.c
*
* BFD daemon CLI implementation.
*/
void bfdd_cli_init(void);
/*
* ptm_adapter.c
*/
void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv);
void bfdd_zclient_stop(void);
void bfdd_zclient_terminate(void);
void bfdd_zclient_unregister(vrf_id_t vrf_id);
void bfdd_zclient_register(vrf_id_t vrf_id);
void bfdd_sessions_enable_vrf(struct vrf *vrf);
void bfdd_sessions_disable_vrf(struct vrf *vrf);
int ptm_bfd_notify(struct bfd_session *bs, uint8_t notify_state);
/*
* dplane.c
*/
/**
* Initialize BFD data plane infrastructure for distributed BFD implementation.
*
* \param sa socket address.
* \param salen socket address structure length.
* \param client `true` means connecting socket, `false` listening socket.
*/
void bfd_dplane_init(const struct sockaddr *sa, socklen_t salen, bool client);
/**
* Attempts to delegate the BFD session liveness detection to hardware.
*
* \param bs the BFD session data structure.
*
* \returns
* `0` on success and BFD daemon should do nothing or `-1` on failure
* and we should fallback to software implementation.
*/
int bfd_dplane_add_session(struct bfd_session *bs);
/**
* Send new session settings to data plane.
*
* \param bs the BFD session to update.
*/
int bfd_dplane_update_session(const struct bfd_session *bs);
/**
* Deletes session from data plane.
*
* \param bs the BFD session to delete.
*
* \returns `0` on success otherwise `-1`.
*/
int bfd_dplane_delete_session(struct bfd_session *bs);
/**
* Asks the data plane for updated counters and update the session data
* structure.
*
* \param bs the BFD session that needs updating.
*
* \returns `0` on success otherwise `-1` on failure.
*/
int bfd_dplane_update_session_counters(struct bfd_session *bs);
void bfd_dplane_show_counters(struct vty *vty);
#endif /* _BFD_H_ */

1770
bfdd/bfd_packet.c Normal file

File diff suppressed because it is too large Load diff

157
bfdd/bfdctl.h Normal file
View file

@ -0,0 +1,157 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*********************************************************************
* Copyright 2017-2018 Network Device Education Foundation, Inc. ("NetDEF")
*
* bfdctl.h: all BFDd control socket protocol definitions.
*
* Authors
* -------
* Rafael Zalamena <rzalamena@opensourcerouting.org>
*/
#ifndef _BFDCTRL_H_
#define _BFDCTRL_H_
#include <netinet/in.h>
#include <stdbool.h>
#include <stdint.h>
/*
* Auxiliary definitions
*/
struct sockaddr_any {
union {
struct sockaddr_in sa_sin;
struct sockaddr_in6 sa_sin6;
};
};
#ifndef MAXNAMELEN
#define MAXNAMELEN 32
#endif
#define BPC_DEF_DETECTMULTIPLIER 3
#define BPC_DEF_RECEIVEINTERVAL 300 /* milliseconds */
#define BPC_DEF_TRANSMITINTERVAL 300 /* milliseconds */
#define BPC_DEF_ECHORECEIVEINTERVAL 50 /* milliseconds */
#define BPC_DEF_ECHOTRANSMITINTERVAL 50 /* milliseconds */
/* Peer status */
enum bfd_peer_status {
BPS_SHUTDOWN = 0, /* == PTM_BFD_ADM_DOWN, "adm-down" */
BPS_DOWN = 1, /* == PTM_BFD_DOWN, "down" */
BPS_INIT = 2, /* == PTM_BFD_INIT, "init" */
BPS_UP = 3, /* == PTM_BFD_UP, "up" */
};
struct bfd_peer_cfg {
bool bpc_mhop;
bool bpc_ipv4;
struct sockaddr_any bpc_peer;
struct sockaddr_any bpc_local;
bool bpc_has_label;
char bpc_label[MAXNAMELEN];
bool bpc_has_localif;
char bpc_localif[MAXNAMELEN + 1];
bool bpc_has_vrfname;
char bpc_vrfname[MAXNAMELEN + 1];
bool bpc_has_detectmultiplier;
uint8_t bpc_detectmultiplier;
bool bpc_has_recvinterval;
uint64_t bpc_recvinterval;
bool bpc_has_txinterval;
uint64_t bpc_txinterval;
bool bpc_has_echorecvinterval;
uint64_t bpc_echorecvinterval;
bool bpc_has_echotxinterval;
uint64_t bpc_echotxinterval;
bool bpc_has_minimum_ttl;
uint8_t bpc_minimum_ttl;
bool bpc_echo;
bool bpc_createonly;
bool bpc_shutdown;
bool bpc_cbit;
bool bpc_passive;
bool bpc_has_profile;
char bpc_profile[64];
/* Status information */
enum bfd_peer_status bpc_bps;
uint32_t bpc_id;
uint32_t bpc_remoteid;
uint8_t bpc_diag;
uint8_t bpc_remotediag;
uint8_t bpc_remote_detectmultiplier;
uint64_t bpc_remote_recvinterval;
uint64_t bpc_remote_txinterval;
uint64_t bpc_remote_echointerval;
uint64_t bpc_lastevent;
};
/*
* Protocol definitions
*/
enum bc_msg_version {
BMV_VERSION_1 = 1,
};
enum bc_msg_type {
BMT_RESPONSE = 1,
BMT_REQUEST_ADD = 2,
BMT_REQUEST_DEL = 3,
BMT_NOTIFY = 4,
BMT_NOTIFY_ADD = 5,
BMT_NOTIFY_DEL = 6,
};
/* Notify flags to use with bcm_notify. */
#define BCM_NOTIFY_ALL ((uint64_t)-1)
#define BCM_NOTIFY_PEER_STATE (1ULL << 0)
#define BCM_NOTIFY_CONFIG (1ULL << 1)
#define BCM_NOTIFY_NONE 0
/* Response 'status' definitions. */
#define BCM_RESPONSE_OK "ok"
#define BCM_RESPONSE_ERROR "error"
/* Notify operation. */
#define BCM_NOTIFY_PEER_STATUS "status"
#define BCM_NOTIFY_CONFIG_ADD "add"
#define BCM_NOTIFY_CONFIG_DELETE "delete"
#define BCM_NOTIFY_CONFIG_UPDATE "update"
/* Notification special ID. */
#define BCM_NOTIFY_ID 0
struct bfd_control_msg {
/* Total length without the header. */
uint32_t bcm_length;
/*
* Message request/response id.
* All requests will have a correspondent response with the
* same id.
*/
uint16_t bcm_id;
/* Message type. */
uint8_t bcm_type;
/* Message version. */
uint8_t bcm_ver;
/* Message payload. */
uint8_t bcm_data[0];
};
#endif

401
bfdd/bfdd.c Normal file
View file

@ -0,0 +1,401 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* BFD daemon code
* Copyright (C) 2018 Network Device Education Foundation, Inc. ("NetDEF")
*/
#include <zebra.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <err.h>
#include "filter.h"
#include "if.h"
#include "vrf.h"
#include "bfd.h"
#include "bfdd_nb.h"
#include "bfddp_packet.h"
#include "lib/version.h"
#include "lib/command.h"
/*
* FRR related code.
*/
DEFINE_MGROUP(BFDD, "Bidirectional Forwarding Detection Daemon");
DEFINE_MTYPE(BFDD, BFDD_CONTROL, "control socket memory");
DEFINE_MTYPE(BFDD, BFDD_NOTIFICATION, "control notification data");
/* Master of threads. */
struct event_loop *master;
/* BFDd privileges */
static zebra_capabilities_t _caps_p[] = {ZCAP_BIND, ZCAP_SYS_ADMIN, ZCAP_NET_RAW};
/* BFD daemon information. */
static struct frr_daemon_info bfdd_di;
void socket_close(int *s)
{
if (*s <= 0)
return;
if (close(*s) != 0)
zlog_err("%s: close(%d): (%d) %s", __func__, *s, errno,
strerror(errno));
*s = -1;
}
static void sigusr1_handler(void)
{
zlog_rotate();
}
static void sigterm_handler(void)
{
bglobal.bg_shutdown = true;
/* Signalize shutdown. */
frr_early_fini();
/* Stop receiving message from zebra. */
bfdd_zclient_stop();
/* Shutdown controller to avoid receiving anymore commands. */
control_shutdown();
/* Shutdown and free all protocol related memory. */
bfd_shutdown();
bfd_vrf_terminate();
bfdd_zclient_terminate();
/* Terminate and free() FRR related memory. */
frr_fini();
exit(0);
}
static void sighup_handler(void)
{
zlog_info("SIGHUP received");
/* Reload config file. */
vty_read_config(NULL, bfdd_di.config_file, config_default);
}
static struct frr_signal_t bfd_signals[] = {
{
.signal = SIGUSR1,
.handler = &sigusr1_handler,
},
{
.signal = SIGTERM,
.handler = &sigterm_handler,
},
{
.signal = SIGINT,
.handler = &sigterm_handler,
},
{
.signal = SIGHUP,
.handler = &sighup_handler,
},
};
static const struct frr_yang_module_info *const bfdd_yang_modules[] = {
&frr_filter_info,
&frr_interface_info,
&frr_bfdd_info,
&frr_vrf_info,
};
/* clang-format off */
FRR_DAEMON_INFO(bfdd, BFD,
.vty_port = BFDD_VTY_PORT,
.proghelp = "Implementation of the BFD protocol.",
.signals = bfd_signals,
.n_signals = array_size(bfd_signals),
.privs = &bglobal.bfdd_privs,
.yang_modules = bfdd_yang_modules,
.n_yang_modules = array_size(bfdd_yang_modules),
);
/* clang-format on */
#define OPTION_CTLSOCK 1001
#define OPTION_DPLANEADDR 2000
static const struct option longopts[] = {
{"bfdctl", required_argument, NULL, OPTION_CTLSOCK},
{"dplaneaddr", required_argument, NULL, OPTION_DPLANEADDR},
{0}
};
/*
* BFD daemon related code.
*/
struct bfd_global bglobal;
const struct bfd_diag_str_list diag_list[] = {
{.str = "control-expired", .type = BD_CONTROL_EXPIRED},
{.str = "echo-failed", .type = BD_ECHO_FAILED},
{.str = "neighbor-down", .type = BD_NEIGHBOR_DOWN},
{.str = "forwarding-reset", .type = BD_FORWARDING_RESET},
{.str = "path-down", .type = BD_PATH_DOWN},
{.str = "concatenated-path-down", .type = BD_CONCATPATH_DOWN},
{.str = "administratively-down", .type = BD_ADMIN_DOWN},
{.str = "reverse-concat-path-down", .type = BD_REVCONCATPATH_DOWN},
{.str = NULL},
};
const struct bfd_state_str_list state_list[] = {
{.str = "admin-down", .type = PTM_BFD_ADM_DOWN},
{.str = "down", .type = PTM_BFD_DOWN},
{.str = "init", .type = PTM_BFD_INIT},
{.str = "up", .type = PTM_BFD_UP},
{.str = NULL},
};
static uint16_t
parse_port(const char *str)
{
char *nulbyte;
long rv;
errno = 0;
rv = strtol(str, &nulbyte, 10);
/* No conversion performed. */
if (rv == 0 && errno == EINVAL) {
fprintf(stderr, "invalid BFD data plane address port: %s\n",
str);
exit(0);
}
/* Invalid number range. */
if ((rv <= 0 || rv >= 65535) || errno == ERANGE) {
fprintf(stderr, "invalid BFD data plane port range: %s\n",
str);
exit(0);
}
/* There was garbage at the end of the string. */
if (*nulbyte != 0) {
fprintf(stderr, "invalid BFD data plane port: %s\n",
str);
exit(0);
}
return (uint16_t)rv;
}
static void
distributed_bfd_init(const char *arg)
{
char *sptr, *saux;
bool is_client = false;
size_t slen;
socklen_t salen;
char addr[64];
char type[64];
union {
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
struct sockaddr_un sun;
} sa;
/* Basic parsing: find ':' to figure out type part and address part. */
sptr = strchr(arg, ':');
if (sptr == NULL) {
fprintf(stderr, "invalid BFD data plane socket: %s\n", arg);
exit(1);
}
/* Calculate type string length. */
slen = (size_t)(sptr - arg);
/* Copy the address part. */
sptr++;
strlcpy(addr, sptr, sizeof(addr));
/* Copy type part. */
strlcpy(type, arg, slen + 1);
/* Reset address data. */
memset(&sa, 0, sizeof(sa));
/* Fill the address information. */
if (strcmp(type, "unix") == 0 || strcmp(type, "unixc") == 0) {
if (strcmp(type, "unixc") == 0)
is_client = true;
salen = sizeof(sa.sun);
sa.sun.sun_family = AF_UNIX;
strlcpy(sa.sun.sun_path, addr, sizeof(sa.sun.sun_path));
} else if (strcmp(type, "ipv4") == 0 || strcmp(type, "ipv4c") == 0) {
if (strcmp(type, "ipv4c") == 0)
is_client = true;
salen = sizeof(sa.sin);
sa.sin.sin_family = AF_INET;
/* Parse port if any. */
sptr = strchr(addr, ':');
if (sptr == NULL) {
sa.sin.sin_port = htons(BFD_DATA_PLANE_DEFAULT_PORT);
} else {
*sptr = 0;
sa.sin.sin_port = htons(parse_port(sptr + 1));
}
if (inet_pton(AF_INET, addr, &sa.sin.sin_addr) != 1)
errx(1, "%s: inet_pton: invalid address %s", __func__,
addr);
} else if (strcmp(type, "ipv6") == 0 || strcmp(type, "ipv6c") == 0) {
if (strcmp(type, "ipv6c") == 0)
is_client = true;
salen = sizeof(sa.sin6);
sa.sin6.sin6_family = AF_INET6;
/* Check for IPv6 enclosures '[]' */
sptr = &addr[0];
if (*sptr != '[')
errx(1, "%s: invalid IPv6 address format: %s", __func__,
addr);
saux = strrchr(addr, ']');
if (saux == NULL)
errx(1, "%s: invalid IPv6 address format: %s", __func__,
addr);
/* Consume the '[]:' part. */
slen = saux - sptr;
memmove(addr, addr + 1, slen);
addr[slen - 1] = 0;
/* Parse port if any. */
saux++;
sptr = strrchr(saux, ':');
if (sptr == NULL) {
sa.sin6.sin6_port = htons(BFD_DATA_PLANE_DEFAULT_PORT);
} else {
*sptr = 0;
sa.sin6.sin6_port = htons(parse_port(sptr + 1));
}
if (inet_pton(AF_INET6, addr, &sa.sin6.sin6_addr) != 1)
errx(1, "%s: inet_pton: invalid address %s", __func__,
addr);
} else {
fprintf(stderr, "invalid BFD data plane socket type: %s\n",
type);
exit(1);
}
/* Initialize BFD data plane listening socket. */
bfd_dplane_init((struct sockaddr *)&sa, salen, is_client);
}
static void bg_init(void)
{
struct zebra_privs_t bfdd_privs = {
#if defined(FRR_USER) && defined(FRR_GROUP)
.user = FRR_USER,
.group = FRR_GROUP,
#endif
#if defined(VTY_GROUP)
.vty_group = VTY_GROUP,
#endif
.caps_p = _caps_p,
.cap_num_p = array_size(_caps_p),
.cap_num_i = 0,
};
TAILQ_INIT(&bglobal.bg_bcslist);
TAILQ_INIT(&bglobal.bg_obslist);
memcpy(&bglobal.bfdd_privs, &bfdd_privs,
sizeof(bfdd_privs));
}
int main(int argc, char *argv[])
{
char ctl_path[512], dplane_addr[512];
bool ctlsockused = false;
int opt;
bglobal.bg_use_dplane = false;
/* Initialize system sockets. */
bg_init();
frr_preinit(&bfdd_di, argc, argv);
frr_opt_add("", longopts,
" --bfdctl Specify bfdd control socket\n"
" --dplaneaddr Specify BFD data plane address\n");
while (true) {
opt = frr_getopt(argc, argv, NULL);
if (opt == EOF)
break;
switch (opt) {
case OPTION_CTLSOCK:
strlcpy(ctl_path, optarg, sizeof(ctl_path));
ctlsockused = true;
break;
case OPTION_DPLANEADDR:
strlcpy(dplane_addr, optarg, sizeof(dplane_addr));
bglobal.bg_use_dplane = true;
break;
default:
frr_help_exit(1);
}
}
if (!ctlsockused)
snprintf(ctl_path, sizeof(ctl_path), BFDD_SOCK_NAME);
/* Initialize FRR infrastructure. */
master = frr_init();
/* Initialize control socket. */
control_init(ctl_path);
/* Initialize BFD data structures. */
bfd_initialize();
bfd_vrf_init();
access_list_init();
/* Initialize zebra connection. */
bfdd_zclient_init(&bglobal.bfdd_privs);
event_add_read(master, control_accept, NULL, bglobal.bg_csock,
&bglobal.bg_csockev);
/* Install commands. */
bfdd_vty_init();
/* read configuration file and daemonize */
frr_config_fork();
/* Initialize BFD data plane listening socket. */
if (bglobal.bg_use_dplane)
distributed_bfd_init(dplane_addr);
frr_run(master);
/* NOTREACHED */
return 0;
}

718
bfdd/bfdd_cli.c Normal file
View file

@ -0,0 +1,718 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* BFD daemon CLI implementation.
*
* Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
* Rafael Zalamena
*/
#include <zebra.h>
#include "lib/command.h"
#include "lib/log.h"
#include "lib/northbound_cli.h"
#include "bfdd/bfdd_cli_clippy.c"
#include "bfd.h"
#include "bfdd_nb.h"
/*
* Definitions.
*/
#define PEER_STR "Configure peer\n"
#define INTERFACE_NAME_STR "Configure interface name to use\n"
#define PEER_IPV4_STR "IPv4 peer address\n"
#define PEER_IPV6_STR "IPv6 peer address\n"
#define MHOP_STR "Configure multihop\n"
#define LOCAL_STR "Configure local address\n"
#define LOCAL_IPV4_STR "IPv4 local address\n"
#define LOCAL_IPV6_STR "IPv6 local address\n"
#define LOCAL_INTF_STR "Configure local interface name to use\n"
#define VRF_STR "Configure VRF\n"
#define VRF_NAME_STR "Configure VRF name\n"
/*
* Prototypes.
*/
static bool
bfd_cli_is_single_hop(struct vty *vty)
{
return strstr(VTY_CURR_XPATH, "/single-hop") != NULL;
}
static bool
bfd_cli_is_profile(struct vty *vty)
{
return strstr(VTY_CURR_XPATH, "/bfd/profile") != NULL;
}
/*
* Functions.
*/
DEFPY_YANG_NOSH(
bfd_enter, bfd_enter_cmd,
"bfd",
"Configure BFD peers\n")
{
int ret;
nb_cli_enqueue_change(vty, "/frr-bfdd:bfdd/bfd", NB_OP_CREATE, NULL);
ret = nb_cli_apply_changes(vty, NULL);
if (ret == CMD_SUCCESS)
VTY_PUSH_XPATH(BFD_NODE, "/frr-bfdd:bfdd/bfd");
return ret;
}
DEFUN_YANG(
bfd_config_reset, bfd_config_reset_cmd,
"no bfd",
NO_STR
"Configure BFD peers\n")
{
nb_cli_enqueue_change(vty, "/frr-bfdd:bfdd/bfd", NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, NULL);
}
void bfd_cli_show_header(struct vty *vty,
const struct lyd_node *dnode
__attribute__((__unused__)),
bool show_defaults __attribute__((__unused__)))
{
vty_out(vty, "!\nbfd\n");
}
void bfd_cli_show_header_end(struct vty *vty, const struct lyd_node *dnode
__attribute__((__unused__)))
{
vty_out(vty, "exit\n");
vty_out(vty, "!\n");
}
DEFPY_YANG_NOSH(
bfd_peer_enter, bfd_peer_enter_cmd,
"peer <A.B.C.D|X:X::X:X> [{multihop$multihop|local-address <A.B.C.D|X:X::X:X>|interface IFNAME$ifname|vrf NAME}]",
PEER_STR
PEER_IPV4_STR
PEER_IPV6_STR
MHOP_STR
LOCAL_STR
LOCAL_IPV4_STR
LOCAL_IPV6_STR
INTERFACE_STR
LOCAL_INTF_STR
VRF_STR
VRF_NAME_STR)
{
int ret, slen;
char source_str[INET6_ADDRSTRLEN + 32];
char xpath[XPATH_MAXLEN], xpath_srcaddr[XPATH_MAXLEN + 32];
if (multihop) {
if (!local_address_str) {
vty_out(vty,
"%% local-address is required when using multihop\n");
return CMD_WARNING_CONFIG_FAILED;
}
if (ifname) {
vty_out(vty,
"%% interface is prohibited when using multihop\n");
return CMD_WARNING_CONFIG_FAILED;
}
snprintf(source_str, sizeof(source_str), "[source-addr='%s']",
local_address_str);
} else
source_str[0] = 0;
slen = snprintf(xpath, sizeof(xpath),
"/frr-bfdd:bfdd/bfd/sessions/%s%s[dest-addr='%s']",
multihop ? "multi-hop" : "single-hop", source_str,
peer_str);
if (ifname)
slen += snprintf(xpath + slen, sizeof(xpath) - slen,
"[interface='%s']", ifname);
else if (!multihop)
slen += snprintf(xpath + slen, sizeof(xpath) - slen,
"[interface='*']");
if (vrf)
snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", vrf);
else
snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']",
VRF_DEFAULT_NAME);
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
if (multihop == NULL && local_address_str != NULL) {
snprintf(xpath_srcaddr, sizeof(xpath_srcaddr),
"%s/source-addr", xpath);
nb_cli_enqueue_change(vty, xpath_srcaddr, NB_OP_MODIFY,
local_address_str);
}
/* Apply settings immediately. */
ret = nb_cli_apply_changes(vty, NULL);
if (ret == CMD_SUCCESS)
VTY_PUSH_XPATH(BFD_PEER_NODE, xpath);
return ret;
}
DEFPY_YANG(
bfd_no_peer, bfd_no_peer_cmd,
"no peer <A.B.C.D|X:X::X:X> [{multihop$multihop|local-address <A.B.C.D|X:X::X:X>|interface IFNAME$ifname|vrf NAME}]",
NO_STR
PEER_STR
PEER_IPV4_STR
PEER_IPV6_STR
MHOP_STR
LOCAL_STR
LOCAL_IPV4_STR
LOCAL_IPV6_STR
INTERFACE_STR
LOCAL_INTF_STR
VRF_STR
VRF_NAME_STR)
{
int slen;
char xpath[XPATH_MAXLEN];
char source_str[INET6_ADDRSTRLEN + 32];
if (multihop) {
if (!local_address_str) {
vty_out(vty,
"%% local-address is required when using multihop\n");
return CMD_WARNING_CONFIG_FAILED;
}
if (ifname) {
vty_out(vty,
"%% interface is prohibited when using multihop\n");
return CMD_WARNING_CONFIG_FAILED;
}
snprintf(source_str, sizeof(source_str), "[source-addr='%s']",
local_address_str);
} else
source_str[0] = 0;
slen = snprintf(xpath, sizeof(xpath),
"/frr-bfdd:bfdd/bfd/sessions/%s%s[dest-addr='%s']",
multihop ? "multi-hop" : "single-hop", source_str,
peer_str);
if (ifname)
slen += snprintf(xpath + slen, sizeof(xpath) - slen,
"[interface='%s']", ifname);
else if (!multihop)
slen += snprintf(xpath + slen, sizeof(xpath) - slen,
"[interface='*']");
if (vrf)
snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", vrf);
else
snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']",
VRF_DEFAULT_NAME);
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
/* Apply settings immediatly. */
return nb_cli_apply_changes(vty, NULL);
}
static void _bfd_cli_show_peer(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults __attribute__((__unused__)),
bool mhop)
{
const char *vrf = yang_dnode_get_string(dnode, "vrf");
vty_out(vty, " peer %s",
yang_dnode_get_string(dnode, "dest-addr"));
if (mhop)
vty_out(vty, " multihop");
if (yang_dnode_exists(dnode, "source-addr"))
vty_out(vty, " local-address %s",
yang_dnode_get_string(dnode, "source-addr"));
if (strcmp(vrf, VRF_DEFAULT_NAME))
vty_out(vty, " vrf %s", vrf);
if (!mhop) {
const char *ifname =
yang_dnode_get_string(dnode, "interface");
if (strcmp(ifname, "*"))
vty_out(vty, " interface %s", ifname);
}
vty_out(vty, "\n");
}
void bfd_cli_show_single_hop_peer(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults)
{
_bfd_cli_show_peer(vty, dnode, show_defaults, false);
}
void bfd_cli_show_multi_hop_peer(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults)
{
_bfd_cli_show_peer(vty, dnode, show_defaults, true);
}
void bfd_cli_show_peer_end(struct vty *vty, const struct lyd_node *dnode
__attribute__((__unused__)))
{
vty_out(vty, " exit\n");
vty_out(vty, " !\n");
}
DEFPY_YANG(
bfd_peer_shutdown, bfd_peer_shutdown_cmd,
"[no] shutdown",
NO_STR
"Disable BFD peer\n")
{
nb_cli_enqueue_change(vty, "./administrative-down", NB_OP_MODIFY,
no ? "false" : "true");
return nb_cli_apply_changes(vty, NULL);
}
void bfd_cli_show_shutdown(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults)
{
vty_out(vty, " %sshutdown\n",
yang_dnode_get_bool(dnode, NULL) ? "" : "no ");
}
DEFPY_YANG(
bfd_peer_passive, bfd_peer_passive_cmd,
"[no] passive-mode",
NO_STR
"Don't attempt to start sessions\n")
{
nb_cli_enqueue_change(vty, "./passive-mode", NB_OP_MODIFY,
no ? "false" : "true");
return nb_cli_apply_changes(vty, NULL);
}
void bfd_cli_show_passive(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults)
{
vty_out(vty, " %spassive-mode\n",
yang_dnode_get_bool(dnode, NULL) ? "" : "no ");
}
DEFPY_YANG(
bfd_peer_minimum_ttl, bfd_peer_minimum_ttl_cmd,
"[no] minimum-ttl (1-254)$ttl",
NO_STR
"Expect packets with at least this TTL\n"
"Minimum TTL expected\n")
{
if (bfd_cli_is_single_hop(vty)) {
vty_out(vty, "%% Minimum TTL is only available for multi hop sessions.\n");
return CMD_WARNING_CONFIG_FAILED;
}
if (no)
nb_cli_enqueue_change(vty, "./minimum-ttl", NB_OP_DESTROY,
NULL);
else
nb_cli_enqueue_change(vty, "./minimum-ttl", NB_OP_MODIFY,
ttl_str);
return nb_cli_apply_changes(vty, NULL);
}
DEFPY_YANG(
no_bfd_peer_minimum_ttl, no_bfd_peer_minimum_ttl_cmd,
"no minimum-ttl",
NO_STR
"Expect packets with at least this TTL\n")
{
nb_cli_enqueue_change(vty, "./minimum-ttl", NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, NULL);
}
void bfd_cli_show_minimum_ttl(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults)
{
vty_out(vty, " minimum-ttl %s\n", yang_dnode_get_string(dnode, NULL));
}
DEFPY_YANG(
bfd_peer_mult, bfd_peer_mult_cmd,
"detect-multiplier (2-255)$multiplier",
"Configure peer detection multiplier\n"
"Configure peer detection multiplier value\n")
{
nb_cli_enqueue_change(vty, "./detection-multiplier", NB_OP_MODIFY,
multiplier_str);
return nb_cli_apply_changes(vty, NULL);
}
void bfd_cli_show_mult(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults)
{
vty_out(vty, " detect-multiplier %s\n",
yang_dnode_get_string(dnode, NULL));
}
DEFPY_YANG(
bfd_peer_rx, bfd_peer_rx_cmd,
"receive-interval (10-60000)$interval",
"Configure peer receive interval\n"
"Configure peer receive interval value in milliseconds\n")
{
char value[32];
snprintf(value, sizeof(value), "%ld", interval * 1000);
nb_cli_enqueue_change(vty, "./required-receive-interval", NB_OP_MODIFY,
value);
return nb_cli_apply_changes(vty, NULL);
}
void bfd_cli_show_rx(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults)
{
uint32_t value = yang_dnode_get_uint32(dnode, NULL);
vty_out(vty, " receive-interval %u\n", value / 1000);
}
DEFPY_YANG(
bfd_peer_tx, bfd_peer_tx_cmd,
"transmit-interval (10-60000)$interval",
"Configure peer transmit interval\n"
"Configure peer transmit interval value in milliseconds\n")
{
char value[32];
snprintf(value, sizeof(value), "%ld", interval * 1000);
nb_cli_enqueue_change(vty, "./desired-transmission-interval",
NB_OP_MODIFY, value);
return nb_cli_apply_changes(vty, NULL);
}
void bfd_cli_show_tx(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults)
{
uint32_t value = yang_dnode_get_uint32(dnode, NULL);
vty_out(vty, " transmit-interval %u\n", value / 1000);
}
DEFPY_YANG(
bfd_peer_echo, bfd_peer_echo_cmd,
"[no] echo-mode",
NO_STR
"Configure echo mode\n")
{
if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty)) {
vty_out(vty,
"%% Echo mode is only available for single hop sessions.\n");
return CMD_WARNING_CONFIG_FAILED;
}
if (!no && !bglobal.bg_use_dplane) {
#ifdef BFD_LINUX
vty_out(vty,
"%% Echo mode works correctly for IPv4, but only works when the peer is also FRR for IPv6.\n");
#else
vty_out(vty,
"%% Current implementation of echo mode works only when the peer is also FRR.\n");
#endif /* BFD_LINUX */
}
nb_cli_enqueue_change(vty, "./echo-mode", NB_OP_MODIFY,
no ? "false" : "true");
return nb_cli_apply_changes(vty, NULL);
}
void bfd_cli_show_echo(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults)
{
vty_out(vty, " %secho-mode\n",
yang_dnode_get_bool(dnode, NULL) ? "" : "no ");
}
DEFPY_YANG(
bfd_peer_echo_interval, bfd_peer_echo_interval_cmd,
"echo-interval (10-60000)$interval",
"Configure peer echo intervals\n"
"Configure peer echo rx/tx intervals value in milliseconds\n")
{
char value[32];
if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty)) {
vty_out(vty, "%% Echo mode is only available for single hop sessions.\n");
return CMD_WARNING_CONFIG_FAILED;
}
snprintf(value, sizeof(value), "%ld", interval * 1000);
nb_cli_enqueue_change(vty, "./desired-echo-transmission-interval",
NB_OP_MODIFY, value);
nb_cli_enqueue_change(vty, "./required-echo-receive-interval",
NB_OP_MODIFY, value);
return nb_cli_apply_changes(vty, NULL);
}
DEFPY_YANG(
bfd_peer_echo_transmit_interval, bfd_peer_echo_transmit_interval_cmd,
"echo transmit-interval (10-60000)$interval",
"Configure peer echo intervals\n"
"Configure desired transmit interval\n"
"Configure interval value in milliseconds\n")
{
char value[32];
if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty)) {
vty_out(vty, "%% Echo mode is only available for single hop sessions.\n");
return CMD_WARNING_CONFIG_FAILED;
}
snprintf(value, sizeof(value), "%ld", interval * 1000);
nb_cli_enqueue_change(vty, "./desired-echo-transmission-interval",
NB_OP_MODIFY, value);
return nb_cli_apply_changes(vty, NULL);
}
void bfd_cli_show_desired_echo_transmission_interval(
struct vty *vty, const struct lyd_node *dnode, bool show_defaults)
{
uint32_t value = yang_dnode_get_uint32(dnode, NULL);
vty_out(vty, " echo transmit-interval %u\n", value / 1000);
}
DEFPY_YANG(
bfd_peer_echo_receive_interval, bfd_peer_echo_receive_interval_cmd,
"echo receive-interval <disabled$disabled|(10-60000)$interval>",
"Configure peer echo intervals\n"
"Configure required receive interval\n"
"Disable echo packets receive\n"
"Configure interval value in milliseconds\n")
{
char value[32];
if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty)) {
vty_out(vty, "%% Echo mode is only available for single hop sessions.\n");
return CMD_WARNING_CONFIG_FAILED;
}
if (disabled)
snprintf(value, sizeof(value), "0");
else
snprintf(value, sizeof(value), "%ld", interval * 1000);
nb_cli_enqueue_change(vty, "./required-echo-receive-interval",
NB_OP_MODIFY, value);
return nb_cli_apply_changes(vty, NULL);
}
void bfd_cli_show_required_echo_receive_interval(struct vty *vty,
const struct lyd_node *dnode,
bool show_defaults)
{
uint32_t value = yang_dnode_get_uint32(dnode, NULL);
if (value)
vty_out(vty, " echo receive-interval %u\n", value / 1000);
else
vty_out(vty, " echo receive-interval disabled\n");
}
/*
* Profile commands.
*/
DEFPY_YANG_NOSH(bfd_profile, bfd_profile_cmd,
"profile BFDPROF$name",
BFD_PROFILE_STR
BFD_PROFILE_NAME_STR)
{
char xpath[XPATH_MAXLEN];
int rv;
snprintf(xpath, sizeof(xpath), "/frr-bfdd:bfdd/bfd/profile[name='%s']",
name);
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
/* Apply settings immediately. */
rv = nb_cli_apply_changes(vty, NULL);
if (rv == CMD_SUCCESS)
VTY_PUSH_XPATH(BFD_PROFILE_NODE, xpath);
return CMD_SUCCESS;
}
DEFPY_YANG(no_bfd_profile, no_bfd_profile_cmd,
"no profile BFDPROF$name",
NO_STR
BFD_PROFILE_STR
BFD_PROFILE_NAME_STR)
{
char xpath[XPATH_MAXLEN];
snprintf(xpath, sizeof(xpath), "/frr-bfdd:bfdd/bfd/profile[name='%s']",
name);
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
/* Apply settings immediately. */
return nb_cli_apply_changes(vty, NULL);
}
void bfd_cli_show_profile(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults)
{
vty_out(vty, " profile %s\n", yang_dnode_get_string(dnode, "name"));
}
ALIAS_YANG(bfd_peer_mult, bfd_profile_mult_cmd,
"detect-multiplier (2-255)$multiplier",
"Configure peer detection multiplier\n"
"Configure peer detection multiplier value\n")
ALIAS_YANG(bfd_peer_tx, bfd_profile_tx_cmd,
"transmit-interval (10-60000)$interval",
"Configure peer transmit interval\n"
"Configure peer transmit interval value in milliseconds\n")
ALIAS_YANG(bfd_peer_rx, bfd_profile_rx_cmd,
"receive-interval (10-60000)$interval",
"Configure peer receive interval\n"
"Configure peer receive interval value in milliseconds\n")
ALIAS_YANG(bfd_peer_shutdown, bfd_profile_shutdown_cmd,
"[no] shutdown",
NO_STR
"Disable BFD peer\n")
ALIAS_YANG(bfd_peer_passive, bfd_profile_passive_cmd,
"[no] passive-mode",
NO_STR
"Don't attempt to start sessions\n")
ALIAS_YANG(bfd_peer_minimum_ttl, bfd_profile_minimum_ttl_cmd,
"[no] minimum-ttl (1-254)$ttl",
NO_STR
"Expect packets with at least this TTL\n"
"Minimum TTL expected\n")
ALIAS_YANG(no_bfd_peer_minimum_ttl, no_bfd_profile_minimum_ttl_cmd,
"no minimum-ttl",
NO_STR
"Expect packets with at least this TTL\n")
ALIAS_YANG(bfd_peer_echo, bfd_profile_echo_cmd,
"[no] echo-mode",
NO_STR
"Configure echo mode\n")
ALIAS_YANG(bfd_peer_echo_interval, bfd_profile_echo_interval_cmd,
"echo-interval (10-60000)$interval",
"Configure peer echo interval\n"
"Configure peer echo interval value in milliseconds\n")
ALIAS_YANG(
bfd_peer_echo_transmit_interval, bfd_profile_echo_transmit_interval_cmd,
"echo transmit-interval (10-60000)$interval",
"Configure peer echo intervals\n"
"Configure desired transmit interval\n"
"Configure interval value in milliseconds\n")
ALIAS_YANG(
bfd_peer_echo_receive_interval, bfd_profile_echo_receive_interval_cmd,
"echo receive-interval <disabled$disabled|(10-60000)$interval>",
"Configure peer echo intervals\n"
"Configure required receive interval\n"
"Disable echo packets receive\n"
"Configure interval value in milliseconds\n")
DEFPY_YANG(bfd_peer_profile, bfd_peer_profile_cmd,
"[no] profile BFDPROF$pname",
NO_STR
"Use BFD profile settings\n"
BFD_PROFILE_NAME_STR)
{
if (no)
nb_cli_enqueue_change(vty, "./profile", NB_OP_DESTROY, NULL);
else
nb_cli_enqueue_change(vty, "./profile", NB_OP_MODIFY, pname);
return nb_cli_apply_changes(vty, NULL);
}
void bfd_cli_peer_profile_show(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults)
{
vty_out(vty, " profile %s\n", yang_dnode_get_string(dnode, NULL));
}
struct cmd_node bfd_profile_node = {
.name = "bfd profile",
.node = BFD_PROFILE_NODE,
.parent_node = BFD_NODE,
.prompt = "%s(config-bfd-profile)# ",
};
static void bfd_profile_var(vector comps, struct cmd_token *token)
{
extern struct bfdproflist bplist;
struct bfd_profile *bp;
TAILQ_FOREACH (bp, &bplist, entry) {
vector_set(comps, XSTRDUP(MTYPE_COMPLETION, bp->name));
}
}
static const struct cmd_variable_handler bfd_vars[] = {
{.tokenname = "BFDPROF", .completions = bfd_profile_var},
{.completions = NULL}
};
void
bfdd_cli_init(void)
{
install_element(CONFIG_NODE, &bfd_enter_cmd);
install_element(CONFIG_NODE, &bfd_config_reset_cmd);
install_element(BFD_NODE, &bfd_peer_enter_cmd);
install_element(BFD_NODE, &bfd_no_peer_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_shutdown_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_mult_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_rx_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_tx_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_echo_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_echo_interval_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_echo_transmit_interval_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_echo_receive_interval_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_profile_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_passive_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_minimum_ttl_cmd);
install_element(BFD_PEER_NODE, &no_bfd_peer_minimum_ttl_cmd);
/* Profile commands. */
cmd_variable_handler_register(bfd_vars);
install_node(&bfd_profile_node);
install_default(BFD_PROFILE_NODE);
install_element(BFD_NODE, &bfd_profile_cmd);
install_element(BFD_NODE, &no_bfd_profile_cmd);
install_element(BFD_PROFILE_NODE, &bfd_profile_mult_cmd);
install_element(BFD_PROFILE_NODE, &bfd_profile_tx_cmd);
install_element(BFD_PROFILE_NODE, &bfd_profile_rx_cmd);
install_element(BFD_PROFILE_NODE, &bfd_profile_shutdown_cmd);
install_element(BFD_PROFILE_NODE, &bfd_profile_echo_cmd);
install_element(BFD_PROFILE_NODE, &bfd_profile_echo_interval_cmd);
install_element(BFD_PROFILE_NODE, &bfd_profile_echo_transmit_interval_cmd);
install_element(BFD_PROFILE_NODE, &bfd_profile_echo_receive_interval_cmd);
install_element(BFD_PROFILE_NODE, &bfd_profile_passive_cmd);
install_element(BFD_PROFILE_NODE, &bfd_profile_minimum_ttl_cmd);
install_element(BFD_PROFILE_NODE, &no_bfd_profile_minimum_ttl_cmd);
}

490
bfdd/bfdd_nb.c Normal file
View file

@ -0,0 +1,490 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* BFD daemon northbound implementation.
*
* Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
* Rafael Zalamena
*/
#include <zebra.h>
#include "lib/log.h"
#include "lib/northbound.h"
#include "bfdd_nb.h"
/* clang-format off */
const struct frr_yang_module_info frr_bfdd_info = {
.name = "frr-bfdd",
.nodes = {
{
.xpath = "/frr-bfdd:bfdd/bfd",
.cbs = {
.create = bfdd_bfd_create,
.destroy = bfdd_bfd_destroy,
.cli_show = bfd_cli_show_header,
.cli_show_end = bfd_cli_show_header_end,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/profile",
.cbs = {
.create = bfdd_bfd_profile_create,
.destroy = bfdd_bfd_profile_destroy,
.cli_show = bfd_cli_show_profile,
.cli_show_end = bfd_cli_show_peer_end,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/profile/detection-multiplier",
.cbs = {
.modify = bfdd_bfd_profile_detection_multiplier_modify,
.cli_show = bfd_cli_show_mult,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/profile/desired-transmission-interval",
.cbs = {
.modify = bfdd_bfd_profile_desired_transmission_interval_modify,
.cli_show = bfd_cli_show_tx,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/profile/required-receive-interval",
.cbs = {
.modify = bfdd_bfd_profile_required_receive_interval_modify,
.cli_show = bfd_cli_show_rx,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/profile/administrative-down",
.cbs = {
.modify = bfdd_bfd_profile_administrative_down_modify,
.cli_show = bfd_cli_show_shutdown,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/profile/passive-mode",
.cbs = {
.modify = bfdd_bfd_profile_passive_mode_modify,
.cli_show = bfd_cli_show_passive,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/profile/minimum-ttl",
.cbs = {
.modify = bfdd_bfd_profile_minimum_ttl_modify,
.cli_show = bfd_cli_show_minimum_ttl,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/profile/echo-mode",
.cbs = {
.modify = bfdd_bfd_profile_echo_mode_modify,
.cli_show = bfd_cli_show_echo,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/profile/desired-echo-transmission-interval",
.cbs = {
.modify = bfdd_bfd_profile_desired_echo_transmission_interval_modify,
.cli_show = bfd_cli_show_desired_echo_transmission_interval,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/profile/required-echo-receive-interval",
.cbs = {
.modify = bfdd_bfd_profile_required_echo_receive_interval_modify,
.cli_show = bfd_cli_show_required_echo_receive_interval,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop",
.cbs = {
.create = bfdd_bfd_sessions_single_hop_create,
.destroy = bfdd_bfd_sessions_single_hop_destroy,
.get_next = bfdd_bfd_sessions_single_hop_get_next,
.get_keys = bfdd_bfd_sessions_single_hop_get_keys,
.lookup_entry = bfdd_bfd_sessions_single_hop_lookup_entry,
.cli_show = bfd_cli_show_single_hop_peer,
.cli_show_end = bfd_cli_show_peer_end,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/source-addr",
.cbs = {
.modify = bfdd_bfd_sessions_single_hop_source_addr_modify,
.destroy = bfdd_bfd_sessions_single_hop_source_addr_destroy,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/profile",
.cbs = {
.modify = bfdd_bfd_sessions_single_hop_profile_modify,
.destroy = bfdd_bfd_sessions_single_hop_profile_destroy,
.cli_show = bfd_cli_peer_profile_show,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/detection-multiplier",
.cbs = {
.modify = bfdd_bfd_sessions_single_hop_detection_multiplier_modify,
.cli_show = bfd_cli_show_mult,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/desired-transmission-interval",
.cbs = {
.modify = bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify,
.cli_show = bfd_cli_show_tx,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/required-receive-interval",
.cbs = {
.modify = bfdd_bfd_sessions_single_hop_required_receive_interval_modify,
.cli_show = bfd_cli_show_rx,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/administrative-down",
.cbs = {
.modify = bfdd_bfd_sessions_single_hop_administrative_down_modify,
.cli_show = bfd_cli_show_shutdown,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/passive-mode",
.cbs = {
.modify = bfdd_bfd_sessions_single_hop_passive_mode_modify,
.cli_show = bfd_cli_show_passive,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/echo-mode",
.cbs = {
.modify = bfdd_bfd_sessions_single_hop_echo_mode_modify,
.cli_show = bfd_cli_show_echo,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/desired-echo-transmission-interval",
.cbs = {
.modify = bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify,
.cli_show = bfd_cli_show_desired_echo_transmission_interval,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/required-echo-receive-interval",
.cbs = {
.modify = bfdd_bfd_sessions_single_hop_required_echo_receive_interval_modify,
.cli_show = bfd_cli_show_required_echo_receive_interval,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-discriminator",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-state",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_local_state_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-diagnostic",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_local_diagnostic_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-multiplier",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_local_multiplier_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-discriminator",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_remote_discriminator_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-state",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_remote_state_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-diagnostic",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_remote_diagnostic_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-multiplier",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_remote_multiplier_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-transmission-interval",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_transmission_interval_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-receive-interval",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_receive_interval_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/detection-mode",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_detection_mode_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/last-down-time",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_last_down_time_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/last-up-time",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_last_up_time_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/session-down-count",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_session_down_count_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/session-up-count",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_session_up_count_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/control-packet-input-count",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_input_count_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/control-packet-output-count",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_output_count_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-echo-transmission-interval",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_echo_transmission_interval_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/echo-packet-input-count",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_input_count_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/echo-packet-output-count",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop",
.cbs = {
.create = bfdd_bfd_sessions_multi_hop_create,
.destroy = bfdd_bfd_sessions_multi_hop_destroy,
.get_next = bfdd_bfd_sessions_multi_hop_get_next,
.get_keys = bfdd_bfd_sessions_multi_hop_get_keys,
.lookup_entry = bfdd_bfd_sessions_multi_hop_lookup_entry,
.cli_show = bfd_cli_show_multi_hop_peer,
.cli_show_end = bfd_cli_show_peer_end,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/profile",
.cbs = {
.modify = bfdd_bfd_sessions_single_hop_profile_modify,
.destroy = bfdd_bfd_sessions_single_hop_profile_destroy,
.cli_show = bfd_cli_peer_profile_show,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/detection-multiplier",
.cbs = {
.modify = bfdd_bfd_sessions_single_hop_detection_multiplier_modify,
.cli_show = bfd_cli_show_mult,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/desired-transmission-interval",
.cbs = {
.modify = bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify,
.cli_show = bfd_cli_show_tx,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/required-receive-interval",
.cbs = {
.modify = bfdd_bfd_sessions_single_hop_required_receive_interval_modify,
.cli_show = bfd_cli_show_rx,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/administrative-down",
.cbs = {
.modify = bfdd_bfd_sessions_single_hop_administrative_down_modify,
.cli_show = bfd_cli_show_shutdown,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/passive-mode",
.cbs = {
.modify = bfdd_bfd_sessions_single_hop_passive_mode_modify,
.cli_show = bfd_cli_show_passive,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/minimum-ttl",
.cbs = {
.modify = bfdd_bfd_sessions_multi_hop_minimum_ttl_modify,
.cli_show = bfd_cli_show_minimum_ttl,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/local-discriminator",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/local-state",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_local_state_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/local-diagnostic",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_local_diagnostic_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/local-multiplier",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_local_multiplier_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/remote-discriminator",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_remote_discriminator_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/remote-state",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_remote_state_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/remote-diagnostic",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_remote_diagnostic_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/remote-multiplier",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_remote_multiplier_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/negotiated-transmission-interval",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_transmission_interval_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/negotiated-receive-interval",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_receive_interval_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/detection-mode",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_detection_mode_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/last-down-time",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_last_down_time_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/last-up-time",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_last_up_time_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/session-down-count",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_session_down_count_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/session-up-count",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_session_up_count_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/control-packet-input-count",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_input_count_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/control-packet-output-count",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_output_count_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/negotiated-echo-transmission-interval",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_echo_transmission_interval_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/echo-packet-input-count",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_input_count_get_elem,
}
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/echo-packet-output-count",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem,
}
},
{
.xpath = NULL,
},
}
};

213
bfdd/bfdd_nb.h Normal file
View file

@ -0,0 +1,213 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* BFD daemon northbound implementation.
*
* Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
* Rafael Zalamena
*/
#ifndef _FRR_BFDD_NB_H_
#define _FRR_BFDD_NB_H_
extern const struct frr_yang_module_info frr_bfdd_info;
/* Mandatory callbacks. */
int bfdd_bfd_create(struct nb_cb_create_args *args);
int bfdd_bfd_destroy(struct nb_cb_destroy_args *args);
int bfdd_bfd_profile_create(struct nb_cb_create_args *args);
int bfdd_bfd_profile_destroy(struct nb_cb_destroy_args *args);
int bfdd_bfd_profile_detection_multiplier_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_profile_desired_transmission_interval_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_profile_required_receive_interval_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_profile_administrative_down_modify(struct nb_cb_modify_args *args);
int bfdd_bfd_profile_passive_mode_modify(struct nb_cb_modify_args *args);
int bfdd_bfd_profile_minimum_ttl_modify(struct nb_cb_modify_args *args);
int bfdd_bfd_profile_echo_mode_modify(struct nb_cb_modify_args *args);
int bfdd_bfd_profile_desired_echo_transmission_interval_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_profile_required_echo_receive_interval_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_single_hop_create(struct nb_cb_create_args *args);
int bfdd_bfd_sessions_single_hop_destroy(struct nb_cb_destroy_args *args);
const void *
bfdd_bfd_sessions_single_hop_get_next(struct nb_cb_get_next_args *args);
int bfdd_bfd_sessions_single_hop_get_keys(struct nb_cb_get_keys_args *args);
const void *
bfdd_bfd_sessions_single_hop_lookup_entry(struct nb_cb_lookup_entry_args *args);
int bfdd_bfd_sessions_single_hop_source_addr_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_single_hop_source_addr_destroy(
struct nb_cb_destroy_args *args);
int bfdd_bfd_sessions_single_hop_profile_modify(struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_single_hop_profile_destroy(
struct nb_cb_destroy_args *args);
int bfdd_bfd_sessions_single_hop_detection_multiplier_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_single_hop_required_receive_interval_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_single_hop_administrative_down_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_single_hop_passive_mode_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_single_hop_echo_mode_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_single_hop_required_echo_receive_interval_modify(
struct nb_cb_modify_args *args);
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_single_hop_stats_local_state_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_single_hop_stats_local_diagnostic_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_single_hop_stats_local_multiplier_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_remote_discriminator_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_single_hop_stats_remote_state_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_single_hop_stats_remote_diagnostic_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_single_hop_stats_remote_multiplier_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_negotiated_transmission_interval_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_negotiated_receive_interval_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_single_hop_stats_detection_mode_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_single_hop_stats_last_down_time_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_single_hop_stats_last_up_time_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_session_down_count_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_single_hop_stats_session_up_count_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_control_packet_input_count_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_control_packet_output_count_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_negotiated_echo_transmission_interval_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_echo_packet_input_count_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem(
struct nb_cb_get_elem_args *args);
int bfdd_bfd_sessions_multi_hop_create(struct nb_cb_create_args *args);
int bfdd_bfd_sessions_multi_hop_destroy(struct nb_cb_destroy_args *args);
const void *
bfdd_bfd_sessions_multi_hop_get_next(struct nb_cb_get_next_args *args);
int bfdd_bfd_sessions_multi_hop_get_keys(struct nb_cb_get_keys_args *args);
const void *
bfdd_bfd_sessions_multi_hop_lookup_entry(struct nb_cb_lookup_entry_args *args);
int bfdd_bfd_sessions_multi_hop_detection_multiplier_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_multi_hop_desired_transmission_interval_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_multi_hop_required_receive_interval_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_multi_hop_administrative_down_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_multi_hop_minimum_ttl_modify(
struct nb_cb_modify_args *args);
struct yang_data *
bfdd_bfd_sessions_multi_hop_stats_local_discriminator_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_multi_hop_stats_local_state_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_multi_hop_stats_local_diagnostic_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_multi_hop_stats_local_multiplier_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
bfdd_bfd_sessions_multi_hop_stats_remote_discriminator_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_multi_hop_stats_remote_state_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_multi_hop_stats_remote_diagnostic_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_multi_hop_stats_remote_multiplier_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
bfdd_bfd_sessions_multi_hop_stats_negotiated_transmission_interval_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
bfdd_bfd_sessions_multi_hop_stats_negotiated_receive_interval_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_multi_hop_stats_detection_mode_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_multi_hop_stats_last_down_time_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_multi_hop_stats_last_up_time_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_multi_hop_stats_session_down_count_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *bfdd_bfd_sessions_multi_hop_stats_session_up_count_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
bfdd_bfd_sessions_multi_hop_stats_control_packet_input_count_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
bfdd_bfd_sessions_multi_hop_stats_control_packet_output_count_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
bfdd_bfd_sessions_multi_hop_stats_negotiated_echo_transmission_interval_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
bfdd_bfd_sessions_multi_hop_stats_echo_packet_input_count_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
bfdd_bfd_sessions_multi_hop_stats_echo_packet_output_count_get_elem(
struct nb_cb_get_elem_args *args);
/* Optional 'cli_show' callbacks. */
void bfd_cli_show_header(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
void bfd_cli_show_header_end(struct vty *vty, const struct lyd_node *dnode);
void bfd_cli_show_single_hop_peer(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
void bfd_cli_show_multi_hop_peer(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
void bfd_cli_show_peer_end(struct vty *vty, const struct lyd_node *dnode);
void bfd_cli_show_mult(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
void bfd_cli_show_tx(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
void bfd_cli_show_rx(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
void bfd_cli_show_shutdown(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
void bfd_cli_show_echo(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
void bfd_cli_show_desired_echo_transmission_interval(
struct vty *vty, const struct lyd_node *dnode, bool show_defaults);
void bfd_cli_show_required_echo_receive_interval(struct vty *vty,
const struct lyd_node *dnode,
bool show_defaults);
void bfd_cli_show_profile(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
void bfd_cli_peer_profile_show(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
void bfd_cli_show_passive(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
void bfd_cli_show_minimum_ttl(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
#endif /* _FRR_BFDD_NB_H_ */

847
bfdd/bfdd_nb_config.c Normal file
View file

@ -0,0 +1,847 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* BFD daemon northbound implementation.
*
* Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
* Rafael Zalamena
*/
#include <zebra.h>
#include "lib/log.h"
#include "lib/northbound.h"
#include "bfd.h"
#include "bfdd_nb.h"
/*
* Helpers.
*/
static void bfd_session_get_key(bool mhop, const struct lyd_node *dnode,
struct bfd_key *bk)
{
const char *ifname = NULL, *vrfname = NULL;
struct sockaddr_any psa, lsa;
/* Required destination parameter. */
strtosa(yang_dnode_get_string(dnode, "dest-addr"), &psa);
/* Get optional source address. */
memset(&lsa, 0, sizeof(lsa));
if (yang_dnode_exists(dnode, "source-addr"))
strtosa(yang_dnode_get_string(dnode, "source-addr"), &lsa);
vrfname = yang_dnode_get_string(dnode, "vrf");
if (!mhop) {
ifname = yang_dnode_get_string(dnode, "interface");
if (strcmp(ifname, "*") == 0)
ifname = NULL;
}
/* Generate the corresponding key. */
gen_bfd_key(bk, &psa, &lsa, mhop, ifname, vrfname);
}
struct session_iter {
int count;
bool wildcard;
};
static int session_iter_cb(const struct lyd_node *dnode, void *arg)
{
struct session_iter *iter = arg;
const char *ifname;
ifname = yang_dnode_get_string(dnode, "interface");
if (strmatch(ifname, "*"))
iter->wildcard = true;
iter->count++;
return YANG_ITER_CONTINUE;
}
static int bfd_session_create(struct nb_cb_create_args *args, bool mhop)
{
const struct lyd_node *sess_dnode;
struct session_iter iter;
struct bfd_session *bs;
const char *dest;
const char *ifname;
const char *vrfname;
struct bfd_key bk;
struct prefix p;
switch (args->event) {
case NB_EV_VALIDATE:
yang_dnode_get_prefix(&p, args->dnode, "dest-addr");
if (mhop) {
/*
* Do not allow IPv6 link-local address for multihop.
*/
if (p.family == AF_INET6
&& IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) {
snprintf(
args->errmsg, args->errmsg_len,
"Cannot use link-local address for multihop sessions");
return NB_ERR_VALIDATION;
}
return NB_OK;
}
/*
* When `dest-addr` is IPv6 and link-local we must
* require interface name, otherwise we can't figure
* which interface to use to send the packets.
*/
ifname = yang_dnode_get_string(args->dnode, "interface");
if (p.family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)
&& strcmp(ifname, "*") == 0) {
snprintf(
args->errmsg, args->errmsg_len,
"When using link-local you must specify an interface");
return NB_ERR_VALIDATION;
}
iter.count = 0;
iter.wildcard = false;
sess_dnode = yang_dnode_get_parent(args->dnode, "sessions");
dest = yang_dnode_get_string(args->dnode, "dest-addr");
vrfname = yang_dnode_get_string(args->dnode, "vrf");
yang_dnode_iterate(session_iter_cb, &iter, sess_dnode,
"./single-hop[dest-addr='%s'][vrf='%s']",
dest, vrfname);
if (iter.wildcard && iter.count > 1) {
snprintf(
args->errmsg, args->errmsg_len,
"It is not allowed to configure the same peer with and without ifname");
return NB_ERR_VALIDATION;
}
break;
case NB_EV_PREPARE:
bfd_session_get_key(mhop, args->dnode, &bk);
bs = bfd_key_lookup(bk);
/* This session was already configured by another daemon. */
if (bs != NULL) {
/* Now it is configured also by CLI. */
SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG);
bs->refcount++;
args->resource->ptr = bs;
break;
}
bs = bfd_session_new();
/* Fill the session key. */
bfd_session_get_key(mhop, args->dnode, &bs->key);
/* Set configuration flags. */
bs->refcount = 1;
SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG);
if (mhop)
SET_FLAG(bs->flags, BFD_SESS_FLAG_MH);
if (bs->key.family == AF_INET6)
SET_FLAG(bs->flags, BFD_SESS_FLAG_IPV6);
args->resource->ptr = bs;
break;
case NB_EV_APPLY:
bs = args->resource->ptr;
/* Only attempt to registrate if freshly allocated. */
if (bs->discrs.my_discr == 0 && bs_registrate(bs) == NULL)
return NB_ERR_RESOURCE;
nb_running_set_entry(args->dnode, bs);
break;
case NB_EV_ABORT:
bs = args->resource->ptr;
if (bs->refcount <= 1)
bfd_session_free(bs);
break;
}
return NB_OK;
}
static int bfd_session_destroy(enum nb_event event,
const struct lyd_node *dnode, bool mhop)
{
struct bfd_session *bs;
struct bfd_key bk;
switch (event) {
case NB_EV_VALIDATE:
bfd_session_get_key(mhop, dnode, &bk);
if (bfd_key_lookup(bk) == NULL)
return NB_ERR_INCONSISTENCY;
break;
case NB_EV_PREPARE:
/* NOTHING */
break;
case NB_EV_APPLY:
bs = nb_running_unset_entry(dnode);
/* CLI is not using this session anymore. */
if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) == 0)
break;
UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG);
bs->refcount--;
/* There are still daemons using it. */
if (bs->refcount > 0)
break;
bfd_session_free(bs);
break;
case NB_EV_ABORT:
/* NOTHING */
break;
}
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd
*/
int bfdd_bfd_create(struct nb_cb_create_args *args)
{
if (args->event != NB_EV_APPLY)
return NB_OK;
/*
* Set any non-NULL value to be able to call
* nb_running_unset_entry in bfdd_bfd_destroy.
*/
nb_running_set_entry(args->dnode, (void *)0x1);
return NB_OK;
}
int bfdd_bfd_destroy(struct nb_cb_destroy_args *args)
{
switch (args->event) {
case NB_EV_VALIDATE:
/* NOTHING */
return NB_OK;
case NB_EV_PREPARE:
/* NOTHING */
return NB_OK;
case NB_EV_APPLY:
/*
* We need to call this to unset pointers from
* the child nodes - sessions and profiles.
*/
nb_running_unset_entry(args->dnode);
bfd_sessions_remove_manual();
bfd_profiles_remove();
break;
case NB_EV_ABORT:
/* NOTHING */
return NB_OK;
}
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/profile
*/
int bfdd_bfd_profile_create(struct nb_cb_create_args *args)
{
struct bfd_profile *bp;
const char *name;
if (args->event != NB_EV_APPLY)
return NB_OK;
name = yang_dnode_get_string(args->dnode, "name");
bp = bfd_profile_new(name);
nb_running_set_entry(args->dnode, bp);
return NB_OK;
}
int bfdd_bfd_profile_destroy(struct nb_cb_destroy_args *args)
{
struct bfd_profile *bp;
if (args->event != NB_EV_APPLY)
return NB_OK;
bp = nb_running_unset_entry(args->dnode);
bfd_profile_free(bp);
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/profile/detection-multiplier
*/
int bfdd_bfd_profile_detection_multiplier_modify(struct nb_cb_modify_args *args)
{
struct bfd_profile *bp;
if (args->event != NB_EV_APPLY)
return NB_OK;
bp = nb_running_get_entry(args->dnode, NULL, true);
bp->detection_multiplier = yang_dnode_get_uint8(args->dnode, NULL);
bfd_profile_update(bp);
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/profile/desired-transmission-interval
*/
int bfdd_bfd_profile_desired_transmission_interval_modify(
struct nb_cb_modify_args *args)
{
struct bfd_profile *bp;
switch (args->event) {
case NB_EV_VALIDATE:
break;
case NB_EV_PREPARE:
/* NOTHING */
break;
case NB_EV_APPLY:
bp = nb_running_get_entry(args->dnode, NULL, true);
bp->min_tx = yang_dnode_get_uint32(args->dnode, NULL);
bfd_profile_update(bp);
break;
case NB_EV_ABORT:
/* NOTHING */
break;
}
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/profile/required-receive-interval
*/
int bfdd_bfd_profile_required_receive_interval_modify(
struct nb_cb_modify_args *args)
{
struct bfd_profile *bp;
switch (args->event) {
case NB_EV_VALIDATE:
break;
case NB_EV_PREPARE:
/* NOTHING */
break;
case NB_EV_APPLY:
bp = nb_running_get_entry(args->dnode, NULL, true);
bp->min_rx = yang_dnode_get_uint32(args->dnode, NULL);
bfd_profile_update(bp);
break;
case NB_EV_ABORT:
/* NOTHING */
break;
}
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/profile/administrative-down
*/
int bfdd_bfd_profile_administrative_down_modify(struct nb_cb_modify_args *args)
{
struct bfd_profile *bp;
if (args->event != NB_EV_APPLY)
return NB_OK;
bp = nb_running_get_entry(args->dnode, NULL, true);
bp->admin_shutdown = yang_dnode_get_bool(args->dnode, NULL);
bfd_profile_update(bp);
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/profile/passive-mode
*/
int bfdd_bfd_profile_passive_mode_modify(struct nb_cb_modify_args *args)
{
struct bfd_profile *bp;
if (args->event != NB_EV_APPLY)
return NB_OK;
bp = nb_running_get_entry(args->dnode, NULL, true);
bp->passive = yang_dnode_get_bool(args->dnode, NULL);
bfd_profile_update(bp);
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/profile/minimum-ttl
*/
int bfdd_bfd_profile_minimum_ttl_modify(struct nb_cb_modify_args *args)
{
struct bfd_profile *bp;
if (args->event != NB_EV_APPLY)
return NB_OK;
bp = nb_running_get_entry(args->dnode, NULL, true);
bp->minimum_ttl = yang_dnode_get_uint8(args->dnode, NULL);
bfd_profile_update(bp);
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/profile/echo-mode
*/
int bfdd_bfd_profile_echo_mode_modify(struct nb_cb_modify_args *args)
{
struct bfd_profile *bp;
bool echo;
if (args->event != NB_EV_APPLY)
return NB_OK;
echo = yang_dnode_get_bool(args->dnode, NULL);
bp = nb_running_get_entry(args->dnode, NULL, true);
if (bp->echo_mode == echo)
return NB_OK;
bp->echo_mode = echo;
bfd_profile_update(bp);
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/profile/desired-echo-transmission-interval
*/
int bfdd_bfd_profile_desired_echo_transmission_interval_modify(
struct nb_cb_modify_args *args)
{
struct bfd_profile *bp;
switch (args->event) {
case NB_EV_VALIDATE:
break;
case NB_EV_PREPARE:
/* NOTHING */
break;
case NB_EV_APPLY:
bp = nb_running_get_entry(args->dnode, NULL, true);
bp->min_echo_tx = yang_dnode_get_uint32(args->dnode, NULL);
bfd_profile_update(bp);
break;
case NB_EV_ABORT:
/* NOTHING */
break;
}
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/profile/required-echo-receive-interval
*/
int bfdd_bfd_profile_required_echo_receive_interval_modify(
struct nb_cb_modify_args *args)
{
struct bfd_profile *bp;
switch (args->event) {
case NB_EV_VALIDATE:
break;
case NB_EV_PREPARE:
/* NOTHING */
break;
case NB_EV_APPLY:
bp = nb_running_get_entry(args->dnode, NULL, true);
bp->min_echo_rx = yang_dnode_get_uint32(args->dnode, NULL);
bfd_profile_update(bp);
break;
case NB_EV_ABORT:
/* NOTHING */
break;
}
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop
*/
int bfdd_bfd_sessions_single_hop_create(struct nb_cb_create_args *args)
{
return bfd_session_create(args, false);
}
int bfdd_bfd_sessions_single_hop_destroy(struct nb_cb_destroy_args *args)
{
return bfd_session_destroy(args->event, args->dnode, false);
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/source-addr
*/
int bfdd_bfd_sessions_single_hop_source_addr_modify(
struct nb_cb_modify_args *args)
{
return NB_OK;
}
int bfdd_bfd_sessions_single_hop_source_addr_destroy(
struct nb_cb_destroy_args *args)
{
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/profile
*/
int bfdd_bfd_sessions_single_hop_profile_modify(struct nb_cb_modify_args *args)
{
struct bfd_session *bs;
if (args->event != NB_EV_APPLY)
return NB_OK;
bs = nb_running_get_entry(args->dnode, NULL, true);
bfd_profile_apply(yang_dnode_get_string(args->dnode, NULL), bs);
return NB_OK;
}
int bfdd_bfd_sessions_single_hop_profile_destroy(
struct nb_cb_destroy_args *args)
{
struct bfd_session *bs;
if (args->event != NB_EV_APPLY)
return NB_OK;
bs = nb_running_get_entry(args->dnode, NULL, true);
bfd_profile_remove(bs);
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/detection-multiplier
*/
int bfdd_bfd_sessions_single_hop_detection_multiplier_modify(
struct nb_cb_modify_args *args)
{
uint8_t detection_multiplier = yang_dnode_get_uint8(args->dnode, NULL);
struct bfd_session *bs;
switch (args->event) {
case NB_EV_VALIDATE:
break;
case NB_EV_PREPARE:
/* NOTHING */
break;
case NB_EV_APPLY:
bs = nb_running_get_entry(args->dnode, NULL, true);
bs->peer_profile.detection_multiplier = detection_multiplier;
bfd_session_apply(bs);
break;
case NB_EV_ABORT:
/* NOTHING */
break;
}
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/desired-transmission-interval
*/
int bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify(
struct nb_cb_modify_args *args)
{
struct bfd_session *bs;
switch (args->event) {
case NB_EV_VALIDATE:
break;
case NB_EV_PREPARE:
/* NOTHING */
break;
case NB_EV_APPLY:
bs = nb_running_get_entry(args->dnode, NULL, true);
bs->peer_profile.min_tx =
yang_dnode_get_uint32(args->dnode, NULL);
bfd_session_apply(bs);
break;
case NB_EV_ABORT:
/* NOTHING */
break;
}
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/required-receive-interval
*/
int bfdd_bfd_sessions_single_hop_required_receive_interval_modify(
struct nb_cb_modify_args *args)
{
struct bfd_session *bs;
switch (args->event) {
case NB_EV_VALIDATE:
break;
case NB_EV_PREPARE:
/* NOTHING */
break;
case NB_EV_APPLY:
bs = nb_running_get_entry(args->dnode, NULL, true);
bs->peer_profile.min_rx =
yang_dnode_get_uint32(args->dnode, NULL);
bfd_session_apply(bs);
break;
case NB_EV_ABORT:
/* NOTHING */
break;
}
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/administrative-down
*/
int bfdd_bfd_sessions_single_hop_administrative_down_modify(
struct nb_cb_modify_args *args)
{
bool shutdown = yang_dnode_get_bool(args->dnode, NULL);
struct bfd_session *bs;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
return NB_OK;
case NB_EV_APPLY:
break;
case NB_EV_ABORT:
return NB_OK;
}
bs = nb_running_get_entry(args->dnode, NULL, true);
bs->peer_profile.admin_shutdown = shutdown;
bfd_session_apply(bs);
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/passive-mode
*/
int bfdd_bfd_sessions_single_hop_passive_mode_modify(
struct nb_cb_modify_args *args)
{
struct bfd_session *bs;
bool passive;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
return NB_OK;
case NB_EV_APPLY:
break;
case NB_EV_ABORT:
return NB_OK;
}
passive = yang_dnode_get_bool(args->dnode, NULL);
bs = nb_running_get_entry(args->dnode, NULL, true);
bs->peer_profile.passive = passive;
bfd_session_apply(bs);
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/echo-mode
*/
int bfdd_bfd_sessions_single_hop_echo_mode_modify(
struct nb_cb_modify_args *args)
{
bool echo = yang_dnode_get_bool(args->dnode, NULL);
struct bfd_session *bs;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
return NB_OK;
case NB_EV_APPLY:
break;
case NB_EV_ABORT:
return NB_OK;
}
bs = nb_running_get_entry(args->dnode, NULL, true);
bs->peer_profile.echo_mode = echo;
bfd_session_apply(bs);
return NB_OK;
}
/*
* XPath:
* /frr-bfdd:bfdd/bfd/sessions/single-hop/desired-echo-transmission-interval
*/
int bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify(
struct nb_cb_modify_args *args)
{
struct bfd_session *bs;
switch (args->event) {
case NB_EV_VALIDATE:
break;
case NB_EV_PREPARE:
/* NOTHING */
break;
case NB_EV_APPLY:
bs = nb_running_get_entry(args->dnode, NULL, true);
bs->peer_profile.min_echo_tx =
yang_dnode_get_uint32(args->dnode, NULL);
bfd_session_apply(bs);
break;
case NB_EV_ABORT:
/* NOTHING */
break;
}
return NB_OK;
}
/*
* XPath:
* /frr-bfdd:bfdd/bfd/sessions/single-hop/required-echo-receive-interval
*/
int bfdd_bfd_sessions_single_hop_required_echo_receive_interval_modify(
struct nb_cb_modify_args *args)
{
struct bfd_session *bs;
switch (args->event) {
case NB_EV_VALIDATE:
break;
case NB_EV_PREPARE:
/* NOTHING */
break;
case NB_EV_APPLY:
bs = nb_running_get_entry(args->dnode, NULL, true);
bs->peer_profile.min_echo_rx =
yang_dnode_get_uint32(args->dnode, NULL);
bfd_session_apply(bs);
break;
case NB_EV_ABORT:
/* NOTHING */
break;
}
return NB_OK;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/multi-hop
*/
int bfdd_bfd_sessions_multi_hop_create(struct nb_cb_create_args *args)
{
return bfd_session_create(args, true);
}
int bfdd_bfd_sessions_multi_hop_destroy(struct nb_cb_destroy_args *args)
{
return bfd_session_destroy(args->event, args->dnode, true);
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/multi-hop/minimum-ttl
*/
int bfdd_bfd_sessions_multi_hop_minimum_ttl_modify(
struct nb_cb_modify_args *args)
{
struct bfd_session *bs;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
return NB_OK;
case NB_EV_APPLY:
break;
case NB_EV_ABORT:
return NB_OK;
}
bs = nb_running_get_entry(args->dnode, NULL, true);
bs->peer_profile.minimum_ttl = yang_dnode_get_uint8(args->dnode, NULL);
bfd_session_apply(bs);
return NB_OK;
}

360
bfdd/bfdd_nb_state.c Normal file
View file

@ -0,0 +1,360 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* BFD daemon northbound implementation.
*
* Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
* Rafael Zalamena
*/
#include <zebra.h>
#include "lib/log.h"
#include "lib/northbound.h"
#include "bfd.h"
#include "bfdd_nb.h"
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop
*/
const void *
bfdd_bfd_sessions_single_hop_get_next(struct nb_cb_get_next_args *args)
{
return bfd_session_next(args->list_entry, false);
}
int bfdd_bfd_sessions_single_hop_get_keys(struct nb_cb_get_keys_args *args)
{
const struct bfd_session *bs = args->list_entry;
char dstbuf[INET6_ADDRSTRLEN];
inet_ntop(bs->key.family, &bs->key.peer, dstbuf, sizeof(dstbuf));
args->keys->num = 3;
strlcpy(args->keys->key[0], dstbuf, sizeof(args->keys->key[0]));
strlcpy(args->keys->key[1], bs->key.ifname, sizeof(args->keys->key[1]));
strlcpy(args->keys->key[2], bs->key.vrfname,
sizeof(args->keys->key[2]));
return NB_OK;
}
const void *
bfdd_bfd_sessions_single_hop_lookup_entry(struct nb_cb_lookup_entry_args *args)
{
const char *dest_addr = args->keys->key[0];
const char *ifname = args->keys->key[1];
const char *vrf = args->keys->key[2];
struct sockaddr_any psa, lsa;
struct bfd_key bk;
strtosa(dest_addr, &psa);
memset(&lsa, 0, sizeof(lsa));
gen_bfd_key(&bk, &psa, &lsa, false, ifname, vrf);
return bfd_key_lookup(bk);
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-discriminator
*/
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem(
struct nb_cb_get_elem_args *args)
{
const struct bfd_session *bs = args->list_entry;
return yang_data_new_uint32(args->xpath, bs->discrs.my_discr);
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-state
*/
struct yang_data *bfdd_bfd_sessions_single_hop_stats_local_state_get_elem(
struct nb_cb_get_elem_args *args)
{
const struct bfd_session *bs = args->list_entry;
return yang_data_new_enum(args->xpath, bs->ses_state);
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-diagnostic
*/
struct yang_data *bfdd_bfd_sessions_single_hop_stats_local_diagnostic_get_elem(
struct nb_cb_get_elem_args *args)
{
const struct bfd_session *bs = args->list_entry;
return yang_data_new_enum(args->xpath, bs->local_diag);
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-multiplier
*/
struct yang_data *bfdd_bfd_sessions_single_hop_stats_local_multiplier_get_elem(
struct nb_cb_get_elem_args *args)
{
const struct bfd_session *bs = args->list_entry;
return yang_data_new_int8(args->xpath, bs->detect_mult);
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-discriminator
*/
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_remote_discriminator_get_elem(
struct nb_cb_get_elem_args *args)
{
const struct bfd_session *bs = args->list_entry;
if (bs->discrs.remote_discr == 0)
return NULL;
return yang_data_new_uint32(args->xpath, bs->discrs.remote_discr);
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-state
*/
struct yang_data *bfdd_bfd_sessions_single_hop_stats_remote_state_get_elem(
struct nb_cb_get_elem_args *args)
{
const struct bfd_session *bs = args->list_entry;
return yang_data_new_enum(args->xpath, bs->ses_state);
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-diagnostic
*/
struct yang_data *bfdd_bfd_sessions_single_hop_stats_remote_diagnostic_get_elem(
struct nb_cb_get_elem_args *args)
{
const struct bfd_session *bs = args->list_entry;
return yang_data_new_enum(args->xpath, bs->remote_diag);
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-multiplier
*/
struct yang_data *bfdd_bfd_sessions_single_hop_stats_remote_multiplier_get_elem(
struct nb_cb_get_elem_args *args)
{
const struct bfd_session *bs = args->list_entry;
return yang_data_new_int8(args->xpath, bs->remote_detect_mult);
}
/*
* XPath:
* /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-transmission-interval
*/
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_negotiated_transmission_interval_get_elem(
struct nb_cb_get_elem_args *args)
{
const struct bfd_session *bs = args->list_entry;
return yang_data_new_uint32(args->xpath,
bs->remote_timers.desired_min_tx);
}
/*
* XPath:
* /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-receive-interval
*/
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_negotiated_receive_interval_get_elem(
struct nb_cb_get_elem_args *args)
{
const struct bfd_session *bs = args->list_entry;
return yang_data_new_uint32(args->xpath,
bs->remote_timers.required_min_rx);
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/detection-mode
*/
struct yang_data *bfdd_bfd_sessions_single_hop_stats_detection_mode_get_elem(
struct nb_cb_get_elem_args *args)
{
const struct bfd_session *bs = args->list_entry;
int detection_mode;
/*
* Detection mode:
* 1. Async with echo
* 2. Async without echo
* 3. Demand with echo
* 4. Demand without echo
*
* TODO: support demand mode.
*/
if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
detection_mode = 1;
else
detection_mode = 2;
return yang_data_new_enum(args->xpath, detection_mode);
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/last-down-time
*/
struct yang_data *bfdd_bfd_sessions_single_hop_stats_last_down_time_get_elem(
struct nb_cb_get_elem_args *args)
{
/*
* TODO: implement me.
*
* No yang support for time elements yet.
*/
return NULL;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/last-up-time
*/
struct yang_data *bfdd_bfd_sessions_single_hop_stats_last_up_time_get_elem(
struct nb_cb_get_elem_args *args)
{
/*
* TODO: implement me.
*
* No yang support for time elements yet.
*/
return NULL;
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/session-down-count
*/
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_session_down_count_get_elem(
struct nb_cb_get_elem_args *args)
{
const struct bfd_session *bs = args->list_entry;
return yang_data_new_uint64(args->xpath, bs->stats.session_down);
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/session-up-count
*/
struct yang_data *bfdd_bfd_sessions_single_hop_stats_session_up_count_get_elem(
struct nb_cb_get_elem_args *args)
{
const struct bfd_session *bs = args->list_entry;
return yang_data_new_uint64(args->xpath, bs->stats.session_up);
}
/*
* XPath:
* /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/control-packet-input-count
*/
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_control_packet_input_count_get_elem(
struct nb_cb_get_elem_args *args)
{
const struct bfd_session *bs = args->list_entry;
return yang_data_new_uint64(args->xpath, bs->stats.rx_ctrl_pkt);
}
/*
* XPath:
* /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/control-packet-output-count
*/
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_control_packet_output_count_get_elem(
struct nb_cb_get_elem_args *args)
{
const struct bfd_session *bs = args->list_entry;
return yang_data_new_uint64(args->xpath, bs->stats.tx_ctrl_pkt);
}
/*
* XPath:
* /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-echo-transmission-interval
*/
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_negotiated_echo_transmission_interval_get_elem(
struct nb_cb_get_elem_args *args)
{
const struct bfd_session *bs = args->list_entry;
return yang_data_new_uint32(args->xpath,
bs->remote_timers.required_min_echo);
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/echo-packet-input-count
*/
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_echo_packet_input_count_get_elem(
struct nb_cb_get_elem_args *args)
{
const struct bfd_session *bs = args->list_entry;
return yang_data_new_uint64(args->xpath, bs->stats.rx_echo_pkt);
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/echo-packet-output-count
*/
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem(
struct nb_cb_get_elem_args *args)
{
const struct bfd_session *bs = args->list_entry;
return yang_data_new_uint64(args->xpath, bs->stats.tx_echo_pkt);
}
/*
* XPath: /frr-bfdd:bfdd/bfd/sessions/multi-hop
*/
const void *
bfdd_bfd_sessions_multi_hop_get_next(struct nb_cb_get_next_args *args)
{
return bfd_session_next(args->list_entry, true);
}
int bfdd_bfd_sessions_multi_hop_get_keys(struct nb_cb_get_keys_args *args)
{
const struct bfd_session *bs = args->list_entry;
char dstbuf[INET6_ADDRSTRLEN], srcbuf[INET6_ADDRSTRLEN];
inet_ntop(bs->key.family, &bs->key.peer, dstbuf, sizeof(dstbuf));
inet_ntop(bs->key.family, &bs->key.local, srcbuf, sizeof(srcbuf));
args->keys->num = 4;
strlcpy(args->keys->key[0], srcbuf, sizeof(args->keys->key[0]));
strlcpy(args->keys->key[1], dstbuf, sizeof(args->keys->key[1]));
strlcpy(args->keys->key[2], bs->key.vrfname,
sizeof(args->keys->key[2]));
return NB_OK;
}
const void *
bfdd_bfd_sessions_multi_hop_lookup_entry(struct nb_cb_lookup_entry_args *args)
{
const char *source_addr = args->keys->key[0];
const char *dest_addr = args->keys->key[1];
const char *vrf = args->keys->key[2];
struct sockaddr_any psa, lsa;
struct bfd_key bk;
strtosa(dest_addr, &psa);
strtosa(source_addr, &lsa);
gen_bfd_key(&bk, &psa, &lsa, true, NULL, vrf);
return bfd_key_lookup(bk);
}

1051
bfdd/bfdd_vty.c Normal file

File diff suppressed because it is too large Load diff

369
bfdd/bfddp_packet.h Normal file
View file

@ -0,0 +1,369 @@
// SPDX-License-Identifier: MIT
/*
* BFD Data Plane protocol messages header.
*
* Copyright (C) 2020 Network Device Education Foundation, Inc. ("NetDEF")
* Rafael F. Zalamena
*/
/**
* \file bfddp_packet.h
*/
#ifndef BFD_DP_PACKET_H
#define BFD_DP_PACKET_H
#include <netinet/in.h>
#include <stdint.h>
/*
* Protocol definitions.
*/
/**
* BFD protocol version as defined in RFC5880 Section 4.1 Generic BFD Control
* Packet Format.
*/
#define BFD_PROTOCOL_VERSION 1
/** Default data plane port. */
#define BFD_DATA_PLANE_DEFAULT_PORT 50700
/** BFD single hop UDP port, as defined in RFC 5881 Section 4. Encapsulation. */
#define BFD_SINGLE_HOP_PORT 3784
/** BFD multi hop UDP port, as defined in RFC 5883 Section 5. Encapsulation. */
#define BFD_MULTI_HOP_PORT 4784
/** Default slow start multiplier. */
#define SLOWSTART_DMULT 3
/** Default slow start transmission speed. */
#define SLOWSTART_TX 1000000u
/** Default slow start receive speed. */
#define SLOWSTART_RX 1000000u
/** Default slow start echo receive speed. */
#define SLOWSTART_ERX 0u
/*
* BFD single hop source UDP ports. As defined in RFC 5881 Section 4.
* Encapsulation.
*/
#define BFD_SOURCE_PORT_BEGIN 49152
#define BFD_SOURCE_PORT_END 65535
/** BFD data plane protocol version. */
#define BFD_DP_VERSION 1
/** BFD data plane message types. */
enum bfddp_message_type {
/** Ask for BFD daemon or data plane for echo packet. */
ECHO_REQUEST = 0,
/** Answer a ECHO_REQUEST packet. */
ECHO_REPLY = 1,
/** Add or update BFD peer session. */
DP_ADD_SESSION = 2,
/** Delete BFD peer session. */
DP_DELETE_SESSION = 3,
/** Tell BFD daemon state changed: timer expired or session down. */
BFD_STATE_CHANGE = 4,
/** Ask for BFD session counters. */
DP_REQUEST_SESSION_COUNTERS = 5,
/** Tell BFD daemon about counters values. */
BFD_SESSION_COUNTERS = 6,
};
/**
* `ECHO_REQUEST`/`ECHO_REPLY` data payload.
*
* Data plane might use whatever precision it wants for `dp_time`
* field, however if you want to be able to tell the delay between
* data plane packet send and BFD daemon packet processing you should
* use `gettimeofday()` and have the data plane clock synchronized with
* BFD daemon (not a problem if data plane runs in the same system).
*
* Normally data plane will only check the time stamp it sent to determine
* the whole packet trip time.
*/
struct bfddp_echo {
/** Filled by data plane. */
uint64_t dp_time;
/** Filled by BFD daemon. */
uint64_t bfdd_time;
};
/** BFD session flags. */
enum bfddp_session_flag {
/** Set when using multi hop. */
SESSION_MULTIHOP = (1 << 0),
/** Set when using demand mode. */
SESSION_DEMAND = (1 << 1),
/** Set when using cbit (Control Plane Independent). */
SESSION_CBIT = (1 << 2),
/** Set when using echo mode. */
SESSION_ECHO = (1 << 3),
/** Set when using IPv6. */
SESSION_IPV6 = (1 << 4),
/** Set when using passive mode. */
SESSION_PASSIVE = (1 << 5),
/** Set when session is administrative down. */
SESSION_SHUTDOWN = (1 << 6),
};
/**
* `DP_ADD_SESSION`/`DP_DELETE_SESSION` data payload.
*
* `lid` is unique in BFD daemon so it might be used as key for data
* structures lookup.
*/
struct bfddp_session {
/** Important session flags. \see bfddp_session_flag. */
uint32_t flags;
/**
* Session source address.
*
* Check `flags` field for `SESSION_IPV6` before using as IPv6.
*/
struct in6_addr src;
/**
* Session destination address.
*
* Check `flags` field for `SESSION_IPV6` before using as IPv6.
*/
struct in6_addr dst;
/** Local discriminator. */
uint32_t lid;
/**
* Minimum desired transmission interval (in microseconds) without
* jitter.
*/
uint32_t min_tx;
/**
* Required minimum receive interval rate (in microseconds) without
* jitter.
*/
uint32_t min_rx;
/**
* Minimum desired echo transmission interval (in microseconds)
* without jitter.
*/
uint32_t min_echo_tx;
/**
* Required minimum echo receive interval rate (in microseconds)
* without jitter.
*/
uint32_t min_echo_rx;
/** Amount of milliseconds to wait before starting the session */
uint32_t hold_time;
/** Minimum TTL. */
uint8_t ttl;
/** Detection multiplier. */
uint8_t detect_mult;
/** Reserved / zeroed. */
uint16_t zero;
/** Interface index (set to `0` when unavailable). */
uint32_t ifindex;
/** Interface name (empty when unavailable). */
char ifname[64];
/* TODO: missing authentication. */
};
/** BFD packet state values as defined in RFC 5880, Section 4.1. */
enum bfd_state_value {
/** Session is administratively down. */
STATE_ADMINDOWN = 0,
/** Session is down or went down. */
STATE_DOWN = 1,
/** Session is initializing. */
STATE_INIT = 2,
/** Session is up. */
STATE_UP = 3,
};
/** BFD diagnostic field values as defined in RFC 5880, Section 4.1. */
enum bfd_diagnostic_value {
/** Nothing was diagnosed. */
DIAG_NOTHING = 0,
/** Control detection time expired. */
DIAG_CONTROL_EXPIRED = 1,
/** Echo function failed. */
DIAG_ECHO_FAILED = 2,
/** Neighbor signaled down. */
DIAG_DOWN = 3,
/** Forwarding plane reset. */
DIAG_FP_RESET = 4,
/** Path down. */
DIAG_PATH_DOWN = 5,
/** Concatenated path down. */
DIAG_CONCAT_PATH_DOWN = 6,
/** Administratively down. */
DIAG_ADMIN_DOWN = 7,
/** Reverse concatenated path down. */
DIAG_REV_CONCAT_PATH_DOWN = 8,
};
/** BFD remote state flags. */
enum bfd_remote_flags {
/** Control Plane Independent bit. */
RBIT_CPI = (1 << 0),
/** Demand mode bit. */
RBIT_DEMAND = (1 << 1),
/** Multipoint bit. */
RBIT_MP = (1 << 2),
};
/**
* `BFD_STATE_CHANGE` data payload.
*/
struct bfddp_state_change {
/** Local discriminator. */
uint32_t lid;
/** Remote discriminator. */
uint32_t rid;
/** Remote configurations/bits set. \see bfd_remote_flags. */
uint32_t remote_flags;
/** Remote minimum desired transmission interval. */
uint32_t desired_tx;
/** Remote minimum receive interval. */
uint32_t required_rx;
/** Remote minimum echo receive interval. */
uint32_t required_echo_rx;
/** Remote state. \see bfd_state_values.*/
uint8_t state;
/** Remote diagnostics (if any) */
uint8_t diagnostics;
/** Remote detection multiplier. */
uint8_t detection_multiplier;
};
/**
* BFD control packet state bits definition.
*/
enum bfddp_control_state_bits {
/** Used to request connection establishment signal. */
STATE_POLL_BIT = (1 << 5),
/** Finalizes the connection establishment signal. */
STATE_FINAL_BIT = (1 << 4),
/** Signalizes that forward plane doesn't depend on control plane. */
STATE_CPI_BIT = (1 << 3),
/** Signalizes the use of authentication. */
STATE_AUTH_BIT = (1 << 2),
/** Signalizes that peer is using demand mode. */
STATE_DEMAND_BIT = (1 << 1),
/** Used in RFC 8562 implementation. */
STATE_MULTI_BIT = (1 << 0),
};
/**
* BFD control packet.
*
* As defined in 'RFC 5880 Section 4.1 Generic BFD Control Packet Format'.
*/
struct bfddp_control_packet {
/** (3 bits version << 5) | (5 bits diag). */
uint8_t version_diag;
/**
* (2 bits state << 6) | (6 bits flags)
*
* \see bfd_state_value, bfddp_control_state_bits.
*/
uint8_t state_bits;
/** Detection multiplier. */
uint8_t detection_multiplier;
/** Packet length in bytes. */
uint8_t length;
/** Our discriminator. */
uint32_t local_id;
/** Remote system discriminator. */
uint32_t remote_id;
/** Desired minimum send interval in microseconds. */
uint32_t desired_tx;
/** Desired minimum receive interval in microseconds. */
uint32_t required_rx;
/** Desired minimum echo receive interval in microseconds. */
uint32_t required_echo_rx;
};
/**
* The protocol wire message header structure.
*/
struct bfddp_message_header {
/** Protocol version format. \see BFD_DP_VERSION. */
uint8_t version;
/** Reserved / zero field. */
uint8_t zero;
/** Message contents type. \see bfddp_message_type. */
uint16_t type;
/**
* Message identification (to pair request/response).
*
* The ID `0` is reserved for asynchronous messages (e.g. unrequested
* messages).
*/
uint16_t id;
/** Message length. */
uint16_t length;
};
/**
* Data plane session counters request.
*
* Message type: `DP_REQUEST_SESSION_COUNTERS`.
*/
struct bfddp_request_counters {
/** Session local discriminator. */
uint32_t lid;
};
/**
* BFD session counters reply.
*
* Message type: `BFD_SESSION_COUNTERS`.
*/
struct bfddp_session_counters {
/** Session local discriminator. */
uint32_t lid;
/** Control packet bytes input. */
uint64_t control_input_bytes;
/** Control packets input. */
uint64_t control_input_packets;
/** Control packet bytes output. */
uint64_t control_output_bytes;
/** Control packets output. */
uint64_t control_output_packets;
/** Echo packet bytes input. */
uint64_t echo_input_bytes;
/** Echo packets input. */
uint64_t echo_input_packets;
/** Echo packet bytes output. */
uint64_t echo_output_bytes;
/** Echo packets output. */
uint64_t echo_output_packets;
};
/**
* The protocol wire messages structure.
*/
struct bfddp_message {
/** Message header. \see bfddp_message_header. */
struct bfddp_message_header header;
/** Message payload. \see bfddp_message_type. */
union {
struct bfddp_echo echo;
struct bfddp_session session;
struct bfddp_state_change state;
struct bfddp_control_packet control;
struct bfddp_request_counters counters_req;
struct bfddp_session_counters session_counters;
} data;
};
#endif /* BFD_DP_PACKET_H */

592
bfdd/config.c Normal file
View file

@ -0,0 +1,592 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*********************************************************************
* Copyright 2017-2018 Network Device Education Foundation, Inc. ("NetDEF")
*
* config.c: implements the BFD daemon configuration handling.
*
* Authors
* -------
* Rafael Zalamena <rzalamena@opensourcerouting.org>
*/
#include <zebra.h>
#include <string.h>
#include "lib/json.h"
#include "bfd.h"
DEFINE_MTYPE_STATIC(BFDD, BFDD_LABEL, "long-lived label memory");
/*
* Definitions
*/
enum peer_list_type {
PLT_IPV4,
PLT_IPV6,
PLT_LABEL,
};
/*
* Prototypes
*/
static int parse_config_json(struct json_object *jo, bpc_handle h, void *arg);
static int parse_list(struct json_object *jo, enum peer_list_type plt,
bpc_handle h, void *arg);
static int parse_peer_config(struct json_object *jo, struct bfd_peer_cfg *bpc);
static int parse_peer_label_config(struct json_object *jo,
struct bfd_peer_cfg *bpc);
static int config_add(struct bfd_peer_cfg *bpc, void *arg);
static int config_del(struct bfd_peer_cfg *bpc, void *arg);
static int json_object_add_peer(struct json_object *jo, struct bfd_session *bs);
/*
* Implementation
*/
static int config_add(struct bfd_peer_cfg *bpc,
void *arg __attribute__((unused)))
{
return ptm_bfd_sess_new(bpc) == NULL;
}
static int config_del(struct bfd_peer_cfg *bpc,
void *arg __attribute__((unused)))
{
return ptm_bfd_sess_del(bpc) != 0;
}
static int parse_config_json(struct json_object *jo, bpc_handle h, void *arg)
{
const char *key, *sval;
struct json_object *jo_val;
struct json_object_iterator joi, join;
int error = 0;
JSON_FOREACH (jo, joi, join) {
key = json_object_iter_peek_name(&joi);
jo_val = json_object_iter_peek_value(&joi);
if (strcmp(key, "ipv4") == 0) {
error += parse_list(jo_val, PLT_IPV4, h, arg);
} else if (strcmp(key, "ipv6") == 0) {
error += parse_list(jo_val, PLT_IPV6, h, arg);
} else if (strcmp(key, "label") == 0) {
error += parse_list(jo_val, PLT_LABEL, h, arg);
} else {
sval = json_object_get_string(jo_val);
zlog_warn("%s:%d invalid configuration: %s", __func__,
__LINE__, sval);
error++;
}
}
/*
* Our callers never call free() on json_object and only expect
* the return value, so lets free() it here.
*/
json_object_put(jo);
return error;
}
int parse_config(const char *fname)
{
struct json_object *jo;
jo = json_object_from_file(fname);
if (jo == NULL)
return -1;
return parse_config_json(jo, config_add, NULL);
}
static int parse_list(struct json_object *jo, enum peer_list_type plt,
bpc_handle h, void *arg)
{
struct json_object *jo_val;
struct bfd_peer_cfg bpc;
int allen, idx;
int error = 0, result;
allen = json_object_array_length(jo);
for (idx = 0; idx < allen; idx++) {
jo_val = json_object_array_get_idx(jo, idx);
/* Set defaults. */
memset(&bpc, 0, sizeof(bpc));
bpc.bpc_detectmultiplier = BFD_DEFDETECTMULT;
bpc.bpc_recvinterval = BFD_DEFREQUIREDMINRX;
bpc.bpc_txinterval = BFD_DEFDESIREDMINTX;
bpc.bpc_echorecvinterval = BFD_DEF_REQ_MIN_ECHO_RX;
bpc.bpc_echotxinterval = BFD_DEF_DES_MIN_ECHO_TX;
switch (plt) {
case PLT_IPV4:
zlog_debug("ipv4 peers %d:", allen);
bpc.bpc_ipv4 = true;
break;
case PLT_IPV6:
zlog_debug("ipv6 peers %d:", allen);
bpc.bpc_ipv4 = false;
break;
case PLT_LABEL:
zlog_debug("label peers %d:", allen);
if (parse_peer_label_config(jo_val, &bpc) != 0) {
error++;
continue;
}
break;
default:
error++;
zlog_err("%s:%d: unsupported peer type", __func__,
__LINE__);
break;
}
result = parse_peer_config(jo_val, &bpc);
error += result;
if (result == 0)
error += (h(&bpc, arg) != 0);
}
return error;
}
static int parse_peer_config(struct json_object *jo, struct bfd_peer_cfg *bpc)
{
const char *key, *sval;
struct json_object *jo_val;
struct json_object_iterator joi, join;
int family_type = (bpc->bpc_ipv4) ? AF_INET : AF_INET6;
int error = 0;
zlog_debug(" peer: %s", bpc->bpc_ipv4 ? "ipv4" : "ipv6");
JSON_FOREACH (jo, joi, join) {
key = json_object_iter_peek_name(&joi);
jo_val = json_object_iter_peek_value(&joi);
if (strcmp(key, "multihop") == 0) {
bpc->bpc_mhop = json_object_get_boolean(jo_val);
zlog_debug(" multihop: %s",
bpc->bpc_mhop ? "true" : "false");
} else if (strcmp(key, "peer-address") == 0) {
sval = json_object_get_string(jo_val);
if (strtosa(sval, &bpc->bpc_peer) != 0
|| bpc->bpc_peer.sa_sin.sin_family != family_type) {
zlog_debug(
"%s:%d failed to parse peer-address '%s'",
__func__, __LINE__, sval);
error++;
}
zlog_debug(" peer-address: %s", sval);
} else if (strcmp(key, "local-address") == 0) {
sval = json_object_get_string(jo_val);
if (strtosa(sval, &bpc->bpc_local) != 0
|| bpc->bpc_local.sa_sin.sin_family
!= family_type) {
zlog_debug(
"%s:%d failed to parse local-address '%s'",
__func__, __LINE__, sval);
error++;
}
zlog_debug(" local-address: %s", sval);
} else if (strcmp(key, "local-interface") == 0) {
bpc->bpc_has_localif = true;
sval = json_object_get_string(jo_val);
if (strlcpy(bpc->bpc_localif, sval,
sizeof(bpc->bpc_localif))
> sizeof(bpc->bpc_localif)) {
zlog_debug(
" local-interface: %s (truncated)",
sval);
error++;
} else {
zlog_debug(" local-interface: %s", sval);
}
} else if (strcmp(key, "vrf-name") == 0) {
bpc->bpc_has_vrfname = true;
sval = json_object_get_string(jo_val);
if (strlcpy(bpc->bpc_vrfname, sval,
sizeof(bpc->bpc_vrfname))
> sizeof(bpc->bpc_vrfname)) {
zlog_debug(" vrf-name: %s (truncated)",
sval);
error++;
} else {
zlog_debug(" vrf-name: %s", sval);
}
} else if (strcmp(key, "detect-multiplier") == 0) {
bpc->bpc_detectmultiplier =
json_object_get_int64(jo_val);
bpc->bpc_has_detectmultiplier = true;
zlog_debug(" detect-multiplier: %u",
bpc->bpc_detectmultiplier);
} else if (strcmp(key, "receive-interval") == 0) {
bpc->bpc_recvinterval = json_object_get_int64(jo_val);
bpc->bpc_has_recvinterval = true;
zlog_debug(" receive-interval: %" PRIu64,
bpc->bpc_recvinterval);
} else if (strcmp(key, "transmit-interval") == 0) {
bpc->bpc_txinterval = json_object_get_int64(jo_val);
bpc->bpc_has_txinterval = true;
zlog_debug(" transmit-interval: %" PRIu64,
bpc->bpc_txinterval);
} else if (strcmp(key, "echo-receive-interval") == 0) {
bpc->bpc_echorecvinterval = json_object_get_int64(jo_val);
bpc->bpc_has_echorecvinterval = true;
zlog_debug(" echo-receive-interval: %" PRIu64,
bpc->bpc_echorecvinterval);
} else if (strcmp(key, "echo-transmit-interval") == 0) {
bpc->bpc_echotxinterval = json_object_get_int64(jo_val);
bpc->bpc_has_echotxinterval = true;
zlog_debug(" echo-transmit-interval: %" PRIu64,
bpc->bpc_echotxinterval);
} else if (strcmp(key, "create-only") == 0) {
bpc->bpc_createonly = json_object_get_boolean(jo_val);
zlog_debug(" create-only: %s",
bpc->bpc_createonly ? "true" : "false");
} else if (strcmp(key, "shutdown") == 0) {
bpc->bpc_shutdown = json_object_get_boolean(jo_val);
zlog_debug(" shutdown: %s",
bpc->bpc_shutdown ? "true" : "false");
} else if (strcmp(key, "echo-mode") == 0) {
bpc->bpc_echo = json_object_get_boolean(jo_val);
zlog_debug(" echo-mode: %s",
bpc->bpc_echo ? "true" : "false");
} else if (strcmp(key, "label") == 0) {
bpc->bpc_has_label = true;
sval = json_object_get_string(jo_val);
if (strlcpy(bpc->bpc_label, sval,
sizeof(bpc->bpc_label))
> sizeof(bpc->bpc_label)) {
zlog_debug(" label: %s (truncated)",
sval);
error++;
} else {
zlog_debug(" label: %s", sval);
}
} else {
sval = json_object_get_string(jo_val);
zlog_warn("%s:%d invalid configuration: '%s: %s'",
__func__, __LINE__, key, sval);
error++;
}
}
if (bpc->bpc_peer.sa_sin.sin_family == 0) {
zlog_debug("%s:%d no peer address provided", __func__,
__LINE__);
error++;
}
return error;
}
static int parse_peer_label_config(struct json_object *jo,
struct bfd_peer_cfg *bpc)
{
struct peer_label *pl;
struct json_object *label;
const char *sval;
/* Get label and translate it to BFD daemon key. */
if (!json_object_object_get_ex(jo, "label", &label))
return 1;
sval = json_object_get_string(label);
pl = pl_find(sval);
if (pl == NULL)
return 1;
zlog_debug(" peer-label: %s", sval);
/* Translate the label into BFD address keys. */
bs_to_bpc(pl->pl_bs, bpc);
return 0;
}
/*
* Control socket JSON parsing.
*/
int config_request_add(const char *jsonstr)
{
struct json_object *jo;
jo = json_tokener_parse(jsonstr);
if (jo == NULL)
return -1;
return parse_config_json(jo, config_add, NULL);
}
int config_request_del(const char *jsonstr)
{
struct json_object *jo;
jo = json_tokener_parse(jsonstr);
if (jo == NULL)
return -1;
return parse_config_json(jo, config_del, NULL);
}
char *config_response(const char *status, const char *error)
{
struct json_object *resp, *jo;
char *jsonstr;
resp = json_object_new_object();
if (resp == NULL)
return NULL;
/* Add 'status' response key. */
jo = json_object_new_string(status);
if (jo == NULL) {
json_object_put(resp);
return NULL;
}
json_object_object_add(resp, "status", jo);
/* Add 'error' response key. */
if (error != NULL) {
jo = json_object_new_string(error);
if (jo == NULL) {
json_object_put(resp);
return NULL;
}
json_object_object_add(resp, "error", jo);
}
/* Generate JSON response. */
jsonstr = XSTRDUP(
MTYPE_BFDD_NOTIFICATION,
json_object_to_json_string_ext(resp, BFDD_JSON_CONV_OPTIONS));
json_object_put(resp);
return jsonstr;
}
char *config_notify(struct bfd_session *bs)
{
struct json_object *resp;
char *jsonstr;
time_t now;
resp = json_object_new_object();
if (resp == NULL)
return NULL;
json_object_string_add(resp, "op", BCM_NOTIFY_PEER_STATUS);
json_object_add_peer(resp, bs);
/* Add status information */
json_object_int_add(resp, "id", bs->discrs.my_discr);
json_object_int_add(resp, "remote-id", bs->discrs.my_discr);
switch (bs->ses_state) {
case PTM_BFD_UP:
json_object_string_add(resp, "state", "up");
now = monotime(NULL);
json_object_int_add(resp, "uptime", now - bs->uptime.tv_sec);
break;
case PTM_BFD_ADM_DOWN:
json_object_string_add(resp, "state", "adm-down");
break;
case PTM_BFD_DOWN:
json_object_string_add(resp, "state", "down");
now = monotime(NULL);
json_object_int_add(resp, "downtime",
now - bs->downtime.tv_sec);
break;
case PTM_BFD_INIT:
json_object_string_add(resp, "state", "init");
break;
default:
json_object_string_add(resp, "state", "unknown");
break;
}
json_object_int_add(resp, "diagnostics", bs->local_diag);
json_object_int_add(resp, "remote-diagnostics", bs->remote_diag);
/* Generate JSON response. */
jsonstr = XSTRDUP(
MTYPE_BFDD_NOTIFICATION,
json_object_to_json_string_ext(resp, BFDD_JSON_CONV_OPTIONS));
json_object_put(resp);
return jsonstr;
}
char *config_notify_config(const char *op, struct bfd_session *bs)
{
struct json_object *resp;
char *jsonstr;
resp = json_object_new_object();
if (resp == NULL)
return NULL;
json_object_string_add(resp, "op", op);
json_object_add_peer(resp, bs);
/* On peer deletion we don't need to add any additional information. */
if (strcmp(op, BCM_NOTIFY_CONFIG_DELETE) == 0)
goto skip_config;
json_object_int_add(resp, "detect-multiplier", bs->detect_mult);
json_object_int_add(resp, "receive-interval",
bs->timers.required_min_rx / 1000);
json_object_int_add(resp, "transmit-interval",
bs->timers.desired_min_tx / 1000);
json_object_int_add(resp, "echo-receive-interval",
bs->timers.required_min_echo_rx / 1000);
json_object_int_add(resp, "echo-transmit-interval",
bs->timers.desired_min_echo_tx / 1000);
json_object_int_add(resp, "remote-detect-multiplier",
bs->remote_detect_mult);
json_object_int_add(resp, "remote-receive-interval",
bs->remote_timers.required_min_rx / 1000);
json_object_int_add(resp, "remote-transmit-interval",
bs->remote_timers.desired_min_tx / 1000);
json_object_int_add(resp, "remote-echo-receive-interval",
bs->remote_timers.required_min_echo / 1000);
if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
json_object_boolean_true_add(resp, "echo-mode");
else
json_object_boolean_false_add(resp, "echo-mode");
if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
json_object_boolean_true_add(resp, "shutdown");
else
json_object_boolean_false_add(resp, "shutdown");
skip_config:
/* Generate JSON response. */
jsonstr = XSTRDUP(
MTYPE_BFDD_NOTIFICATION,
json_object_to_json_string_ext(resp, BFDD_JSON_CONV_OPTIONS));
json_object_put(resp);
return jsonstr;
}
int config_notify_request(struct bfd_control_socket *bcs, const char *jsonstr,
bpc_handle bh)
{
struct json_object *jo;
jo = json_tokener_parse(jsonstr);
if (jo == NULL)
return -1;
return parse_config_json(jo, bh, bcs);
}
static int json_object_add_peer(struct json_object *jo, struct bfd_session *bs)
{
char addr_buf[INET6_ADDRSTRLEN];
/* Add peer 'key' information. */
if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6))
json_object_boolean_true_add(jo, "ipv6");
else
json_object_boolean_false_add(jo, "ipv6");
if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
json_object_boolean_true_add(jo, "multihop");
json_object_string_add(jo, "peer-address",
inet_ntop(bs->key.family, &bs->key.peer,
addr_buf, sizeof(addr_buf)));
json_object_string_add(jo, "local-address",
inet_ntop(bs->key.family, &bs->key.local,
addr_buf, sizeof(addr_buf)));
if (bs->key.vrfname[0])
json_object_string_add(jo, "vrf-name", bs->key.vrfname);
} else {
json_object_boolean_false_add(jo, "multihop");
json_object_string_add(jo, "peer-address",
inet_ntop(bs->key.family, &bs->key.peer,
addr_buf, sizeof(addr_buf)));
if (memcmp(&bs->key.local, &zero_addr, sizeof(bs->key.local)))
json_object_string_add(
jo, "local-address",
inet_ntop(bs->key.family, &bs->key.local,
addr_buf, sizeof(addr_buf)));
if (bs->key.ifname[0])
json_object_string_add(jo, "local-interface",
bs->key.ifname);
}
if (bs->pl)
json_object_string_add(jo, "label", bs->pl->pl_label);
return 0;
}
/*
* Label handling
*/
struct peer_label *pl_find(const char *label)
{
struct peer_label *pl;
TAILQ_FOREACH (pl, &bglobal.bg_pllist, pl_entry) {
if (strcmp(pl->pl_label, label) != 0)
continue;
return pl;
}
return NULL;
}
struct peer_label *pl_new(const char *label, struct bfd_session *bs)
{
struct peer_label *pl;
pl = XCALLOC(MTYPE_BFDD_LABEL, sizeof(*pl));
if (strlcpy(pl->pl_label, label, sizeof(pl->pl_label))
> sizeof(pl->pl_label))
zlog_warn("%s:%d: label was truncated", __func__, __LINE__);
pl->pl_bs = bs;
bs->pl = pl;
TAILQ_INSERT_HEAD(&bglobal.bg_pllist, pl, pl_entry);
return pl;
}
void pl_free(struct peer_label *pl)
{
if (pl == NULL)
return;
/* Remove the pointer back. */
pl->pl_bs->pl = NULL;
TAILQ_REMOVE(&bglobal.bg_pllist, pl, pl_entry);
XFREE(MTYPE_BFDD_LABEL, pl);
}

844
bfdd/control.c Normal file
View file

@ -0,0 +1,844 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*********************************************************************
* Copyright 2017-2018 Network Device Education Foundation, Inc. ("NetDEF")
*
* control.c: implements the BFD daemon control socket. It will be used
* to talk with clients daemon/scripts/consumers.
*
* Authors
* -------
* Rafael Zalamena <rzalamena@opensourcerouting.org>
*/
#include <zebra.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/un.h>
#include "bfd.h"
/*
* Prototypes
*/
static int sock_set_nonblock(int fd);
struct bfd_control_queue *control_queue_new(struct bfd_control_socket *bcs);
static void control_queue_free(struct bfd_control_socket *bcs,
struct bfd_control_queue *bcq);
static int control_queue_dequeue(struct bfd_control_socket *bcs);
static int control_queue_enqueue(struct bfd_control_socket *bcs,
struct bfd_control_msg *bcm);
static int control_queue_enqueue_first(struct bfd_control_socket *bcs,
struct bfd_control_msg *bcm);
struct bfd_notify_peer *control_notifypeer_new(struct bfd_control_socket *bcs,
struct bfd_session *bs);
static void control_notifypeer_free(struct bfd_control_socket *bcs,
struct bfd_notify_peer *bnp);
struct bfd_notify_peer *control_notifypeer_find(struct bfd_control_socket *bcs,
struct bfd_session *bs);
struct bfd_control_socket *control_new(int sd);
static void control_free(struct bfd_control_socket *bcs);
static void control_reset_buf(struct bfd_control_buffer *bcb);
static void control_read(struct event *t);
static void control_write(struct event *t);
static void control_handle_request_add(struct bfd_control_socket *bcs,
struct bfd_control_msg *bcm);
static void control_handle_request_del(struct bfd_control_socket *bcs,
struct bfd_control_msg *bcm);
static int notify_add_cb(struct bfd_peer_cfg *bpc, void *arg);
static int notify_del_cb(struct bfd_peer_cfg *bpc, void *arg);
static void control_handle_notify_add(struct bfd_control_socket *bcs,
struct bfd_control_msg *bcm);
static void control_handle_notify_del(struct bfd_control_socket *bcs,
struct bfd_control_msg *bcm);
static void _control_handle_notify(struct hash_bucket *hb, void *arg);
static void control_handle_notify(struct bfd_control_socket *bcs,
struct bfd_control_msg *bcm);
static void control_response(struct bfd_control_socket *bcs, uint16_t id,
const char *status, const char *error);
static void _control_notify_config(struct bfd_control_socket *bcs,
const char *op, struct bfd_session *bs);
static void _control_notify(struct bfd_control_socket *bcs,
struct bfd_session *bs);
/*
* Functions
*/
static int sock_set_nonblock(int fd)
{
int flags;
flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) {
zlog_warn("%s: fcntl F_GETFL: %s", __func__, strerror(errno));
return -1;
}
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) == -1) {
zlog_warn("%s: fcntl F_SETFL: %s", __func__, strerror(errno));
return -1;
}
return 0;
}
int control_init(const char *path)
{
int sd;
mode_t umval;
struct sockaddr_un sun_ = {
.sun_family = AF_UNIX,
};
assert(path);
strlcpy(sun_.sun_path, path, sizeof(sun_.sun_path));
/* Remove previously created sockets. */
unlink(sun_.sun_path);
sd = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC);
if (sd == -1) {
zlog_err("%s: socket: %s", __func__, strerror(errno));
return -1;
}
umval = umask(0);
if (bind(sd, (struct sockaddr *)&sun_, sizeof(sun_)) == -1) {
zlog_err("%s: bind: %s", __func__, strerror(errno));
close(sd);
return -1;
}
umask(umval);
if (listen(sd, SOMAXCONN) == -1) {
zlog_err("%s: listen: %s", __func__, strerror(errno));
close(sd);
return -1;
}
sock_set_nonblock(sd);
bglobal.bg_csock = sd;
return 0;
}
void control_shutdown(void)
{
struct bfd_control_socket *bcs;
event_cancel(&bglobal.bg_csockev);
socket_close(&bglobal.bg_csock);
while (!TAILQ_EMPTY(&bglobal.bg_bcslist)) {
bcs = TAILQ_FIRST(&bglobal.bg_bcslist);
control_free(bcs);
}
}
void control_accept(struct event *t)
{
int csock, sd = EVENT_FD(t);
csock = accept(sd, NULL, 0);
if (csock == -1) {
zlog_warn("%s: accept: %s", __func__, strerror(errno));
return;
}
control_new(csock);
event_add_read(master, control_accept, NULL, sd, &bglobal.bg_csockev);
}
/*
* Client handling
*/
struct bfd_control_socket *control_new(int sd)
{
struct bfd_control_socket *bcs;
bcs = XCALLOC(MTYPE_BFDD_CONTROL, sizeof(*bcs));
/* Disable notifications by default. */
bcs->bcs_notify = 0;
bcs->bcs_sd = sd;
event_add_read(master, control_read, bcs, sd, &bcs->bcs_ev);
TAILQ_INIT(&bcs->bcs_bcqueue);
TAILQ_INIT(&bcs->bcs_bnplist);
TAILQ_INSERT_TAIL(&bglobal.bg_bcslist, bcs, bcs_entry);
return bcs;
}
static void control_free(struct bfd_control_socket *bcs)
{
struct bfd_control_queue *bcq;
struct bfd_notify_peer *bnp;
event_cancel(&(bcs->bcs_ev));
event_cancel(&(bcs->bcs_outev));
close(bcs->bcs_sd);
TAILQ_REMOVE(&bglobal.bg_bcslist, bcs, bcs_entry);
/* Empty output queue. */
while (!TAILQ_EMPTY(&bcs->bcs_bcqueue)) {
bcq = TAILQ_FIRST(&bcs->bcs_bcqueue);
control_queue_free(bcs, bcq);
}
/* Empty notification list. */
while (!TAILQ_EMPTY(&bcs->bcs_bnplist)) {
bnp = TAILQ_FIRST(&bcs->bcs_bnplist);
control_notifypeer_free(bcs, bnp);
}
control_reset_buf(&bcs->bcs_bin);
XFREE(MTYPE_BFDD_CONTROL, bcs);
}
struct bfd_notify_peer *control_notifypeer_new(struct bfd_control_socket *bcs,
struct bfd_session *bs)
{
struct bfd_notify_peer *bnp;
bnp = control_notifypeer_find(bcs, bs);
if (bnp)
return bnp;
bnp = XCALLOC(MTYPE_BFDD_CONTROL, sizeof(*bnp));
TAILQ_INSERT_TAIL(&bcs->bcs_bnplist, bnp, bnp_entry);
bnp->bnp_bs = bs;
bs->refcount++;
return bnp;
}
static void control_notifypeer_free(struct bfd_control_socket *bcs,
struct bfd_notify_peer *bnp)
{
TAILQ_REMOVE(&bcs->bcs_bnplist, bnp, bnp_entry);
bnp->bnp_bs->refcount--;
XFREE(MTYPE_BFDD_CONTROL, bnp);
}
struct bfd_notify_peer *control_notifypeer_find(struct bfd_control_socket *bcs,
struct bfd_session *bs)
{
struct bfd_notify_peer *bnp;
TAILQ_FOREACH (bnp, &bcs->bcs_bnplist, bnp_entry) {
if (bnp->bnp_bs == bs)
return bnp;
}
return NULL;
}
struct bfd_control_queue *control_queue_new(struct bfd_control_socket *bcs)
{
struct bfd_control_queue *bcq;
bcq = XCALLOC(MTYPE_BFDD_NOTIFICATION, sizeof(*bcq));
control_reset_buf(&bcq->bcq_bcb);
TAILQ_INSERT_TAIL(&bcs->bcs_bcqueue, bcq, bcq_entry);
return bcq;
}
static void control_queue_free(struct bfd_control_socket *bcs,
struct bfd_control_queue *bcq)
{
control_reset_buf(&bcq->bcq_bcb);
TAILQ_REMOVE(&bcs->bcs_bcqueue, bcq, bcq_entry);
XFREE(MTYPE_BFDD_NOTIFICATION, bcq);
}
static int control_queue_dequeue(struct bfd_control_socket *bcs)
{
struct bfd_control_queue *bcq;
/* List is empty, nothing to do. */
if (TAILQ_EMPTY(&bcs->bcs_bcqueue))
goto empty_list;
bcq = TAILQ_FIRST(&bcs->bcs_bcqueue);
control_queue_free(bcs, bcq);
/* Get the next buffer to send. */
if (TAILQ_EMPTY(&bcs->bcs_bcqueue))
goto empty_list;
bcq = TAILQ_FIRST(&bcs->bcs_bcqueue);
bcs->bcs_bout = &bcq->bcq_bcb;
bcs->bcs_outev = NULL;
event_add_write(master, control_write, bcs, bcs->bcs_sd,
&bcs->bcs_outev);
return 1;
empty_list:
event_cancel(&(bcs->bcs_outev));
bcs->bcs_bout = NULL;
return 0;
}
static int control_queue_enqueue(struct bfd_control_socket *bcs,
struct bfd_control_msg *bcm)
{
struct bfd_control_queue *bcq;
struct bfd_control_buffer *bcb;
bcq = control_queue_new(bcs);
bcb = &bcq->bcq_bcb;
bcb->bcb_left = sizeof(struct bfd_control_msg) + ntohl(bcm->bcm_length);
bcb->bcb_pos = 0;
bcb->bcb_bcm = bcm;
/* If this is the first item, then dequeue and start using it. */
if (bcs->bcs_bout == NULL) {
bcs->bcs_bout = bcb;
/* New messages, active write events. */
event_add_write(master, control_write, bcs, bcs->bcs_sd,
&bcs->bcs_outev);
}
return 0;
}
static int control_queue_enqueue_first(struct bfd_control_socket *bcs,
struct bfd_control_msg *bcm)
{
struct bfd_control_queue *bcq, *bcqn;
struct bfd_control_buffer *bcb;
/* Enqueue it somewhere. */
if (control_queue_enqueue(bcs, bcm) == -1)
return -1;
/*
* The item is either the first or the last. So we must first
* check the best case where the item is already the first.
*/
bcq = TAILQ_FIRST(&bcs->bcs_bcqueue);
bcb = &bcq->bcq_bcb;
if (bcm == bcb->bcb_bcm)
return 0;
/*
* The item was not the first, so it is the last. We'll try to
* assign it to the head of the queue, however if there is a
* transfer in progress, then we have to make the item as the
* next one.
*
* Interrupting the transfer of in progress message will cause
* the client to lose track of the message position/data.
*/
bcqn = TAILQ_LAST(&bcs->bcs_bcqueue, bcqueue);
TAILQ_REMOVE(&bcs->bcs_bcqueue, bcqn, bcq_entry);
if (bcb->bcb_pos != 0) {
/*
* First position is already being sent, insert into
* second position.
*/
TAILQ_INSERT_AFTER(&bcs->bcs_bcqueue, bcq, bcqn, bcq_entry);
} else {
/*
* Old message didn't start being sent, we still have
* time to put this one in the head of the queue.
*/
TAILQ_INSERT_HEAD(&bcs->bcs_bcqueue, bcqn, bcq_entry);
bcb = &bcqn->bcq_bcb;
bcs->bcs_bout = bcb;
}
return 0;
}
static void control_reset_buf(struct bfd_control_buffer *bcb)
{
/* Get ride of old data. */
XFREE(MTYPE_BFDD_NOTIFICATION, bcb->bcb_buf);
bcb->bcb_pos = 0;
bcb->bcb_left = 0;
}
static void control_read(struct event *t)
{
struct bfd_control_socket *bcs = EVENT_ARG(t);
struct bfd_control_buffer *bcb = &bcs->bcs_bin;
int sd = bcs->bcs_sd;
struct bfd_control_msg bcm;
ssize_t bread;
size_t plen;
/*
* Check if we have already downloaded message content, if so then skip
* to
* download the rest of it and process.
*
* Otherwise download a new message header and allocate the necessary
* memory.
*/
if (bcb->bcb_buf != NULL)
goto skip_header;
bread = read(sd, &bcm, sizeof(bcm));
if (bread == 0) {
control_free(bcs);
return;
}
if (bread < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
goto schedule_next_read;
zlog_warn("%s: read: %s", __func__, strerror(errno));
control_free(bcs);
return;
}
/* Validate header fields. */
plen = ntohl(bcm.bcm_length);
if (plen < 2) {
zlog_debug("%s: client closed due small message length: %d",
__func__, bcm.bcm_length);
control_free(bcs);
return;
}
#define FRR_BFD_MAXLEN 10 * 1024
if (plen > FRR_BFD_MAXLEN) {
zlog_debug("%s: client closed, invalid message length: %d",
__func__, bcm.bcm_length);
control_free(bcs);
return;
}
if (bcm.bcm_ver != BMV_VERSION_1) {
zlog_debug("%s: client closed due bad version: %d", __func__,
bcm.bcm_ver);
control_free(bcs);
return;
}
/* Prepare the buffer to load the message. */
bcs->bcs_version = bcm.bcm_ver;
bcs->bcs_type = bcm.bcm_type;
bcb->bcb_pos = sizeof(bcm);
bcb->bcb_left = plen;
bcb->bcb_buf = XMALLOC(MTYPE_BFDD_NOTIFICATION,
sizeof(bcm) + bcb->bcb_left + 1);
if (bcb->bcb_buf == NULL) {
zlog_warn("%s: not enough memory for message size: %zu",
__func__, bcb->bcb_left);
control_free(bcs);
return;
}
memcpy(bcb->bcb_buf, &bcm, sizeof(bcm));
/* Terminate data string with NULL for later processing. */
bcb->bcb_buf[sizeof(bcm) + bcb->bcb_left] = 0;
skip_header:
/* Download the remaining data of the message and process it. */
bread = read(sd, &bcb->bcb_buf[bcb->bcb_pos], bcb->bcb_left);
if (bread == 0) {
control_free(bcs);
return;
}
if (bread < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
goto schedule_next_read;
zlog_warn("%s: read: %s", __func__, strerror(errno));
control_free(bcs);
return;
}
bcb->bcb_pos += bread;
bcb->bcb_left -= bread;
/* We need more data, return to wait more. */
if (bcb->bcb_left > 0)
goto schedule_next_read;
switch (bcb->bcb_bcm->bcm_type) {
case BMT_REQUEST_ADD:
control_handle_request_add(bcs, bcb->bcb_bcm);
break;
case BMT_REQUEST_DEL:
control_handle_request_del(bcs, bcb->bcb_bcm);
break;
case BMT_NOTIFY:
control_handle_notify(bcs, bcb->bcb_bcm);
break;
case BMT_NOTIFY_ADD:
control_handle_notify_add(bcs, bcb->bcb_bcm);
break;
case BMT_NOTIFY_DEL:
control_handle_notify_del(bcs, bcb->bcb_bcm);
break;
default:
zlog_debug("%s: unhandled message type: %d", __func__,
bcb->bcb_bcm->bcm_type);
control_response(bcs, bcb->bcb_bcm->bcm_id, BCM_RESPONSE_ERROR,
"invalid message type");
break;
}
bcs->bcs_version = 0;
bcs->bcs_type = 0;
control_reset_buf(bcb);
schedule_next_read:
bcs->bcs_ev = NULL;
event_add_read(master, control_read, bcs, sd, &bcs->bcs_ev);
}
static void control_write(struct event *t)
{
struct bfd_control_socket *bcs = EVENT_ARG(t);
struct bfd_control_buffer *bcb = bcs->bcs_bout;
int sd = bcs->bcs_sd;
ssize_t bwrite;
bwrite = write(sd, &bcb->bcb_buf[bcb->bcb_pos], bcb->bcb_left);
if (bwrite == 0) {
control_free(bcs);
return;
}
if (bwrite < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
bcs->bcs_outev = NULL;
event_add_write(master, control_write, bcs, bcs->bcs_sd,
&bcs->bcs_outev);
return;
}
zlog_warn("%s: write: %s", __func__, strerror(errno));
control_free(bcs);
return;
}
bcb->bcb_pos += bwrite;
bcb->bcb_left -= bwrite;
if (bcb->bcb_left > 0) {
bcs->bcs_outev = NULL;
event_add_write(master, control_write, bcs, bcs->bcs_sd,
&bcs->bcs_outev);
return;
}
control_queue_dequeue(bcs);
}
/*
* Message processing
*/
static void control_handle_request_add(struct bfd_control_socket *bcs,
struct bfd_control_msg *bcm)
{
const char *json = (const char *)bcm->bcm_data;
if (config_request_add(json) == 0)
control_response(bcs, bcm->bcm_id, BCM_RESPONSE_OK, NULL);
else
control_response(bcs, bcm->bcm_id, BCM_RESPONSE_ERROR,
"request add failed");
}
static void control_handle_request_del(struct bfd_control_socket *bcs,
struct bfd_control_msg *bcm)
{
const char *json = (const char *)bcm->bcm_data;
if (config_request_del(json) == 0)
control_response(bcs, bcm->bcm_id, BCM_RESPONSE_OK, NULL);
else
control_response(bcs, bcm->bcm_id, BCM_RESPONSE_ERROR,
"request del failed");
}
static struct bfd_session *_notify_find_peer(struct bfd_peer_cfg *bpc)
{
struct peer_label *pl;
if (bpc->bpc_has_label) {
pl = pl_find(bpc->bpc_label);
if (pl)
return pl->pl_bs;
}
return bs_peer_find(bpc);
}
static void _control_handle_notify(struct hash_bucket *hb, void *arg)
{
struct bfd_control_socket *bcs = arg;
struct bfd_session *bs = hb->data;
/* Notify peer configuration. */
if (bcs->bcs_notify & BCM_NOTIFY_CONFIG)
_control_notify_config(bcs, BCM_NOTIFY_CONFIG_ADD, bs);
/* Notify peer status. */
if (bcs->bcs_notify & BCM_NOTIFY_PEER_STATE)
_control_notify(bcs, bs);
}
static void control_handle_notify(struct bfd_control_socket *bcs,
struct bfd_control_msg *bcm)
{
memcpy(&bcs->bcs_notify, bcm->bcm_data, sizeof(bcs->bcs_notify));
control_response(bcs, bcm->bcm_id, BCM_RESPONSE_OK, NULL);
/*
* If peer asked for notification configuration, send everything that
* was configured until the moment to sync up.
*/
if (bcs->bcs_notify & (BCM_NOTIFY_CONFIG | BCM_NOTIFY_PEER_STATE))
bfd_id_iterate(_control_handle_notify, bcs);
}
static int notify_add_cb(struct bfd_peer_cfg *bpc, void *arg)
{
struct bfd_control_socket *bcs = arg;
struct bfd_session *bs = _notify_find_peer(bpc);
if (bs == NULL)
return -1;
control_notifypeer_new(bcs, bs);
/* Notify peer status. */
_control_notify(bcs, bs);
return 0;
}
static int notify_del_cb(struct bfd_peer_cfg *bpc, void *arg)
{
struct bfd_control_socket *bcs = arg;
struct bfd_session *bs = _notify_find_peer(bpc);
struct bfd_notify_peer *bnp;
if (bs == NULL)
return -1;
bnp = control_notifypeer_find(bcs, bs);
if (bnp)
control_notifypeer_free(bcs, bnp);
return 0;
}
static void control_handle_notify_add(struct bfd_control_socket *bcs,
struct bfd_control_msg *bcm)
{
const char *json = (const char *)bcm->bcm_data;
if (config_notify_request(bcs, json, notify_add_cb) == 0) {
control_response(bcs, bcm->bcm_id, BCM_RESPONSE_OK, NULL);
return;
}
control_response(bcs, bcm->bcm_id, BCM_RESPONSE_ERROR,
"failed to parse notify data");
}
static void control_handle_notify_del(struct bfd_control_socket *bcs,
struct bfd_control_msg *bcm)
{
const char *json = (const char *)bcm->bcm_data;
if (config_notify_request(bcs, json, notify_del_cb) == 0) {
control_response(bcs, bcm->bcm_id, BCM_RESPONSE_OK, NULL);
return;
}
control_response(bcs, bcm->bcm_id, BCM_RESPONSE_ERROR,
"failed to parse notify data");
}
/*
* Internal functions used by the BFD daemon.
*/
static void control_response(struct bfd_control_socket *bcs, uint16_t id,
const char *status, const char *error)
{
struct bfd_control_msg *bcm;
char *jsonstr;
size_t jsonstrlen;
/* Generate JSON response. */
jsonstr = config_response(status, error);
if (jsonstr == NULL) {
zlog_warn("%s: config_response: failed to get JSON str",
__func__);
return;
}
/* Allocate data and answer. */
jsonstrlen = strlen(jsonstr);
bcm = XMALLOC(MTYPE_BFDD_NOTIFICATION,
sizeof(struct bfd_control_msg) + jsonstrlen);
bcm->bcm_length = htonl(jsonstrlen);
bcm->bcm_ver = BMV_VERSION_1;
bcm->bcm_type = BMT_RESPONSE;
bcm->bcm_id = id;
memcpy(bcm->bcm_data, jsonstr, jsonstrlen);
XFREE(MTYPE_BFDD_NOTIFICATION, jsonstr);
control_queue_enqueue_first(bcs, bcm);
}
static void _control_notify(struct bfd_control_socket *bcs,
struct bfd_session *bs)
{
struct bfd_control_msg *bcm;
char *jsonstr;
size_t jsonstrlen;
/* Generate JSON response. */
jsonstr = config_notify(bs);
if (jsonstr == NULL) {
zlog_warn("%s: config_notify: failed to get JSON str",
__func__);
return;
}
/* Allocate data and answer. */
jsonstrlen = strlen(jsonstr);
bcm = XMALLOC(MTYPE_BFDD_NOTIFICATION,
sizeof(struct bfd_control_msg) + jsonstrlen);
bcm->bcm_length = htonl(jsonstrlen);
bcm->bcm_ver = BMV_VERSION_1;
bcm->bcm_type = BMT_NOTIFY;
bcm->bcm_id = htons(BCM_NOTIFY_ID);
memcpy(bcm->bcm_data, jsonstr, jsonstrlen);
XFREE(MTYPE_BFDD_NOTIFICATION, jsonstr);
control_queue_enqueue(bcs, bcm);
}
int control_notify(struct bfd_session *bs, uint8_t notify_state)
{
struct bfd_control_socket *bcs;
struct bfd_notify_peer *bnp;
/* Notify zebra listeners as well. */
ptm_bfd_notify(bs, notify_state);
/*
* PERFORMANCE: reuse the bfd_control_msg allocated data for
* all control sockets to avoid wasting memory.
*/
TAILQ_FOREACH (bcs, &bglobal.bg_bcslist, bcs_entry) {
/*
* Test for all notifications first, then search for
* specific peers.
*/
if ((bcs->bcs_notify & BCM_NOTIFY_PEER_STATE) == 0) {
bnp = control_notifypeer_find(bcs, bs);
/*
* If the notification is not configured here,
* don't send it.
*/
if (bnp == NULL)
continue;
}
_control_notify(bcs, bs);
}
return 0;
}
static void _control_notify_config(struct bfd_control_socket *bcs,
const char *op, struct bfd_session *bs)
{
struct bfd_control_msg *bcm;
char *jsonstr;
size_t jsonstrlen;
/* Generate JSON response. */
jsonstr = config_notify_config(op, bs);
if (jsonstr == NULL) {
zlog_warn("%s: config_notify_config: failed to get JSON str",
__func__);
return;
}
/* Allocate data and answer. */
jsonstrlen = strlen(jsonstr);
bcm = XMALLOC(MTYPE_BFDD_NOTIFICATION,
sizeof(struct bfd_control_msg) + jsonstrlen);
bcm->bcm_length = htonl(jsonstrlen);
bcm->bcm_ver = BMV_VERSION_1;
bcm->bcm_type = BMT_NOTIFY;
bcm->bcm_id = htons(BCM_NOTIFY_ID);
memcpy(bcm->bcm_data, jsonstr, jsonstrlen);
XFREE(MTYPE_BFDD_NOTIFICATION, jsonstr);
control_queue_enqueue(bcs, bcm);
}
int control_notify_config(const char *op, struct bfd_session *bs)
{
struct bfd_control_socket *bcs;
struct bfd_notify_peer *bnp;
/* Remove the control sockets notification for this peer. */
if (strcmp(op, BCM_NOTIFY_CONFIG_DELETE) == 0 && bs->refcount > 0) {
TAILQ_FOREACH (bcs, &bglobal.bg_bcslist, bcs_entry) {
bnp = control_notifypeer_find(bcs, bs);
if (bnp)
control_notifypeer_free(bcs, bnp);
}
}
/*
* PERFORMANCE: reuse the bfd_control_msg allocated data for
* all control sockets to avoid wasting memory.
*/
TAILQ_FOREACH (bcs, &bglobal.bg_bcslist, bcs_entry) {
/*
* Test for all notifications first, then search for
* specific peers.
*/
if ((bcs->bcs_notify & BCM_NOTIFY_CONFIG) == 0)
continue;
_control_notify_config(bcs, op, bs);
}
return 0;
}

1176
bfdd/dplane.c Normal file

File diff suppressed because it is too large Load diff

114
bfdd/event.c Normal file
View file

@ -0,0 +1,114 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*********************************************************************
* Copyright 2017-2018 Network Device Education Foundation, Inc. ("NetDEF")
*
* event.c: implements the BFD loop event handlers.
*
* Authors
* -------
* Rafael Zalamena <rzalamena@opensourcerouting.org>
*/
#include <zebra.h>
#include "bfd.h"
void tv_normalize(struct timeval *tv);
void tv_normalize(struct timeval *tv)
{
/* Remove seconds part from microseconds. */
tv->tv_sec = tv->tv_usec / 1000000;
tv->tv_usec = tv->tv_usec % 1000000;
}
void bfd_recvtimer_update(struct bfd_session *bs)
{
struct timeval tv = {.tv_sec = 0, .tv_usec = bs->detect_TO};
/* Remove previous schedule if any. */
bfd_recvtimer_delete(bs);
/* Don't add event if peer is deactivated. */
if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) ||
bs->sock == -1)
return;
tv_normalize(&tv);
event_add_timer_tv(master, bfd_recvtimer_cb, bs, &tv,
&bs->recvtimer_ev);
}
void bfd_echo_recvtimer_update(struct bfd_session *bs)
{
struct timeval tv = {.tv_sec = 0, .tv_usec = bs->echo_detect_TO};
/* Remove previous schedule if any. */
bfd_echo_recvtimer_delete(bs);
/* Don't add event if peer is deactivated. */
if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) ||
bs->sock == -1)
return;
tv_normalize(&tv);
event_add_timer_tv(master, bfd_echo_recvtimer_cb, bs, &tv,
&bs->echo_recvtimer_ev);
}
void bfd_xmttimer_update(struct bfd_session *bs, uint64_t jitter)
{
struct timeval tv = {.tv_sec = 0, .tv_usec = jitter};
/* Remove previous schedule if any. */
bfd_xmttimer_delete(bs);
/* Don't add event if peer is deactivated. */
if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) ||
bs->sock == -1)
return;
tv_normalize(&tv);
event_add_timer_tv(master, bfd_xmt_cb, bs, &tv, &bs->xmttimer_ev);
}
void bfd_echo_xmttimer_update(struct bfd_session *bs, uint64_t jitter)
{
struct timeval tv = {.tv_sec = 0, .tv_usec = jitter};
/* Remove previous schedule if any. */
bfd_echo_xmttimer_delete(bs);
/* Don't add event if peer is deactivated. */
if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) ||
bs->sock == -1)
return;
tv_normalize(&tv);
event_add_timer_tv(master, bfd_echo_xmt_cb, bs, &tv,
&bs->echo_xmttimer_ev);
}
void bfd_recvtimer_delete(struct bfd_session *bs)
{
EVENT_OFF(bs->recvtimer_ev);
}
void bfd_echo_recvtimer_delete(struct bfd_session *bs)
{
EVENT_OFF(bs->echo_recvtimer_ev);
}
void bfd_xmttimer_delete(struct bfd_session *bs)
{
EVENT_OFF(bs->xmttimer_ev);
}
void bfd_echo_xmttimer_delete(struct bfd_session *bs)
{
EVENT_OFF(bs->echo_xmttimer_ev);
}

986
bfdd/ptm_adapter.c Normal file
View file

@ -0,0 +1,986 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* BFD PTM adapter code
* Copyright (C) 2018 Network Device Education Foundation, Inc. ("NetDEF")
*/
#include <zebra.h>
#include "lib/libfrr.h"
#include "lib/queue.h"
#include "lib/stream.h"
#include "lib/zclient.h"
#include "lib/printfrr.h"
#include "lib/bfd.h"
#include "bfd.h"
/*
* Data structures
*/
struct ptm_client_notification {
struct bfd_session *pcn_bs;
struct ptm_client *pcn_pc;
TAILQ_ENTRY(ptm_client_notification) pcn_entry;
};
TAILQ_HEAD(pcnqueue, ptm_client_notification);
struct ptm_client {
uint32_t pc_pid;
struct pcnqueue pc_pcnqueue;
TAILQ_ENTRY(ptm_client) pc_entry;
};
TAILQ_HEAD(pcqueue, ptm_client);
static struct pcqueue pcqueue;
static struct zclient *zclient;
/*
* Prototypes
*/
static int _ptm_msg_address(struct stream *msg, int family, const void *addr);
static void _ptm_msg_read_address(struct stream *msg, struct sockaddr_any *sa);
static int _ptm_msg_read(struct stream *msg, int command, vrf_id_t vrf_id,
struct bfd_peer_cfg *bpc, struct ptm_client **pc);
static struct ptm_client *pc_lookup(uint32_t pid);
static struct ptm_client *pc_new(uint32_t pid);
static void pc_free(struct ptm_client *pc);
static void pc_free_all(void);
static struct ptm_client_notification *pcn_new(struct ptm_client *pc,
struct bfd_session *bs);
static struct ptm_client_notification *pcn_lookup(struct ptm_client *pc,
struct bfd_session *bs);
static void pcn_free(struct ptm_client_notification *pcn);
static void bfdd_dest_register(struct stream *msg, vrf_id_t vrf_id);
static void bfdd_dest_deregister(struct stream *msg, vrf_id_t vrf_id);
static void bfdd_client_register(struct stream *msg);
static void bfdd_client_deregister(struct stream *msg);
/*
* Functions
*/
PRINTFRR(2, 3)
static void debug_printbpc(const struct bfd_peer_cfg *bpc, const char *fmt, ...)
{
char timers[3][128] = {};
char minttl_str[32] = {};
char addr[3][128] = {};
char profile[128] = {};
char cbit_str[32];
char msgbuf[512];
va_list vl;
/* Avoid debug calculations if it's disabled. */
if (bglobal.debug_zebra == false)
return;
snprintf(addr[0], sizeof(addr[0]), "peer:%s", satostr(&bpc->bpc_peer));
if (bpc->bpc_local.sa_sin.sin_family)
snprintf(addr[1], sizeof(addr[1]), " local:%s",
satostr(&bpc->bpc_local));
if (bpc->bpc_has_localif)
snprintf(addr[2], sizeof(addr[2]), " ifname:%s",
bpc->bpc_localif);
if (bpc->bpc_has_vrfname)
snprintf(addr[2], sizeof(addr[2]), " vrf:%s", bpc->bpc_vrfname);
if (bpc->bpc_has_recvinterval)
snprintfrr(timers[0], sizeof(timers[0]), " rx:%" PRIu64,
bpc->bpc_recvinterval);
if (bpc->bpc_has_txinterval)
snprintfrr(timers[1], sizeof(timers[1]), " tx:%" PRIu64,
bpc->bpc_recvinterval);
if (bpc->bpc_has_detectmultiplier)
snprintf(timers[2], sizeof(timers[2]), " detect-multiplier:%d",
bpc->bpc_detectmultiplier);
snprintf(cbit_str, sizeof(cbit_str), " cbit:0x%02x", bpc->bpc_cbit);
if (bpc->bpc_has_minimum_ttl)
snprintf(minttl_str, sizeof(minttl_str), " minimum-ttl:%d",
bpc->bpc_minimum_ttl);
if (bpc->bpc_has_profile)
snprintf(profile, sizeof(profile), " profile:%s",
bpc->bpc_profile);
va_start(vl, fmt);
vsnprintf(msgbuf, sizeof(msgbuf), fmt, vl);
va_end(vl);
zlog_debug("%s [mhop:%s %s%s%s%s%s%s%s%s%s]", msgbuf,
bpc->bpc_mhop ? "yes" : "no", addr[0], addr[1], addr[2],
timers[0], timers[1], timers[2], cbit_str, minttl_str,
profile);
}
static void _ptm_bfd_session_del(struct bfd_session *bs, uint8_t diag)
{
if (bglobal.debug_peer_event)
zlog_debug("session-delete: %s", bs_to_string(bs));
/* Change state and notify peer. */
bs->ses_state = PTM_BFD_DOWN;
bs->local_diag = diag;
ptm_bfd_snd(bs, 0);
/* Session reached refcount == 0, lets delete it. */
if (bs->refcount == 0) {
/*
* Sanity check: if there is a refcount bug, we can't delete
* the session a user configured manually. Lets leave a
* message here so we can catch the bug if it exists.
*/
if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG)) {
zlog_err(
"ptm-del-session: [%s] session refcount is zero but it was configured by CLI",
bs_to_string(bs));
} else {
control_notify_config(BCM_NOTIFY_CONFIG_DELETE, bs);
bfd_session_free(bs);
}
}
}
static int _ptm_msg_address(struct stream *msg, int family, const void *addr)
{
stream_putc(msg, family);
switch (family) {
case AF_INET:
stream_put(msg, addr, sizeof(struct in_addr));
stream_putc(msg, 32);
break;
case AF_INET6:
stream_put(msg, addr, sizeof(struct in6_addr));
stream_putc(msg, 128);
break;
default:
assert(0);
break;
}
return 0;
}
int ptm_bfd_notify(struct bfd_session *bs, uint8_t notify_state)
{
struct stream *msg;
bs->stats.znotification++;
/*
* Message format:
* - header: command, vrf
* - l: interface index
* - c: family
* - AF_INET:
* - 4 bytes: ipv4
* - AF_INET6:
* - 16 bytes: ipv6
* - c: prefix length
* - l: bfd status
* - c: family
* - AF_INET:
* - 4 bytes: ipv4
* - AF_INET6:
* - 16 bytes: ipv6
* - c: prefix length
* - c: cbit
*
* Commands: ZEBRA_BFD_DEST_REPLAY
*
* q(64), l(32), w(16), c(8)
*/
msg = zclient->obuf;
stream_reset(msg);
/* TODO: VRF handling */
if (bs->vrf)
zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, bs->vrf->vrf_id);
else
zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, VRF_DEFAULT);
/* This header will be handled by `zebra_ptm.c`. */
stream_putl(msg, ZEBRA_INTERFACE_BFD_DEST_UPDATE);
/* NOTE: Interface is a shortcut to avoid comparing source address. */
if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) && bs->ifp != NULL)
stream_putl(msg, bs->ifp->ifindex);
else
stream_putl(msg, IFINDEX_INTERNAL);
/* BFD destination prefix information. */
_ptm_msg_address(msg, bs->key.family, &bs->key.peer);
/* BFD status */
switch (notify_state) {
case PTM_BFD_UP:
stream_putl(msg, BFD_STATUS_UP);
break;
case PTM_BFD_ADM_DOWN:
stream_putl(msg, BFD_STATUS_ADMIN_DOWN);
break;
case PTM_BFD_DOWN:
case PTM_BFD_INIT:
stream_putl(msg, BFD_STATUS_DOWN);
break;
default:
stream_putl(msg, BFD_STATUS_UNKNOWN);
break;
}
/* BFD source prefix information. */
_ptm_msg_address(msg, bs->key.family, &bs->key.local);
stream_putc(msg, bs->remote_cbit);
/* Write packet size. */
stream_putw_at(msg, 0, stream_get_endp(msg));
return zclient_send_message(zclient);
}
static void _ptm_msg_read_address(struct stream *msg, struct sockaddr_any *sa)
{
uint16_t family;
STREAM_GETW(msg, family);
switch (family) {
case AF_INET:
sa->sa_sin.sin_family = family;
STREAM_GET(&sa->sa_sin.sin_addr, msg,
sizeof(sa->sa_sin.sin_addr));
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
sa->sa_sin.sin_len = sizeof(sa->sa_sin);
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
return;
case AF_INET6:
sa->sa_sin6.sin6_family = family;
STREAM_GET(&sa->sa_sin6.sin6_addr, msg,
sizeof(sa->sa_sin6.sin6_addr));
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
sa->sa_sin6.sin6_len = sizeof(sa->sa_sin6);
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
return;
default:
zlog_warn("ptm-read-address: invalid family: %d", family);
break;
}
stream_failure:
memset(sa, 0, sizeof(*sa));
}
static int _ptm_msg_read(struct stream *msg, int command, vrf_id_t vrf_id,
struct bfd_peer_cfg *bpc, struct ptm_client **pc)
{
uint32_t pid;
size_t ifnamelen;
/*
* Register/Deregister/Update Message format:
*
* Old format (being used by PTM BFD).
* - header: Command, VRF
* - l: pid
* - w: family
* - AF_INET:
* - l: destination ipv4
* - AF_INET6:
* - 16 bytes: destination IPv6
* - command != ZEBRA_BFD_DEST_DEREGISTER
* - l: min_rx
* - l: min_tx
* - c: detect multiplier
* - c: is_multihop?
* - multihop:
* - w: family
* - AF_INET:
* - l: source IPv4 address
* - AF_INET6:
* - 16 bytes: source IPv6 address
* - c: ttl
* - no multihop
* - AF_INET6:
* - w: family
* - 16 bytes: source IPv6 address
* - c: ifname length
* - X bytes: interface name
*
* New format:
* - header: Command, VRF
* - l: pid
* - w: family
* - AF_INET:
* - l: destination IPv4 address
* - AF_INET6:
* - 16 bytes: destination IPv6 address
* - l: min_rx
* - l: min_tx
* - c: detect multiplier
* - c: is_multihop?
* - w: family
* - AF_INET:
* - l: source IPv4 address
* - AF_INET6:
* - 16 bytes: source IPv6 address
* - c: ttl
* - c: ifname length
* - X bytes: interface name
* - c: bfd_cbit
* - c: profile name length.
* - X bytes: profile name.
*
* q(64), l(32), w(16), c(8)
*/
/* Initialize parameters return values. */
memset(bpc, 0, sizeof(*bpc));
*pc = NULL;
/* Find or allocate process context data. */
STREAM_GETL(msg, pid);
*pc = pc_new(pid);
/* Register/update peer information. */
_ptm_msg_read_address(msg, &bpc->bpc_peer);
/* Determine IP type from peer destination. */
bpc->bpc_ipv4 = (bpc->bpc_peer.sa_sin.sin_family == AF_INET);
/* Get peer configuration. */
STREAM_GETL(msg, bpc->bpc_recvinterval);
bpc->bpc_has_recvinterval =
(bpc->bpc_recvinterval != BPC_DEF_RECEIVEINTERVAL);
STREAM_GETL(msg, bpc->bpc_txinterval);
bpc->bpc_has_txinterval =
(bpc->bpc_txinterval != BPC_DEF_TRANSMITINTERVAL);
STREAM_GETC(msg, bpc->bpc_detectmultiplier);
bpc->bpc_has_detectmultiplier =
(bpc->bpc_detectmultiplier != BPC_DEF_DETECTMULTIPLIER);
/* Read (single|multi)hop and its options. */
STREAM_GETC(msg, bpc->bpc_mhop);
/* Read multihop source address and TTL. */
_ptm_msg_read_address(msg, &bpc->bpc_local);
/* Read the minimum TTL (0 means unset or invalid). */
STREAM_GETC(msg, bpc->bpc_minimum_ttl);
if (bpc->bpc_minimum_ttl == 0) {
bpc->bpc_minimum_ttl = BFD_DEF_MHOP_TTL;
bpc->bpc_has_minimum_ttl = false;
} else {
bpc->bpc_minimum_ttl = (BFD_TTL_VAL + 1) - bpc->bpc_minimum_ttl;
bpc->bpc_has_minimum_ttl = true;
}
/*
* Read interface name and make sure it fits our data
* structure, otherwise fail.
*/
STREAM_GETC(msg, ifnamelen);
if (ifnamelen >= sizeof(bpc->bpc_localif)) {
zlog_err("ptm-read: interface name is too big");
return -1;
}
bpc->bpc_has_localif = ifnamelen > 0;
if (bpc->bpc_has_localif) {
STREAM_GET(bpc->bpc_localif, msg, ifnamelen);
bpc->bpc_localif[ifnamelen] = 0;
}
if (vrf_id != VRF_DEFAULT) {
struct vrf *vrf;
vrf = vrf_lookup_by_id(vrf_id);
if (vrf) {
bpc->bpc_has_vrfname = true;
strlcpy(bpc->bpc_vrfname, vrf->name, sizeof(bpc->bpc_vrfname));
} else {
zlog_err("ptm-read: vrf id %u could not be identified",
vrf_id);
return -1;
}
} else {
bpc->bpc_has_vrfname = true;
strlcpy(bpc->bpc_vrfname, VRF_DEFAULT_NAME, sizeof(bpc->bpc_vrfname));
}
/* Read control plane independant configuration. */
STREAM_GETC(msg, bpc->bpc_cbit);
/* Handle profile names. */
STREAM_GETC(msg, ifnamelen);
bpc->bpc_has_profile = ifnamelen > 0;
if (bpc->bpc_has_profile) {
STREAM_GET(bpc->bpc_profile, msg, ifnamelen);
bpc->bpc_profile[ifnamelen] = 0;
}
/* Sanity check: peer and local address must match IP types. */
if (bpc->bpc_local.sa_sin.sin_family != AF_UNSPEC
&& (bpc->bpc_local.sa_sin.sin_family
!= bpc->bpc_peer.sa_sin.sin_family)) {
zlog_warn("ptm-read: peer family doesn't match local type");
return -1;
}
return 0;
stream_failure:
return -1;
}
static void bfdd_dest_register(struct stream *msg, vrf_id_t vrf_id)
{
struct ptm_client *pc;
struct bfd_session *bs;
struct bfd_peer_cfg bpc;
/* Read the client context and peer data. */
if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_REGISTER, vrf_id, &bpc, &pc) == -1)
return;
debug_printbpc(&bpc, "ptm-add-dest: register peer");
/* Find or start new BFD session. */
bs = bs_peer_find(&bpc);
if (bs == NULL) {
bs = ptm_bfd_sess_new(&bpc);
if (bs == NULL) {
if (bglobal.debug_zebra)
zlog_debug(
"ptm-add-dest: failed to create BFD session");
return;
}
} else {
/*
* BFD session was already created, we are just updating the
* current peer.
*
* `ptm-bfd` (or `HAVE_BFDD == 0`) is the only implementation
* that allow users to set peer specific timers via protocol.
* BFD daemon (this code) on the other hand only supports
* changing peer configuration manually (through `peer` node)
* or via profiles.
*/
if (bpc.bpc_has_profile)
bfd_profile_apply(bpc.bpc_profile, bs);
}
/* Create client peer notification register. */
pcn_new(pc, bs);
ptm_bfd_notify(bs, bs->ses_state);
}
static void bfdd_dest_deregister(struct stream *msg, vrf_id_t vrf_id)
{
struct ptm_client *pc;
struct ptm_client_notification *pcn;
struct bfd_session *bs;
struct bfd_peer_cfg bpc;
/* Read the client context and peer data. */
if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_DEREGISTER, vrf_id, &bpc, &pc) == -1)
return;
debug_printbpc(&bpc, "ptm-del-dest: deregister peer");
/* Find or start new BFD session. */
bs = bs_peer_find(&bpc);
if (bs == NULL) {
if (bglobal.debug_zebra)
zlog_debug("ptm-del-dest: failed to find BFD session");
return;
}
/* Unregister client peer notification. */
pcn = pcn_lookup(pc, bs);
if (pcn != NULL) {
pcn_free(pcn);
return;
}
if (bglobal.debug_zebra)
zlog_debug("ptm-del-dest: failed to find BFD session");
/*
* XXX: We either got a double deregistration or the daemon who
* created this is no longer around. Lets try to delete it anyway
* and the worst case is the refcount will detain us.
*/
_ptm_bfd_session_del(bs, BD_NEIGHBOR_DOWN);
}
/*
* header: command, VRF
* l: pid
*/
static void bfdd_client_register(struct stream *msg)
{
uint32_t pid;
/* Find or allocate process context data. */
STREAM_GETL(msg, pid);
pc_new(pid);
return;
stream_failure:
zlog_err("ptm-add-client: failed to register client");
}
/*
* header: command, VRF
* l: pid
*/
static void bfdd_client_deregister(struct stream *msg)
{
struct ptm_client *pc;
uint32_t pid;
/* Find or allocate process context data. */
STREAM_GETL(msg, pid);
pc = pc_lookup(pid);
if (pc == NULL) {
if (bglobal.debug_zebra)
zlog_debug("ptm-del-client: failed to find client: %u",
pid);
return;
}
if (bglobal.debug_zebra)
zlog_debug("ptm-del-client: client pid %u", pid);
pc_free(pc);
return;
stream_failure:
zlog_err("ptm-del-client: failed to deregister client");
}
static int bfdd_replay(ZAPI_CALLBACK_ARGS)
{
struct stream *msg = zclient->ibuf;
uint32_t rcmd;
STREAM_GETL(msg, rcmd);
switch (rcmd) {
case ZEBRA_BFD_DEST_REGISTER:
case ZEBRA_BFD_DEST_UPDATE:
bfdd_dest_register(msg, vrf_id);
break;
case ZEBRA_BFD_DEST_DEREGISTER:
bfdd_dest_deregister(msg, vrf_id);
break;
case ZEBRA_BFD_CLIENT_REGISTER:
bfdd_client_register(msg);
break;
case ZEBRA_BFD_CLIENT_DEREGISTER:
bfdd_client_deregister(msg);
break;
default:
if (bglobal.debug_zebra)
zlog_debug("ptm-replay: invalid message type %u", rcmd);
return -1;
}
return 0;
stream_failure:
zlog_err("ptm-replay: failed to find command");
return -1;
}
static void bfdd_zebra_connected(struct zclient *zc)
{
struct stream *msg = zc->obuf;
/* Clean-up and free ptm clients data memory. */
pc_free_all();
/*
* The replay is an empty message just to trigger client daemons
* configuration replay.
*/
stream_reset(msg);
zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, VRF_DEFAULT);
stream_putl(msg, ZEBRA_BFD_DEST_REPLAY);
stream_putw_at(msg, 0, stream_get_endp(msg));
/* Ask for interfaces information. */
zclient_create_header(msg, ZEBRA_INTERFACE_ADD, VRF_DEFAULT);
/* Send requests. */
zclient_send_message(zclient);
}
static void bfdd_sessions_enable_interface(struct interface *ifp)
{
struct bfd_session_observer *bso;
struct bfd_session *bs;
struct vrf *vrf;
vrf = ifp->vrf;
TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
bs = bso->bso_bs;
/* check vrf name */
if (bs->key.vrfname[0] &&
strcmp(vrf->name, bs->key.vrfname))
continue;
/* If Interface matches vrfname, then bypass iface check */
if (vrf_is_backend_netns() || strcmp(ifp->name, vrf->name)) {
/* Interface name mismatch. */
if (bs->key.ifname[0] &&
strcmp(ifp->name, bs->key.ifname))
continue;
}
/* Skip enabled sessions. */
if (bs->sock != -1)
continue;
/* Try to enable it. */
bfd_session_enable(bs);
}
}
static void bfdd_sessions_disable_interface(struct interface *ifp)
{
struct bfd_session_observer *bso;
struct bfd_session *bs;
TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
bs = bso->bso_bs;
if (bs->ifp != ifp)
continue;
/* Skip disabled sessions. */
if (bs->sock == -1) {
bs->ifp = NULL;
continue;
}
bfd_session_disable(bs);
bs->ifp = NULL;
}
}
void bfdd_sessions_enable_vrf(struct vrf *vrf)
{
struct bfd_session_observer *bso;
struct bfd_session *bs;
/* it may affect configs without interfaces */
TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
bs = bso->bso_bs;
if (bs->vrf)
continue;
if (bs->key.vrfname[0] &&
strcmp(vrf->name, bs->key.vrfname))
continue;
/* need to update the vrf information on
* bs so that callbacks are handled
*/
bs->vrf = vrf;
/* Skip enabled sessions. */
if (bs->sock != -1)
continue;
/* Try to enable it. */
bfd_session_enable(bs);
}
}
void bfdd_sessions_disable_vrf(struct vrf *vrf)
{
struct bfd_session_observer *bso;
struct bfd_session *bs;
TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
bs = bso->bso_bs;
if (bs->key.vrfname[0] &&
strcmp(vrf->name, bs->key.vrfname))
continue;
/* Skip disabled sessions. */
if (bs->sock == -1)
continue;
bfd_session_disable(bs);
bs->vrf = NULL;
}
}
static int bfd_ifp_destroy(struct interface *ifp)
{
if (bglobal.debug_zebra)
zlog_debug("zclient: delete interface %s (VRF %s(%u))",
ifp->name, ifp->vrf->name, ifp->vrf->vrf_id);
bfdd_sessions_disable_interface(ifp);
return 0;
}
static void bfdd_sessions_enable_address(struct connected *ifc)
{
struct bfd_session_observer *bso;
struct bfd_session *bs;
struct prefix prefix;
TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
/* Skip enabled sessions. */
bs = bso->bso_bs;
if (bs->sock != -1)
continue;
/* Check address. */
prefix = bso->bso_addr;
prefix.prefixlen = ifc->address->prefixlen;
if (prefix_cmp(&prefix, ifc->address))
continue;
/* Try to enable it. */
bfd_session_enable(bs);
}
}
static int bfdd_interface_address_update(ZAPI_CALLBACK_ARGS)
{
struct connected *ifc;
ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
if (ifc == NULL)
return 0;
if (bglobal.debug_zebra)
zlog_debug("zclient: %s local address %pFX (VRF %u)",
cmd == ZEBRA_INTERFACE_ADDRESS_ADD ? "add"
: "delete",
ifc->address, vrf_id);
if (cmd == ZEBRA_INTERFACE_ADDRESS_ADD)
bfdd_sessions_enable_address(ifc);
else
connected_free(&ifc);
return 0;
}
static int bfd_ifp_create(struct interface *ifp)
{
if (bglobal.debug_zebra)
zlog_debug("zclient: add interface %s (VRF %s(%u))", ifp->name,
ifp->vrf->name, ifp->vrf->vrf_id);
bfdd_sessions_enable_interface(ifp);
return 0;
}
static zclient_handler *const bfd_handlers[] = {
/*
* We'll receive all messages through replay, however it will
* contain a special field with the real command inside so we
* avoid having to create too many handlers.
*/
[ZEBRA_BFD_DEST_REPLAY] = bfdd_replay,
/* Learn about new addresses being registered. */
[ZEBRA_INTERFACE_ADDRESS_ADD] = bfdd_interface_address_update,
[ZEBRA_INTERFACE_ADDRESS_DELETE] = bfdd_interface_address_update,
};
void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv)
{
hook_register_prio(if_real, 0, bfd_ifp_create);
hook_register_prio(if_unreal, 0, bfd_ifp_destroy);
zclient = zclient_new(master, &zclient_options_default, bfd_handlers,
array_size(bfd_handlers));
assert(zclient != NULL);
zclient_init(zclient, ZEBRA_ROUTE_BFD, 0, bfdd_priv);
/* Send replay request on zebra connect. */
zclient->zebra_connected = bfdd_zebra_connected;
}
void bfdd_zclient_register(vrf_id_t vrf_id)
{
if (!zclient || zclient->sock < 0)
return;
zclient_send_reg_requests(zclient, vrf_id);
}
void bfdd_zclient_unregister(vrf_id_t vrf_id)
{
if (!zclient || zclient->sock < 0)
return;
zclient_send_dereg_requests(zclient, vrf_id);
}
void bfdd_zclient_stop(void)
{
zclient_stop(zclient);
/* Clean-up and free ptm clients data memory. */
pc_free_all();
}
void bfdd_zclient_terminate(void)
{
zclient_free(zclient);
}
/*
* Client handling.
*/
static struct ptm_client *pc_lookup(uint32_t pid)
{
struct ptm_client *pc;
TAILQ_FOREACH (pc, &pcqueue, pc_entry) {
if (pc->pc_pid != pid)
continue;
break;
}
return pc;
}
static struct ptm_client *pc_new(uint32_t pid)
{
struct ptm_client *pc;
/* Look up first, if not found create the client. */
pc = pc_lookup(pid);
if (pc != NULL)
return pc;
/* Allocate the client data and save it. */
pc = XCALLOC(MTYPE_BFDD_CONTROL, sizeof(*pc));
pc->pc_pid = pid;
TAILQ_INSERT_HEAD(&pcqueue, pc, pc_entry);
return pc;
}
static void pc_free(struct ptm_client *pc)
{
struct ptm_client_notification *pcn;
TAILQ_REMOVE(&pcqueue, pc, pc_entry);
while (!TAILQ_EMPTY(&pc->pc_pcnqueue)) {
pcn = TAILQ_FIRST(&pc->pc_pcnqueue);
pcn_free(pcn);
}
XFREE(MTYPE_BFDD_CONTROL, pc);
}
static void pc_free_all(void)
{
struct ptm_client *pc;
while (!TAILQ_EMPTY(&pcqueue)) {
pc = TAILQ_FIRST(&pcqueue);
pc_free(pc);
}
}
static struct ptm_client_notification *pcn_new(struct ptm_client *pc,
struct bfd_session *bs)
{
struct ptm_client_notification *pcn;
/* Try to find an existing pcn fist. */
pcn = pcn_lookup(pc, bs);
if (pcn != NULL)
return pcn;
/* Save the client notification data. */
pcn = XCALLOC(MTYPE_BFDD_NOTIFICATION, sizeof(*pcn));
TAILQ_INSERT_HEAD(&pc->pc_pcnqueue, pcn, pcn_entry);
pcn->pcn_pc = pc;
pcn->pcn_bs = bs;
bs->refcount++;
return pcn;
}
static struct ptm_client_notification *pcn_lookup(struct ptm_client *pc,
struct bfd_session *bs)
{
struct ptm_client_notification *pcn;
TAILQ_FOREACH (pcn, &pc->pc_pcnqueue, pcn_entry) {
if (pcn->pcn_bs != bs)
continue;
break;
}
return pcn;
}
static void pcn_free(struct ptm_client_notification *pcn)
{
struct ptm_client *pc;
struct bfd_session *bs;
/* Handle session de-registration. */
bs = pcn->pcn_bs;
pcn->pcn_bs = NULL;
bs->refcount--;
/* Log modification to users. */
if (bglobal.debug_zebra)
zlog_debug("ptm-del-session: [%s] refcount=%" PRIu64,
bs_to_string(bs), bs->refcount);
/* Set session down. */
_ptm_bfd_session_del(bs, BD_NEIGHBOR_DOWN);
/* Handle ptm_client deregistration. */
pc = pcn->pcn_pc;
pcn->pcn_pc = NULL;
TAILQ_REMOVE(&pc->pc_pcnqueue, pcn, pcn_entry);
XFREE(MTYPE_BFDD_NOTIFICATION, pcn);
}

50
bfdd/subdir.am Normal file
View file

@ -0,0 +1,50 @@
#
# bfdd
#
if BFDD
noinst_LIBRARIES += bfdd/libbfd.a
sbin_PROGRAMS += bfdd/bfdd
vtysh_daemons += bfdd
man8 += $(MANBUILD)/frr-bfdd.8
endif
bfdd_libbfd_a_SOURCES = \
bfdd/bfd.c \
bfdd/bfdd_nb.c \
bfdd/bfdd_nb_config.c \
bfdd/bfdd_nb_state.c \
bfdd/bfdd_vty.c \
bfdd/bfdd_cli.c \
bfdd/bfd_packet.c \
bfdd/config.c \
bfdd/control.c \
bfdd/dplane.c \
bfdd/event.c \
bfdd/ptm_adapter.c \
# end
# Install headers so it can be used by external data plane
# implementations.
bfdd_headersdir = $(pkgincludedir)/bfdd
bfdd_headers_HEADERS = \
bfdd/bfddp_packet.h \
# end
clippy_scan += \
bfdd/bfdd_cli.c \
bfdd/bfdd_vty.c \
# end
noinst_HEADERS += \
bfdd/bfdctl.h \
bfdd/bfdd_nb.h \
bfdd/bfd.h \
# end
nodist_bfdd_bfdd_SOURCES = \
yang/frr-bfdd.yang.c \
# end
bfdd_bfdd_SOURCES = bfdd/bfdd.c
bfdd_bfdd_LDADD = bfdd/libbfd.a lib/libfrr.la

3
bgpd/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
bgpd
bgp_btoa
bgpd.conf

10
bgpd/Makefile Normal file
View file

@ -0,0 +1,10 @@
all: ALWAYS
@$(MAKE) -s -C .. bgpd/bgpd
%: ALWAYS
@$(MAKE) -s -C .. bgpd/$@
Makefile:
#nothing
ALWAYS:
.PHONY: ALWAYS makefiles
.SUFFIXES:

517
bgpd/bgp_addpath.c Normal file
View file

@ -0,0 +1,517 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Addpath TX ID selection, and related utilities
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "bgp_addpath.h"
#include "bgp_route.h"
#include "bgp_open.h"
#include "bgp_packet.h"
static const struct bgp_addpath_strategy_names strat_names[BGP_ADDPATH_MAX] = {
{
.config_name = "addpath-tx-all-paths",
.human_name = "All",
.human_description = "Advertise all paths via addpath",
.type_json_name = "addpathTxAllPaths",
.id_json_name = "addpathTxIdAll"
},
{
.config_name = "addpath-tx-bestpath-per-AS",
.human_name = "Best-Per-AS",
.human_description = "Advertise bestpath per AS via addpath",
.type_json_name = "addpathTxBestpathPerAS",
.id_json_name = "addpathTxIdBestPerAS"
},
{
.config_name = "addpath-tx-best-selected",
.human_name = "Best-Selected",
.human_description = "Advertise best N selected paths via addpath",
.type_json_name = "addpathTxBestSelectedPaths",
.id_json_name = "addpathTxIdBestSelected"
},
};
static const struct bgp_addpath_strategy_names unknown_names = {
.config_name = "addpath-tx-unknown",
.human_name = "Unknown-Addpath-Strategy",
.human_description = "Unknown Addpath Strategy",
.type_json_name = "addpathTxUnknown",
.id_json_name = "addpathTxIdUnknown"
};
/*
* Returns a structure full of strings associated with an addpath type. Will
* never return null.
*/
const struct bgp_addpath_strategy_names *
bgp_addpath_names(enum bgp_addpath_strat strat)
{
if (strat < BGP_ADDPATH_MAX)
return &(strat_names[strat]);
else
return &unknown_names;
};
/*
* Returns if any peer is transmitting addpaths for a given afi/safi.
*/
bool bgp_addpath_is_addpath_used(struct bgp_addpath_bgp_data *d, afi_t afi,
safi_t safi)
{
return d->total_peercount[afi][safi] > 0;
}
/*
* Initialize the BGP instance level data for addpath.
*/
void bgp_addpath_init_bgp_data(struct bgp_addpath_bgp_data *d)
{
safi_t safi;
afi_t afi;
int i;
FOREACH_AFI_SAFI (afi, safi) {
for (i = 0; i < BGP_ADDPATH_MAX; i++) {
d->id_allocators[afi][safi][i] = NULL;
d->peercount[afi][safi][i] = 0;
}
d->total_peercount[afi][safi] = 0;
}
}
/*
* Free up resources associated with BGP route info structures.
*/
void bgp_addpath_free_info_data(struct bgp_addpath_info_data *d,
struct bgp_addpath_node_data *nd)
{
int i;
for (i = 0; i < BGP_ADDPATH_MAX; i++) {
if (d->addpath_tx_id[i] != IDALLOC_INVALID)
idalloc_free_to_pool(&nd->free_ids[i],
d->addpath_tx_id[i]);
}
}
/*
* Return the addpath ID used to send a particular route, to a particular peer,
* in a particular AFI/SAFI.
*/
uint32_t bgp_addpath_id_for_peer(struct peer *peer, afi_t afi, safi_t safi,
struct bgp_addpath_info_data *d)
{
if (safi == SAFI_LABELED_UNICAST)
safi = SAFI_UNICAST;
if (peer->addpath_type[afi][safi] < BGP_ADDPATH_MAX)
return d->addpath_tx_id[peer->addpath_type[afi][safi]];
else
return IDALLOC_INVALID;
}
/*
* Returns true if the path has an assigned addpath ID for any of the addpath
* strategies.
*/
bool bgp_addpath_info_has_ids(struct bgp_addpath_info_data *d)
{
int i;
for (i = 0; i < BGP_ADDPATH_MAX; i++)
if (d->addpath_tx_id[i] != 0)
return true;
return false;
}
/*
* Releases any ID's associated with the BGP prefix.
*/
void bgp_addpath_free_node_data(struct bgp_addpath_bgp_data *bd,
struct bgp_addpath_node_data *nd, afi_t afi,
safi_t safi)
{
int i;
for (i = 0; i < BGP_ADDPATH_MAX; i++) {
idalloc_drain_pool(bd->id_allocators[afi][safi][i],
&(nd->free_ids[i]));
}
}
/*
* Check to see if the addpath strategy requires DMED to be configured to work.
*/
bool bgp_addpath_dmed_required(int strategy)
{
return strategy == BGP_ADDPATH_BEST_PER_AS;
}
/*
* Return true if this is a path we should advertise due to a
* configured addpath-tx knob
*/
bool bgp_addpath_tx_path(enum bgp_addpath_strat strat, struct bgp_path_info *pi)
{
switch (strat) {
case BGP_ADDPATH_NONE:
return false;
case BGP_ADDPATH_ALL:
return true;
case BGP_ADDPATH_BEST_PER_AS:
if (CHECK_FLAG(pi->flags, BGP_PATH_DMED_SELECTED))
return true;
else
return false;
case BGP_ADDPATH_BEST_SELECTED:
return true;
case BGP_ADDPATH_MAX:
return false;
}
assert(!"Reached end of function we should never hit");
}
static void bgp_addpath_flush_type_rn(struct bgp *bgp, afi_t afi, safi_t safi,
enum bgp_addpath_strat addpath_type,
struct bgp_dest *dest)
{
struct bgp_path_info *pi;
if (safi == SAFI_LABELED_UNICAST)
safi = SAFI_UNICAST;
idalloc_drain_pool(
bgp->tx_addpath.id_allocators[afi][safi][addpath_type],
&(dest->tx_addpath.free_ids[addpath_type]));
for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
if (pi->tx_addpath.addpath_tx_id[addpath_type]
!= IDALLOC_INVALID) {
idalloc_free(
bgp->tx_addpath
.id_allocators[afi][safi][addpath_type],
pi->tx_addpath.addpath_tx_id[addpath_type]);
pi->tx_addpath.addpath_tx_id[addpath_type] =
IDALLOC_INVALID;
}
}
}
/*
* Purge all addpath ID's on a BGP instance associated with the addpath
* strategy, and afi/safi combination. This lets us let go of all memory held to
* track ID numbers associated with an addpath type not in use. Since
* post-bestpath ID processing is skipped for types not used, this is the only
* chance to free this data.
*/
static void bgp_addpath_flush_type(struct bgp *bgp, afi_t afi, safi_t safi,
enum bgp_addpath_strat addpath_type)
{
struct bgp_dest *dest, *ndest;
if (safi == SAFI_LABELED_UNICAST)
safi = SAFI_UNICAST;
for (dest = bgp_table_top(bgp->rib[afi][safi]); dest;
dest = bgp_route_next(dest)) {
if (safi == SAFI_MPLS_VPN) {
struct bgp_table *table;
table = bgp_dest_get_bgp_table_info(dest);
if (!table)
continue;
for (ndest = bgp_table_top(table); ndest;
ndest = bgp_route_next(ndest))
bgp_addpath_flush_type_rn(bgp, afi, safi,
addpath_type, ndest);
} else {
bgp_addpath_flush_type_rn(bgp, afi, safi, addpath_type,
dest);
}
}
idalloc_destroy(bgp->tx_addpath.id_allocators[afi][safi][addpath_type]);
bgp->tx_addpath.id_allocators[afi][safi][addpath_type] = NULL;
}
/*
* Allocate an Addpath ID for the given type on a path, if necessary.
*/
static void bgp_addpath_populate_path(struct id_alloc *allocator,
struct bgp_path_info *path,
enum bgp_addpath_strat addpath_type)
{
if (bgp_addpath_tx_path(addpath_type, path)) {
path->tx_addpath.addpath_tx_id[addpath_type] =
idalloc_allocate(allocator);
}
}
/*
* Compute addpath ID's on a BGP instance associated with the addpath strategy,
* and afi/safi combination. Since we won't waste the time computing addpath IDs
* for unused strategies, the first time a peer is configured to use a strategy,
* we have to backfill the data.
* In labeled-unicast, addpath allocations SHOULD be done in unicast SAFI.
*/
static void bgp_addpath_populate_type(struct bgp *bgp, afi_t afi, safi_t safi,
enum bgp_addpath_strat addpath_type)
{
struct bgp_dest *dest, *ndest;
char buf[200];
struct id_alloc *allocator;
if (safi == SAFI_LABELED_UNICAST)
safi = SAFI_UNICAST;
snprintf(buf, sizeof(buf), "Addpath ID Allocator %s:%d/%d",
bgp_addpath_names(addpath_type)->config_name, (int)afi,
(int)safi);
buf[sizeof(buf) - 1] = '\0';
zlog_info("Computing addpath IDs for addpath type %s",
bgp_addpath_names(addpath_type)->human_name);
bgp->tx_addpath.id_allocators[afi][safi][addpath_type] =
idalloc_new(buf);
idalloc_reserve(bgp->tx_addpath.id_allocators[afi][safi][addpath_type],
BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE);
allocator = bgp->tx_addpath.id_allocators[afi][safi][addpath_type];
for (dest = bgp_table_top(bgp->rib[afi][safi]); dest;
dest = bgp_route_next(dest)) {
struct bgp_path_info *bi;
if (safi == SAFI_MPLS_VPN) {
struct bgp_table *table;
table = bgp_dest_get_bgp_table_info(dest);
if (!table)
continue;
for (ndest = bgp_table_top(table); ndest;
ndest = bgp_route_next(ndest))
for (bi = bgp_dest_get_bgp_path_info(ndest); bi;
bi = bi->next)
bgp_addpath_populate_path(allocator, bi,
addpath_type);
} else {
for (bi = bgp_dest_get_bgp_path_info(dest); bi;
bi = bi->next)
bgp_addpath_populate_path(allocator, bi,
addpath_type);
}
}
}
/*
* Handle updates to a peer or group's addpath strategy. If after adjusting
* counts a addpath strategy is in use for the first time, or no longer in use,
* the IDs for that strategy will be populated or flushed.
*/
void bgp_addpath_type_changed(struct bgp *bgp)
{
afi_t afi;
safi_t safi;
struct listnode *node, *nnode;
struct peer *peer;
int peer_count[AFI_MAX][SAFI_MAX][BGP_ADDPATH_MAX];
enum bgp_addpath_strat type;
FOREACH_AFI_SAFI(afi, safi) {
for (type=0; type<BGP_ADDPATH_MAX; type++) {
peer_count[afi][safi][type] = 0;
}
bgp->tx_addpath.total_peercount[afi][safi] = 0;
}
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
FOREACH_AFI_SAFI(afi, safi) {
type = peer->addpath_type[afi][safi];
if (type != BGP_ADDPATH_NONE) {
peer_count[afi][safi][type] += 1;
bgp->tx_addpath.total_peercount[afi][safi] += 1;
}
}
}
FOREACH_AFI_SAFI(afi, safi) {
for (type=0; type<BGP_ADDPATH_MAX; type++) {
int old = bgp->tx_addpath.peercount[afi][safi][type];
int new = peer_count[afi][safi][type];
bgp->tx_addpath.peercount[afi][safi][type] = new;
if (old == 0 && new != 0) {
bgp_addpath_populate_type(bgp, afi, safi,
type);
} else if (old != 0 && new == 0) {
bgp_addpath_flush_type(bgp, afi, safi, type);
}
}
}
}
int bgp_addpath_capability_action(enum bgp_addpath_strat addpath_type,
uint8_t paths)
{
int action = CAPABILITY_ACTION_UNSET;
switch (addpath_type) {
case BGP_ADDPATH_ALL:
case BGP_ADDPATH_BEST_PER_AS:
action = CAPABILITY_ACTION_SET;
break;
case BGP_ADDPATH_BEST_SELECTED:
if (paths)
action = CAPABILITY_ACTION_SET;
else
action = CAPABILITY_ACTION_UNSET;
break;
case BGP_ADDPATH_NONE:
case BGP_ADDPATH_MAX:
action = CAPABILITY_ACTION_UNSET;
break;
}
return action;
}
/*
* Change the addpath type assigned to a peer, or peer group. In addition to
* adjusting the counts, peer sessions will be reset as needed to make the
* change take effect.
*/
void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi,
enum bgp_addpath_strat addpath_type,
uint8_t paths)
{
struct bgp *bgp = peer->bgp;
enum bgp_addpath_strat old_type;
struct listnode *node, *nnode;
struct peer *tmp_peer;
struct peer_group *group;
int action = bgp_addpath_capability_action(addpath_type, paths);
if (safi == SAFI_LABELED_UNICAST)
safi = SAFI_UNICAST;
peer->addpath_best_selected[afi][safi] = paths;
old_type = peer->addpath_type[afi][safi];
if (addpath_type == old_type)
return;
if (addpath_type == BGP_ADDPATH_NONE && peer->group &&
!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
/* A "no" config on a group member inherits group */
addpath_type = peer->group->conf->addpath_type[afi][safi];
}
peer->addpath_type[afi][safi] = addpath_type;
bgp_addpath_type_changed(bgp);
if (addpath_type != BGP_ADDPATH_NONE) {
if (bgp_addpath_dmed_required(addpath_type)) {
if (!CHECK_FLAG(bgp->flags,
BGP_FLAG_DETERMINISTIC_MED)) {
zlog_warn(
"%s: enabling bgp deterministic-med, this is required for addpath-tx-bestpath-per-AS",
peer->host);
SET_FLAG(bgp->flags,
BGP_FLAG_DETERMINISTIC_MED);
bgp_recalculate_all_bestpaths(bgp);
}
}
}
zlog_info("Resetting peer %s%pBP due to change in addpath config",
CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP) ? "group " : "",
peer);
if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
group = peer->group;
/* group will be null as peer_group_delete calls peer_delete on
* group->conf. That peer_delete will eventuallly end up here
* if the group was configured to tx addpaths.
*/
if (group != NULL) {
for (ALL_LIST_ELEMENTS(group->peer, node, nnode,
tmp_peer)) {
if (tmp_peer->addpath_type[afi][safi] ==
old_type) {
bgp_addpath_set_peer_type(
tmp_peer, afi, safi,
addpath_type, paths);
}
}
}
} else {
if (!CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV) &&
!CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV))
peer_change_action(peer, afi, safi, peer_change_reset);
}
bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ADDPATH, action);
}
/*
* Intended to run after bestpath. This function will take TX IDs from paths
* that no longer need them, and give them to paths that do. This prevents
* best-per-as updates from needing to do a separate withdraw and update just to
* swap out which path is sent.
*/
void bgp_addpath_update_ids(struct bgp *bgp, struct bgp_dest *bn, afi_t afi,
safi_t safi)
{
int i;
struct bgp_path_info *pi;
struct id_alloc_pool **pool_ptr;
if (safi == SAFI_LABELED_UNICAST)
safi = SAFI_UNICAST;
for (i = 0; i < BGP_ADDPATH_MAX; i++) {
struct id_alloc *alloc =
bgp->tx_addpath.id_allocators[afi][safi][i];
pool_ptr = &(bn->tx_addpath.free_ids[i]);
if (bgp->tx_addpath.peercount[afi][safi][i] == 0)
continue;
/* Free Unused IDs back to the pool.*/
for (pi = bgp_dest_get_bgp_path_info(bn); pi; pi = pi->next) {
if (pi->tx_addpath.addpath_tx_id[i] != IDALLOC_INVALID
&& !bgp_addpath_tx_path(i, pi)) {
idalloc_free_to_pool(pool_ptr,
pi->tx_addpath.addpath_tx_id[i]);
pi->tx_addpath.addpath_tx_id[i] =
IDALLOC_INVALID;
}
}
/* Give IDs to paths that need them (pulling from the pool) */
for (pi = bgp_dest_get_bgp_path_info(bn); pi; pi = pi->next) {
if (pi->tx_addpath.addpath_tx_id[i] == IDALLOC_INVALID
&& bgp_addpath_tx_path(i, pi)) {
pi->tx_addpath.addpath_tx_id[i] =
idalloc_allocate_prefer_pool(
alloc, pool_ptr);
}
}
/* Free any IDs left in the pool to the main allocator */
idalloc_drain_pool(alloc, pool_ptr);
}
}

74
bgpd/bgp_addpath.h Normal file
View file

@ -0,0 +1,74 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Addpath TX ID selection, and related utilities
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates
*/
#ifndef _QUAGGA_BGPD_TX_ADDPATH_H
#define _QUAGGA_BGPD_TX_ADDPATH_H
#include <stdint.h>
#include <zebra.h>
#include "bgpd/bgp_addpath_types.h"
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_table.h"
#include "lib/json.h"
struct bgp_addpath_capability {
uint16_t afi;
uint8_t safi;
uint8_t flags;
};
struct bgp_paths_limit_capability {
uint16_t afi;
uint8_t safi;
uint16_t paths_limit;
} __attribute__((packed));
#define BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE 1
void bgp_addpath_init_bgp_data(struct bgp_addpath_bgp_data *d);
bool bgp_addpath_is_addpath_used(struct bgp_addpath_bgp_data *d, afi_t afi,
safi_t safi);
void bgp_addpath_free_node_data(struct bgp_addpath_bgp_data *bd,
struct bgp_addpath_node_data *nd,
afi_t afi, safi_t safi);
void bgp_addpath_free_info_data(struct bgp_addpath_info_data *d,
struct bgp_addpath_node_data *nd);
bool bgp_addpath_info_has_ids(struct bgp_addpath_info_data *d);
uint32_t bgp_addpath_id_for_peer(struct peer *peer, afi_t afi, safi_t safi,
struct bgp_addpath_info_data *d);
const struct bgp_addpath_strategy_names *
bgp_addpath_names(enum bgp_addpath_strat strat);
bool bgp_addpath_dmed_required(int strategy);
/*
* Return true if this is a path we should advertise due to a configured
* addpath-tx knob
*/
bool bgp_addpath_tx_path(enum bgp_addpath_strat strat,
struct bgp_path_info *pi);
/*
* Change the type of addpath used for a peer.
*/
void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi,
enum bgp_addpath_strat addpath_type,
uint8_t paths);
void bgp_addpath_update_ids(struct bgp *bgp, struct bgp_dest *dest, afi_t afi,
safi_t safi);
void bgp_addpath_type_changed(struct bgp *bgp);
extern int bgp_addpath_capability_action(enum bgp_addpath_strat addpath_type,
uint8_t paths);
#endif

43
bgpd/bgp_addpath_types.h Normal file
View file

@ -0,0 +1,43 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Addpath TX ID selection, and related utilities
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates
*/
#ifndef _QUAGGA_BGPD_TX_ADDPATH_DATA_H
#define _QUAGGA_BGPD_TX_ADDPATH_DATA_H
#include "lib/id_alloc.h"
#include <stdint.h>
enum bgp_addpath_strat {
BGP_ADDPATH_ALL = 0,
BGP_ADDPATH_BEST_PER_AS,
BGP_ADDPATH_BEST_SELECTED,
BGP_ADDPATH_MAX,
BGP_ADDPATH_NONE,
};
/* TX Addpath structures */
struct bgp_addpath_bgp_data {
unsigned int peercount[AFI_MAX][SAFI_MAX][BGP_ADDPATH_MAX];
unsigned int total_peercount[AFI_MAX][SAFI_MAX];
struct id_alloc *id_allocators[AFI_MAX][SAFI_MAX][BGP_ADDPATH_MAX];
};
struct bgp_addpath_node_data {
struct id_alloc_pool *free_ids[BGP_ADDPATH_MAX];
};
struct bgp_addpath_info_data {
uint32_t addpath_tx_id[BGP_ADDPATH_MAX];
};
struct bgp_addpath_strategy_names {
const char *config_name;
const char *human_name; /* path detail non-json */
const char *human_description; /* non-json peer descriptions */
const char *type_json_name; /* json peer listings */
const char *id_json_name; /* path json output for tx ID# */
};
#endif

229
bgpd/bgp_advertise.c Normal file
View file

@ -0,0 +1,229 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* BGP advertisement and adjacency
* Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro
*/
#include <zebra.h>
#include "command.h"
#include "memory.h"
#include "prefix.h"
#include "hash.h"
#include "frrevent.h"
#include "queue.h"
#include "filter.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_table.h"
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_advertise.h"
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_debug.h"
#include "bgpd/bgp_aspath.h"
#include "bgpd/bgp_packet.h"
#include "bgpd/bgp_fsm.h"
#include "bgpd/bgp_mplsvpn.h"
#include "bgpd/bgp_updgrp.h"
/* BGP advertise attribute is used for pack same attribute update into
one packet. To do that we maintain attribute hash in struct
peer. */
struct bgp_advertise_attr *bgp_advertise_attr_new(void)
{
return XCALLOC(MTYPE_BGP_ADVERTISE_ATTR,
sizeof(struct bgp_advertise_attr));
}
void bgp_advertise_attr_free(struct bgp_advertise_attr *baa)
{
bgp_advertise_attr_fifo_fini(&baa->fifo);
XFREE(MTYPE_BGP_ADVERTISE_ATTR, baa);
}
static void *bgp_advertise_attr_hash_alloc(void *p)
{
struct bgp_advertise_attr *ref = (struct bgp_advertise_attr *)p;
struct bgp_advertise_attr *baa;
baa = bgp_advertise_attr_new();
baa->attr = ref->attr;
bgp_advertise_attr_fifo_init(&baa->fifo);
return baa;
}
unsigned int bgp_advertise_attr_hash_key(const void *p)
{
const struct bgp_advertise_attr *baa = p;
return attrhash_key_make(baa->attr);
}
bool bgp_advertise_attr_hash_cmp(const void *p1, const void *p2)
{
const struct bgp_advertise_attr *baa1 = p1;
const struct bgp_advertise_attr *baa2 = p2;
return attrhash_cmp(baa1->attr, baa2->attr);
}
/* BGP update and withdraw information is stored in BGP advertise
structure. This structure is referred from BGP adjacency
information. */
struct bgp_advertise *bgp_advertise_new(void)
{
return XCALLOC(MTYPE_BGP_ADVERTISE, sizeof(struct bgp_advertise));
}
void bgp_advertise_free(struct bgp_advertise *adv)
{
if (adv->pathi)
/* bgp_advertise bgp_path_info reference */
bgp_path_info_unlock(adv->pathi);
XFREE(MTYPE_BGP_ADVERTISE, adv);
}
void bgp_advertise_add(struct bgp_advertise_attr *baa,
struct bgp_advertise *adv)
{
bgp_advertise_attr_fifo_add_tail(&baa->fifo, adv);
}
void bgp_advertise_delete(struct bgp_advertise_attr *baa,
struct bgp_advertise *adv)
{
bgp_advertise_attr_fifo_del(&baa->fifo, adv);
}
struct bgp_advertise_attr *bgp_advertise_attr_intern(struct hash *hash,
struct attr *attr)
{
struct bgp_advertise_attr ref;
struct bgp_advertise_attr *baa;
ref.attr = bgp_attr_intern(attr);
baa = (struct bgp_advertise_attr *)hash_get(
hash, &ref, bgp_advertise_attr_hash_alloc);
baa->refcnt++;
return baa;
}
void bgp_advertise_attr_unintern(struct hash *hash,
struct bgp_advertise_attr *baa)
{
if (baa->refcnt)
baa->refcnt--;
if (baa->refcnt && baa->attr)
bgp_attr_unintern(&baa->attr);
else {
if (baa->attr) {
hash_release(hash, baa);
bgp_attr_unintern(&baa->attr);
}
bgp_advertise_attr_free(baa);
}
}
bool bgp_adj_out_lookup(struct peer *peer, struct bgp_dest *dest,
uint32_t addpath_tx_id)
{
struct bgp_adj_out *adj;
struct peer_af *paf;
afi_t afi;
safi_t safi;
bool addpath_capable;
RB_FOREACH (adj, bgp_adj_out_rb, &dest->adj_out)
SUBGRP_FOREACH_PEER (adj->subgroup, paf)
if (paf->peer == peer) {
afi = SUBGRP_AFI(adj->subgroup);
safi = SUBGRP_SAFI(adj->subgroup);
addpath_capable =
bgp_addpath_encode_tx(peer, afi, safi);
/* Match on a specific addpath_tx_id if we are
* using addpath for
* this
* peer and if an addpath_tx_id was specified */
if (addpath_capable && addpath_tx_id
&& adj->addpath_tx_id != addpath_tx_id)
continue;
return (adj->adv
? (adj->adv->baa ? true : false)
: (adj->attr ? true : false));
}
return false;
}
void bgp_adj_in_set(struct bgp_dest *dest, struct peer *peer, struct attr *attr,
uint32_t addpath_id, struct bgp_labels *labels)
{
struct bgp_adj_in *adj;
for (adj = dest->adj_in; adj; adj = adj->next) {
if (adj->peer == peer && adj->addpath_rx_id == addpath_id) {
if (!attrhash_cmp(adj->attr, attr)) {
bgp_attr_unintern(&adj->attr);
adj->attr = bgp_attr_intern(attr);
}
if (!bgp_labels_cmp(adj->labels, labels)) {
bgp_labels_unintern(&adj->labels);
adj->labels = bgp_labels_intern(labels);
}
return;
}
}
adj = XCALLOC(MTYPE_BGP_ADJ_IN, sizeof(struct bgp_adj_in));
adj->peer = peer_lock(peer); /* adj_in peer reference */
adj->attr = bgp_attr_intern(attr);
adj->uptime = monotime(NULL);
adj->addpath_rx_id = addpath_id;
adj->labels = bgp_labels_intern(labels);
BGP_ADJ_IN_ADD(dest, adj);
peer->stat_pfx_adj_rib_in++;
bgp_dest_lock_node(dest);
}
void bgp_adj_in_remove(struct bgp_dest **dest, struct bgp_adj_in *bai)
{
bgp_attr_unintern(&bai->attr);
bgp_labels_unintern(&bai->labels);
if (bai->peer)
bai->peer->stat_pfx_adj_rib_in--;
BGP_ADJ_IN_DEL(*dest, bai);
*dest = bgp_dest_unlock_node(*dest);
peer_unlock(bai->peer); /* adj_in peer reference */
XFREE(MTYPE_BGP_ADJ_IN, bai);
}
bool bgp_adj_in_unset(struct bgp_dest **dest, struct peer *peer,
uint32_t addpath_id)
{
struct bgp_adj_in *adj;
struct bgp_adj_in *adj_next;
adj = (*dest)->adj_in;
if (!adj)
return false;
while (adj) {
adj_next = adj->next;
if (adj->peer == peer && adj->addpath_rx_id == addpath_id)
bgp_adj_in_remove(dest, adj);
adj = adj_next;
assert(*dest);
}
return true;
}

165
bgpd/bgp_advertise.h Normal file
View file

@ -0,0 +1,165 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* BGP advertisement and adjacency
* Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro
*/
#ifndef _QUAGGA_BGP_ADVERTISE_H
#define _QUAGGA_BGP_ADVERTISE_H
#include "lib/typesafe.h"
PREDECL_DLIST(bgp_adv_fifo);
struct update_subgroup;
struct bgp_advertise;
PREDECL_DLIST(bgp_advertise_attr_fifo);
struct bgp_advertise_attr;
/* BGP advertise attribute. */
struct bgp_advertise {
/* FIFO for advertisement. */
struct bgp_adv_fifo_item fifo;
/* FIFO for this item in the bgp_advertise_attr fifo */
struct bgp_advertise_attr_fifo_item item;
/* Prefix information. */
struct bgp_dest *dest;
/* Reference pointer. */
struct bgp_adj_out *adj;
/* Advertisement attribute. */
struct bgp_advertise_attr *baa;
/* BGP info. */
struct bgp_path_info *pathi;
};
DECLARE_DLIST(bgp_advertise_attr_fifo, struct bgp_advertise, item);
DECLARE_DLIST(bgp_adv_fifo, struct bgp_advertise, fifo);
/* BGP advertise attribute. */
struct bgp_advertise_attr {
/* Head of advertisement pointer. */
struct bgp_advertise_attr_fifo_head fifo;
/* Reference counter. */
unsigned long refcnt;
/* Attribute pointer to be announced. */
struct attr *attr;
};
/* BGP adjacency out. */
struct bgp_adj_out {
/* RB Tree of adjacency entries */
RB_ENTRY(bgp_adj_out) adj_entry;
/* Advertised subgroup. */
struct update_subgroup *subgroup;
/* Threading that makes the adj part of subgroup's adj queue */
TAILQ_ENTRY(bgp_adj_out) subgrp_adj_train;
/* Prefix information. */
struct bgp_dest *dest;
uint32_t addpath_tx_id;
/* Attribute hash */
uint32_t attr_hash;
/* Advertised attribute. */
struct attr *attr;
/* VPN label information */
struct bgp_labels *labels;
/* Advertisement information. */
struct bgp_advertise *adv;
};
RB_HEAD(bgp_adj_out_rb, bgp_adj_out);
RB_PROTOTYPE(bgp_adj_out_rb, bgp_adj_out, adj_entry,
bgp_adj_out_compare);
/* BGP adjacency in. */
struct bgp_adj_in {
/* Linked list pointer. */
struct bgp_adj_in *next;
struct bgp_adj_in *prev;
/* Received peer. */
struct peer *peer;
/* Received attribute. */
struct attr *attr;
/* VPN label information */
struct bgp_labels *labels;
/* timestamp (monotime) */
time_t uptime;
/* Addpath identifier */
uint32_t addpath_rx_id;
};
/* BGP advertisement list. */
struct bgp_synchronize {
struct bgp_adv_fifo_head update;
struct bgp_adv_fifo_head withdraw;
};
/* BGP adjacency linked list. */
#define BGP_PATH_INFO_ADD(N, A, TYPE) \
do { \
(A)->prev = NULL; \
(A)->next = (N)->TYPE; \
if ((N)->TYPE) \
(N)->TYPE->prev = (A); \
(N)->TYPE = (A); \
} while (0)
#define BGP_PATH_INFO_DEL(N, A, TYPE) \
do { \
if ((A)->next) \
(A)->next->prev = (A)->prev; \
if ((A)->prev) \
(A)->prev->next = (A)->next; \
else \
(N)->TYPE = (A)->next; \
} while (0)
#define BGP_ADJ_IN_ADD(N, A) BGP_PATH_INFO_ADD(N, A, adj_in)
#define BGP_ADJ_IN_DEL(N, A) BGP_PATH_INFO_DEL(N, A, adj_in)
/* Prototypes. */
extern bool bgp_adj_out_lookup(struct peer *peer, struct bgp_dest *dest,
uint32_t addpath_tx_id);
extern void bgp_adj_in_set(struct bgp_dest *dest, struct peer *peer,
struct attr *attr, uint32_t addpath_id,
struct bgp_labels *labels);
extern bool bgp_adj_in_unset(struct bgp_dest **dest, struct peer *peer,
uint32_t addpath_id);
extern void bgp_adj_in_remove(struct bgp_dest **dest, struct bgp_adj_in *bai);
extern unsigned int bgp_advertise_attr_hash_key(const void *p);
extern bool bgp_advertise_attr_hash_cmp(const void *p1, const void *p2);
extern void bgp_advertise_add(struct bgp_advertise_attr *baa,
struct bgp_advertise *adv);
extern struct bgp_advertise *bgp_advertise_new(void);
extern void bgp_advertise_free(struct bgp_advertise *adv);
extern struct bgp_advertise_attr *bgp_advertise_attr_intern(struct hash *hash,
struct attr *attr);
extern struct bgp_advertise_attr *bgp_advertise_attr_new(void);
extern void bgp_advertise_delete(struct bgp_advertise_attr *baa,
struct bgp_advertise *adv);
extern void bgp_advertise_attr_unintern(struct hash *hash,
struct bgp_advertise_attr *baa);
extern void bgp_advertise_attr_free(struct bgp_advertise_attr *baa);
#endif /* _QUAGGA_BGP_ADVERTISE_H */

2481
bgpd/bgp_aspath.c Normal file

File diff suppressed because it is too large Load diff

171
bgpd/bgp_aspath.h Normal file
View file

@ -0,0 +1,171 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* AS path related definitions.
* Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
*/
#ifndef _QUAGGA_BGP_ASPATH_H
#define _QUAGGA_BGP_ASPATH_H
#include "lib/json.h"
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_filter.h"
#include <typesafe.h>
/* AS path segment type. */
#define AS_SET 1
#define AS_SEQUENCE 2
#define AS_CONFED_SEQUENCE 3
#define AS_CONFED_SET 4
/* Private AS range defined in RFC2270. */
#define BGP_PRIVATE_AS_MIN 64512U
#define BGP_PRIVATE_AS_MAX UINT16_MAX
/* Private 4 byte AS range defined in RFC6996. */
#define BGP_PRIVATE_AS4_MIN 4200000000U
#define BGP_PRIVATE_AS4_MAX 4294967294U
/* we leave BGP_AS_MAX as the 16bit AS MAX number. */
#define BGP_AS_ZERO 0
#define BGP_AS_MAX UINT16_MAX
#define BGP_AS4_MAX 4294967295U
/* Transition 16Bit AS as defined by IANA */
#define BGP_AS_TRANS 23456U
#define BGP_AS_IS_PRIVATE(ASN) \
(((ASN) >= BGP_PRIVATE_AS_MIN && (ASN) <= BGP_PRIVATE_AS_MAX) \
|| ((ASN) >= BGP_PRIVATE_AS4_MIN && (ASN) <= BGP_PRIVATE_AS4_MAX))
/* AS_PATH segment data in abstracted form, no limit is placed on length */
struct assegment {
struct assegment *next;
as_t *as;
unsigned short length;
uint8_t type;
};
/* AS path may be include some AsSegments. */
struct aspath {
/* Reference count to this aspath. */
unsigned long refcnt;
/* segment data */
struct assegment *segments;
/* AS path as a json object */
json_object *json;
/* String expression of AS path. This string is used by vty output
and AS path regular expression match. */
char *str;
unsigned short str_len;
/* AS notation used by string expression of AS path */
enum asnotation_mode asnotation;
};
#define ASPATH_STR_DEFAULT_LEN 32
/* `set as-path exclude ASn' */
struct aspath_exclude {
struct as_list_list_item exclude_list;
struct aspath *aspath;
bool exclude_all;
char *exclude_aspath_acl_name;
struct as_list *exclude_aspath_acl;
};
DECLARE_DLIST(as_list_list, struct aspath_exclude, exclude_list);
/* Prototypes. */
extern void aspath_init(void);
extern void aspath_finish(void);
extern struct aspath *aspath_parse(struct stream *s, size_t length,
int use32bit,
enum asnotation_mode asnotation);
extern struct aspath *aspath_dup(struct aspath *aspath);
extern struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2);
extern struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2);
extern void as_exclude_set_orphan(struct aspath_exclude *ase);
extern void as_exclude_remove_orphan(struct aspath_exclude *ase);
extern struct aspath_exclude *as_exclude_lookup_orphan(const char *acl_name);
extern struct aspath *aspath_filter_exclude(struct aspath *source,
struct aspath *exclude_list);
extern struct aspath *aspath_filter_exclude_all(struct aspath *source);
extern struct aspath *aspath_filter_exclude_acl(struct aspath *source,
struct as_list *acl_list);
extern struct aspath *aspath_add_seq_n(struct aspath *aspath, as_t asno,
unsigned num);
extern struct aspath *aspath_add_seq(struct aspath *aspath, as_t asno);
extern struct aspath *aspath_add_confed_seq(struct aspath *aspath, as_t asno);
extern bool aspath_cmp(const void *as1, const void *as2);
extern bool aspath_cmp_left(const struct aspath *aspath1,
const struct aspath *aspath2);
extern bool aspath_cmp_left_confed(const struct aspath *as1,
const struct aspath *as2);
extern struct aspath *aspath_delete_confed_seq(struct aspath *aspath);
extern struct aspath *aspath_empty(enum asnotation_mode asnotation);
extern struct aspath *aspath_empty_get(void);
extern struct aspath *aspath_str2aspath(const char *str,
enum asnotation_mode asnotation);
extern void aspath_str_update(struct aspath *as, bool make_json);
extern void aspath_free(struct aspath *aspath);
extern struct aspath *aspath_intern(struct aspath *aspath);
extern void aspath_unintern(struct aspath **aspath);
extern const char *aspath_print(struct aspath *aspath);
extern void aspath_print_vty(struct vty *vty, struct aspath *aspath);
extern void aspath_print_all_vty(struct vty *vty);
extern unsigned int aspath_key_make(const void *p);
extern unsigned int aspath_get_first_as(struct aspath *aspath);
extern unsigned int aspath_get_last_as(struct aspath *aspath);
extern int aspath_loop_check(struct aspath *aspath, as_t asno);
extern int aspath_loop_check_confed(struct aspath *aspath, as_t asno);
extern bool aspath_private_as_check(struct aspath *aspath);
extern struct aspath *aspath_replace_regex_asn(struct aspath *aspath,
struct as_list *acl_list,
as_t our_asn);
extern struct aspath *aspath_replace_specific_asn(struct aspath *aspath,
as_t target_asn,
as_t our_asn);
extern struct aspath *aspath_replace_all_asn(struct aspath *aspath,
as_t our_asn);
extern struct aspath *aspath_replace_private_asns(struct aspath *aspath,
as_t asn, as_t peer_asn);
extern struct aspath *aspath_remove_private_asns(struct aspath *aspath,
as_t peer_asn);
extern bool aspath_firstas_check(struct aspath *aspath, as_t asno);
extern bool aspath_confed_check(struct aspath *aspath);
extern bool aspath_left_confed_check(struct aspath *aspath);
extern unsigned long aspath_count(void);
extern unsigned int aspath_count_hops(const struct aspath *aspath);
extern bool aspath_check_as_sets(struct aspath *aspath);
extern bool aspath_check_as_zero(struct aspath *aspath);
extern unsigned int aspath_count_confeds(struct aspath *aspath);
extern unsigned int aspath_size(struct aspath *aspath);
extern as_t aspath_highest(struct aspath *aspath);
extern as_t aspath_leftmost(struct aspath *aspath);
extern size_t aspath_put(struct stream *s, struct aspath *aspath, int use32bit);
extern struct aspath *aspath_reconcile_as4(struct aspath *aspath,
struct aspath *as4path);
extern bool aspath_has_as4(struct aspath *aspath);
/* For SNMP BGP4PATHATTRASPATHSEGMENT, might be useful for debug */
extern uint8_t *aspath_snmp_pathseg(struct aspath *aspath, size_t *varlen);
extern void bgp_compute_aggregate_aspath(struct bgp_aggregate *aggregate,
struct aspath *aspath);
extern void bgp_compute_aggregate_aspath_hash(struct bgp_aggregate *aggregate,
struct aspath *aspath);
extern void bgp_compute_aggregate_aspath_val(struct bgp_aggregate *aggregate);
extern void bgp_remove_aspath_from_aggregate(struct bgp_aggregate *aggregate,
struct aspath *aspath);
extern void bgp_remove_aspath_from_aggregate_hash(
struct bgp_aggregate *aggregate,
struct aspath *aspath);
extern void bgp_aggr_aspath_remove(void *arg);
#endif /* _QUAGGA_BGP_ASPATH_H */

5383
bgpd/bgp_attr.c Normal file

File diff suppressed because it is too large Load diff

652
bgpd/bgp_attr.h Normal file
View file

@ -0,0 +1,652 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* BGP attributes.
* Copyright (C) 1996, 97, 98 Kunihiro Ishiguro
*/
#ifndef _QUAGGA_BGP_ATTR_H
#define _QUAGGA_BGP_ATTR_H
#include "mpls.h"
#include "bgp_attr_evpn.h"
#include "bgpd/bgp_encap_types.h"
#include "srte.h"
/* Simple bit mapping. */
#define BITMAP_NBBY 8
#define SET_BITMAP(MAP, NUM) \
SET_FLAG(MAP[(NUM) / BITMAP_NBBY], 1 << ((NUM) % BITMAP_NBBY))
#define CHECK_BITMAP(MAP, NUM) \
CHECK_FLAG(MAP[(NUM) / BITMAP_NBBY], 1 << ((NUM) % BITMAP_NBBY))
#define BGP_MED_MAX UINT32_MAX
/* BGP Attribute type range. */
#define BGP_ATTR_TYPE_RANGE 256
#define BGP_ATTR_BITMAP_SIZE (BGP_ATTR_TYPE_RANGE / BITMAP_NBBY)
/* BGP Attribute flags. */
#define BGP_ATTR_FLAG_OPTIONAL 0x80 /* Attribute is optional. */
#define BGP_ATTR_FLAG_TRANS 0x40 /* Attribute is transitive. */
#define BGP_ATTR_FLAG_PARTIAL 0x20 /* Attribute is partial. */
#define BGP_ATTR_FLAG_EXTLEN 0x10 /* Extended length flag. */
/* BGP attribute header must bigger than 2. */
#define BGP_ATTR_MIN_LEN 3 /* Attribute flag, type length. */
#define BGP_ATTR_DEFAULT_WEIGHT 32768
/* Valid lengths for mp_nexthop_len */
#define BGP_ATTR_NHLEN_IPV4 IPV4_MAX_BYTELEN
#define BGP_ATTR_NHLEN_VPNV4 8+IPV4_MAX_BYTELEN
#define BGP_ATTR_NHLEN_IPV6_GLOBAL IPV6_MAX_BYTELEN
#define BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL (IPV6_MAX_BYTELEN * 2)
#define BGP_ATTR_NHLEN_VPNV6_GLOBAL 8+IPV6_MAX_BYTELEN
#define BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL ((8+IPV6_MAX_BYTELEN) * 2)
/* Prefix SID types */
#define BGP_PREFIX_SID_LABEL_INDEX 1
#define BGP_PREFIX_SID_IPV6 2
#define BGP_PREFIX_SID_ORIGINATOR_SRGB 3
#define BGP_PREFIX_SID_VPN_SID 4
#define BGP_PREFIX_SID_SRV6_L3_SERVICE 5
#define BGP_PREFIX_SID_SRV6_L2_SERVICE 6
#define BGP_PREFIX_SID_LABEL_INDEX_LENGTH 7
#define BGP_PREFIX_SID_IPV6_LENGTH 19
#define BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH 6
#define BGP_PREFIX_SID_VPN_SID_LENGTH 19
/* SRv6 Service Sub-TLV types */
#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO 1
#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO_LENGTH 21
/* SRv6 Service Data Sub-Sub-TLV types */
#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE 1
#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE_LENGTH 6
#define BGP_ATTR_NH_AFI(afi, attr) \
((afi != AFI_L2VPN) ? afi : \
((attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV4) ? AFI_IP : AFI_IP6))
/* PMSI tunnel types (RFC 6514) */
struct bgp_attr_encap_subtlv {
struct bgp_attr_encap_subtlv *next; /* for chaining */
/* Reference count of this attribute. */
unsigned long refcnt;
uint16_t type;
uint16_t length;
uint8_t value[0]; /* will be extended */
};
#ifdef ENABLE_BGP_VNC
/*
* old rfp<->rfapi representation
*/
struct bgp_tea_options {
struct bgp_tea_options *next;
uint8_t options_count;
uint16_t options_length; /* each TLV may be 256 in length */
uint8_t type;
uint8_t length;
void *value; /* pointer to data */
};
#endif
enum pta_type {
PMSI_TNLTYPE_NO_INFO = 0,
PMSI_TNLTYPE_RSVP_TE_P2MP,
PMSI_TNLTYPE_MLDP_P2MP,
PMSI_TNLTYPE_PIM_SSM,
PMSI_TNLTYPE_PIM_SM,
PMSI_TNLTYPE_PIM_BIDIR,
PMSI_TNLTYPE_INGR_REPL,
PMSI_TNLTYPE_MLDP_MP2MP,
PMSI_TNLTYPE_MAX = PMSI_TNLTYPE_MLDP_MP2MP
};
/*
* Prefix-SID type-4
* SRv6-VPN-SID-TLV
* draft-dawra-idr-srv6-vpn-04
*/
struct bgp_attr_srv6_vpn {
unsigned long refcnt;
uint8_t sid_flags;
struct in6_addr sid;
};
/*
* Prefix-SID type-5
* SRv6-L3VPN-Service-TLV
* draft-dawra-idr-srv6-vpn-05
*/
struct bgp_attr_srv6_l3vpn {
unsigned long refcnt;
uint8_t sid_flags;
uint16_t endpoint_behavior;
struct in6_addr sid;
uint8_t loc_block_len;
uint8_t loc_node_len;
uint8_t func_len;
uint8_t arg_len;
uint8_t transposition_len;
uint8_t transposition_offset;
};
/* BGP core attribute structure. */
struct attr {
/* AS Path structure */
struct aspath *aspath;
/* Community structure */
struct community *community;
/* Reference count of this attribute. */
unsigned long refcnt;
/* Flag of attribute is set or not. */
uint64_t flag;
/* Apart from in6_addr, the remaining static attributes */
struct in_addr nexthop;
uint32_t med;
uint32_t local_pref;
ifindex_t nh_ifindex;
uint8_t nh_flags;
#define BGP_ATTR_NH_VALID 0x01
#define BGP_ATTR_NH_IF_OPERSTATE 0x02
#define BGP_ATTR_NH_MP_PREFER_GLOBAL 0x04 /* MP Nexthop preference */
/* Path origin attribute */
uint8_t origin;
/* ES info */
uint8_t es_flags;
/* Path is not "locally-active" on the advertising VTEP. This is
* translated into an ARP-ND ECOM.
*/
#define ATTR_ES_PROXY_ADVERT (1 << 0)
/* Destination ES is present locally. This flag is set on local
* paths and sync paths
*/
#define ATTR_ES_IS_LOCAL (1 << 1)
/* There are one or more non-best paths from ES peers. Note that
* this flag is only set on the local MAC-IP paths in the VNI
* route table (not set in the global routing table). And only
* non-proxy advertisements from an ES peer can result in this
* flag being set.
*/
#define ATTR_ES_PEER_ACTIVE (1 << 2)
/* There are one or more non-best proxy paths from ES peers */
#define ATTR_ES_PEER_PROXY (1 << 3)
/* An ES peer has router bit set - only applicable if
* ATTR_ES_PEER_ACTIVE is set
*/
#define ATTR_ES_PEER_ROUTER (1 << 4)
/* These two flags are only set on L3 routes installed in a
* VRF as a result of EVPN MAC-IP route
* XXX - while splitting up per-family attrs these need to be
* classified as non-EVPN
*/
#define ATTR_ES_L3_NHG_USE (1 << 5)
#define ATTR_ES_L3_NHG_ACTIVE (1 << 6)
#define ATTR_ES_L3_NHG (ATTR_ES_L3_NHG_USE | ATTR_ES_L3_NHG_ACTIVE)
/* NA router flag (R-bit) support in EVPN */
uint8_t router_flag;
/* Distance as applied by Route map */
uint8_t distance;
/* EVPN DF preference for DF election on local ESs */
uint8_t df_alg;
uint16_t df_pref;
/* PMSI tunnel type (RFC 6514). */
enum pta_type pmsi_tnl_type;
/* has the route-map changed any attribute?
Used on the peer outbound side. */
uint32_t rmap_change_flags;
/* Multi-Protocol Nexthop, AFI IPv6 */
struct in6_addr mp_nexthop_global;
struct in6_addr mp_nexthop_local;
/* ifIndex corresponding to mp_nexthop_local. */
ifindex_t nh_lla_ifindex;
/* MPLS label */
mpls_label_t label;
/* Extended Communities attribute. */
struct ecommunity *ecommunity;
/* Extended Communities attribute. */
struct ecommunity *ipv6_ecommunity;
/* Large Communities attribute. */
struct lcommunity *lcommunity;
/* Route-Reflector Cluster attribute */
struct cluster_list *cluster1;
/* Unknown transitive attribute. */
struct transit *transit;
struct in_addr mp_nexthop_global_in;
/* Aggregator Router ID attribute */
struct in_addr aggregator_addr;
/* Route Reflector Originator attribute */
struct in_addr originator_id;
/* Local weight, not actually an attribute */
uint32_t weight;
/* Aggregator ASN */
as_t aggregator_as;
/* MP Nexthop length */
uint8_t mp_nexthop_len;
/* Static MAC for EVPN */
uint8_t sticky;
/* Flag for default gateway extended community in EVPN */
uint8_t default_gw;
/* route tag */
route_tag_t tag;
/* Label index */
uint32_t label_index;
/* SRv6 VPN SID */
struct bgp_attr_srv6_vpn *srv6_vpn;
/* SRv6 L3VPN SID */
struct bgp_attr_srv6_l3vpn *srv6_l3vpn;
struct bgp_attr_encap_subtlv *encap_subtlvs; /* rfc5512 */
#ifdef ENABLE_BGP_VNC
struct bgp_attr_encap_subtlv *vnc_subtlvs; /* VNC-specific */
#endif
/* EVPN */
struct bgp_route_evpn evpn_overlay;
/* EVPN MAC Mobility sequence number, if any. */
uint32_t mm_seqnum;
/* highest MM sequence number rxed in a MAC-IP route from an
* ES peer (this includes both proxy and non-proxy MAC-IP
* advertisements from ES peers).
* This is only applicable to local paths in the VNI routing
* table and derived from other imported/non-best paths.
*/
uint32_t mm_sync_seqnum;
/* EVPN local router-mac */
struct ethaddr rmac;
uint16_t encap_tunneltype;
/* rmap set table */
uint32_t rmap_table_id;
/* Link bandwidth value, if any. */
uint64_t link_bw;
/* EVPN ES */
esi_t esi;
/* SR-TE Color */
uint32_t srte_color;
/* Nexthop type */
enum nexthop_types_t nh_type;
/* If NEXTHOP_TYPE_BLACKHOLE, then blackhole type */
enum blackhole_type bh_type;
/* OTC value if set */
uint32_t otc;
/* AIGP Metric */
uint64_t aigp_metric;
};
/* rmap_change_flags definition */
#define BATTR_RMAP_IPV4_NHOP_CHANGED (1 << 0)
#define BATTR_RMAP_NEXTHOP_PEER_ADDRESS (1 << 1)
#define BATTR_REFLECTED (1 << 2)
#define BATTR_RMAP_NEXTHOP_UNCHANGED (1 << 3)
#define BATTR_RMAP_IPV6_GLOBAL_NHOP_CHANGED (1 << 4)
#define BATTR_RMAP_IPV6_LL_NHOP_CHANGED (1 << 5)
#define BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED (1 << 6)
#define BATTR_RMAP_LINK_BW_SET (1 << 7)
#define BATTR_RMAP_L3VPN_ACCEPT_GRE (1 << 8)
#define BATTR_RMAP_VPNV4_NHOP_CHANGED (1 << 9)
#define BATTR_RMAP_VPNV6_GLOBAL_NHOP_CHANGED (1 << 10)
/* Router Reflector related structure. */
struct cluster_list {
unsigned long refcnt;
int length;
struct in_addr *list;
};
/* Unknown transit attribute. */
struct transit {
unsigned long refcnt;
int length;
uint8_t *val;
};
/* "(void) 0" will generate a compiler error. this is a safety check to
* ensure we're not using a value that exceeds the bit size of attr->flag. */
#define ATTR_FLAG_BIT(X) \
__builtin_choose_expr((X) >= 1 && (X) <= 64, 1ULL << ((X)-1), (void)0)
#define BGP_CLUSTER_LIST_LENGTH(attr) \
(((attr)->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)) \
? bgp_attr_get_cluster((attr))->length \
: 0)
enum bgp_attr_parse_ret {
BGP_ATTR_PARSE_PROCEED = 0,
BGP_ATTR_PARSE_ERROR = -1,
BGP_ATTR_PARSE_WITHDRAW = -2,
/* only used internally, send notify + convert to BGP_ATTR_PARSE_ERROR
*/
BGP_ATTR_PARSE_ERROR_NOTIFYPLS = -3,
BGP_ATTR_PARSE_MISSING_MANDATORY = -4,
};
struct bpacket_attr_vec_arr;
/* Prototypes. */
extern void bgp_attr_init(void);
extern void bgp_attr_finish(void);
extern enum bgp_attr_parse_ret
bgp_attr_parse(struct peer *peer, struct attr *attr, bgp_size_t size,
struct bgp_nlri *mp_update, struct bgp_nlri *mp_withdraw);
extern struct attr *bgp_attr_intern(struct attr *attr);
extern void bgp_attr_unintern_sub(struct attr *attr);
extern void bgp_attr_unintern(struct attr **pattr);
extern void bgp_attr_flush(struct attr *attr);
extern struct attr *bgp_attr_default_set(struct attr *attr, struct bgp *bgp,
uint8_t origin);
extern struct attr *bgp_attr_aggregate_intern(
struct bgp *bgp, uint8_t origin, struct aspath *aspath,
struct community *community, struct ecommunity *ecommunity,
struct lcommunity *lcommunity, struct bgp_aggregate *aggregate,
uint8_t atomic_aggregate, const struct prefix *p);
extern bgp_size_t bgp_packet_attribute(
struct bgp *bgp, struct peer *peer, struct stream *s, struct attr *attr,
struct bpacket_attr_vec_arr *vecarr, struct prefix *p, afi_t afi,
safi_t safi, struct peer *from, struct prefix_rd *prd,
mpls_label_t *label, uint8_t num_labels, bool addpath_capable,
uint32_t addpath_tx_id, struct bgp_path_info *bpi);
extern void bgp_dump_routes_attr(struct stream *s, struct bgp_path_info *bpi,
const struct prefix *p);
extern bool attrhash_cmp(const void *arg1, const void *arg2);
extern unsigned int attrhash_key_make(const void *p);
extern void attr_show_all(struct vty *vty);
extern unsigned long int attr_count(void);
extern unsigned long int attr_unknown_count(void);
extern void bgp_path_attribute_discard_vty(struct vty *vty, struct peer *peer,
const char *discard_attrs, bool set);
extern void bgp_path_attribute_withdraw_vty(struct vty *vty, struct peer *peer,
const char *withdraw_attrs,
bool set);
extern enum bgp_attr_parse_ret bgp_attr_ignore(struct peer *peer, uint8_t type);
/* Cluster list prototypes. */
extern bool cluster_loop_check(struct cluster_list *cluster,
struct in_addr originator);
/* Below exported for unit-test purposes only */
struct bgp_attr_parser_args {
struct peer *peer;
bgp_size_t length; /* attribute data length; */
bgp_size_t total; /* total length, inc header */
struct attr *attr;
uint8_t type;
uint8_t flags;
uint8_t *startp;
};
extern int bgp_mp_reach_parse(struct bgp_attr_parser_args *args,
struct bgp_nlri *mp_update);
extern int bgp_mp_unreach_parse(struct bgp_attr_parser_args *args,
struct bgp_nlri *mp_withdraw);
extern enum bgp_attr_parse_ret
bgp_attr_prefix_sid(struct bgp_attr_parser_args *args);
extern struct bgp_attr_encap_subtlv *
encap_tlv_dup(struct bgp_attr_encap_subtlv *orig);
extern void bgp_attr_flush_encap(struct attr *attr);
extern void bgp_attr_extcom_tunnel_type(struct attr *attr,
bgp_encap_types *tunnel_type);
/**
* Set of functions to encode MP_REACH_NLRI and MP_UNREACH_NLRI attributes.
* Typical call sequence is to call _start(), followed by multiple _prefix(),
* one for each NLRI that needs to be encoded into the UPDATE message, and
* finally the _end() function.
*/
extern size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer,
afi_t afi, safi_t safi,
struct bpacket_attr_vec_arr *vecarr,
struct attr *attr);
extern void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi,
const struct prefix *p,
const struct prefix_rd *prd,
mpls_label_t *label, uint8_t num_labels,
bool addpath_capable,
uint32_t addpath_tx_id, struct attr *);
extern size_t bgp_packet_mpattr_prefix_size(afi_t afi, safi_t safi,
const struct prefix *p);
extern void bgp_packet_mpattr_end(struct stream *s, size_t sizep);
extern size_t bgp_packet_mpunreach_start(struct stream *s, afi_t afi,
safi_t safi);
extern void bgp_packet_mpunreach_prefix(
struct stream *s, const struct prefix *p, afi_t afi, safi_t safi,
const struct prefix_rd *prd, mpls_label_t *label, uint8_t num_labels,
bool addpath_capable, uint32_t addpath_tx_id, struct attr *attr);
extern void bgp_packet_mpunreach_end(struct stream *s, size_t attrlen_pnt);
extern enum bgp_attr_parse_ret bgp_attr_nexthop_valid(struct peer *peer,
struct attr *attr);
extern uint32_t bgp_attr_get_color(struct attr *attr);
static inline bool bgp_rmap_nhop_changed(uint32_t out_rmap_flags,
uint32_t in_rmap_flags)
{
return ((CHECK_FLAG(out_rmap_flags, BATTR_RMAP_NEXTHOP_PEER_ADDRESS) ||
CHECK_FLAG(out_rmap_flags, BATTR_RMAP_NEXTHOP_UNCHANGED) ||
CHECK_FLAG(out_rmap_flags, BATTR_RMAP_IPV4_NHOP_CHANGED) ||
CHECK_FLAG(out_rmap_flags, BATTR_RMAP_VPNV4_NHOP_CHANGED) ||
CHECK_FLAG(out_rmap_flags,
BATTR_RMAP_VPNV6_GLOBAL_NHOP_CHANGED) ||
CHECK_FLAG(out_rmap_flags,
BATTR_RMAP_IPV6_GLOBAL_NHOP_CHANGED) ||
CHECK_FLAG(out_rmap_flags,
BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED) ||
CHECK_FLAG(out_rmap_flags, BATTR_RMAP_IPV6_LL_NHOP_CHANGED) ||
CHECK_FLAG(in_rmap_flags, BATTR_RMAP_NEXTHOP_UNCHANGED))
? true
: false);
}
static inline uint32_t mac_mobility_seqnum(struct attr *attr)
{
return (attr) ? attr->mm_seqnum : 0;
}
static inline enum pta_type bgp_attr_get_pmsi_tnl_type(struct attr *attr)
{
return attr->pmsi_tnl_type;
}
static inline void bgp_attr_set_pmsi_tnl_type(struct attr *attr,
enum pta_type pmsi_tnl_type)
{
attr->pmsi_tnl_type = pmsi_tnl_type;
}
static inline struct ecommunity *
bgp_attr_get_ecommunity(const struct attr *attr)
{
return attr->ecommunity;
}
static inline void bgp_attr_set_ecommunity(struct attr *attr,
struct ecommunity *ecomm)
{
attr->ecommunity = ecomm;
if (ecomm)
SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES));
else
UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES));
}
static inline struct lcommunity *
bgp_attr_get_lcommunity(const struct attr *attr)
{
return attr->lcommunity;
}
static inline void bgp_attr_set_lcommunity(struct attr *attr,
struct lcommunity *lcomm)
{
attr->lcommunity = lcomm;
if (lcomm)
SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES));
else
UNSET_FLAG(attr->flag,
ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES));
}
static inline struct community *bgp_attr_get_community(const struct attr *attr)
{
return attr->community;
}
static inline void bgp_attr_set_community(struct attr *attr,
struct community *comm)
{
attr->community = comm;
if (comm)
SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES));
else
UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES));
}
static inline struct ecommunity *
bgp_attr_get_ipv6_ecommunity(const struct attr *attr)
{
return attr->ipv6_ecommunity;
}
static inline void bgp_attr_set_ipv6_ecommunity(struct attr *attr,
struct ecommunity *ipv6_ecomm)
{
attr->ipv6_ecommunity = ipv6_ecomm;
if (ipv6_ecomm)
SET_FLAG(attr->flag,
ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES));
else
UNSET_FLAG(attr->flag,
ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES));
}
static inline struct transit *bgp_attr_get_transit(const struct attr *attr)
{
return attr->transit;
}
static inline void bgp_attr_set_transit(struct attr *attr,
struct transit *transit)
{
attr->transit = transit;
}
static inline uint64_t bgp_attr_get_aigp_metric(const struct attr *attr)
{
return attr->aigp_metric;
}
static inline void bgp_attr_set_aigp_metric(struct attr *attr, uint64_t aigp)
{
attr->aigp_metric = aigp;
if (aigp)
SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AIGP));
}
static inline struct cluster_list *bgp_attr_get_cluster(const struct attr *attr)
{
return attr->cluster1;
}
static inline void bgp_attr_set_cluster(struct attr *attr,
struct cluster_list *cl)
{
attr->cluster1 = cl;
if (cl)
SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST));
else
UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST));
}
static inline const struct bgp_route_evpn *
bgp_attr_get_evpn_overlay(const struct attr *attr)
{
return &attr->evpn_overlay;
}
static inline void bgp_attr_set_evpn_overlay(struct attr *attr,
struct bgp_route_evpn *eo)
{
memcpy(&attr->evpn_overlay, eo, sizeof(struct bgp_route_evpn));
}
static inline struct bgp_attr_encap_subtlv *
bgp_attr_get_vnc_subtlvs(const struct attr *attr)
{
#ifdef ENABLE_BGP_VNC
return attr->vnc_subtlvs;
#else
return NULL;
#endif
}
static inline void
bgp_attr_set_vnc_subtlvs(struct attr *attr,
struct bgp_attr_encap_subtlv *vnc_subtlvs)
{
#ifdef ENABLE_BGP_VNC
attr->vnc_subtlvs = vnc_subtlvs;
#endif
}
extern bool route_matches_soo(struct bgp_path_info *pi, struct ecommunity *soo);
#endif /* _QUAGGA_BGP_ATTR_H */

302
bgpd/bgp_attr_evpn.c Normal file
View file

@ -0,0 +1,302 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* Ethernet-VPN Attribute handling file
* Copyright (C) 2016 6WIND
*/
#include <zebra.h>
#include "command.h"
#include "filter.h"
#include "prefix.h"
#include "log.h"
#include "memory.h"
#include "stream.h"
#include "vxlan.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_attr_evpn.h"
#include "bgpd/bgp_ecommunity.h"
#include "bgpd/bgp_evpn.h"
#include "bgpd/bgp_evpn_private.h"
bool bgp_route_evpn_same(const struct bgp_route_evpn *e1,
const struct bgp_route_evpn *e2)
{
return (e1->type == e2->type &&
!memcmp(&(e1->eth_s_id), &(e2->eth_s_id), sizeof(esi_t)) &&
!ipaddr_cmp(&(e1->gw_ip), &(e2->gw_ip)));
}
void bgp_add_routermac_ecom(struct attr *attr, struct ethaddr *routermac)
{
struct ecommunity_val routermac_ecom;
struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr);
memset(&routermac_ecom, 0, sizeof(routermac_ecom));
routermac_ecom.val[0] = ECOMMUNITY_ENCODE_EVPN;
routermac_ecom.val[1] = ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC;
memcpy(&routermac_ecom.val[2], routermac->octet, ETH_ALEN);
if (!ecomm) {
bgp_attr_set_ecommunity(attr, ecommunity_new());
ecomm = bgp_attr_get_ecommunity(attr);
}
ecommunity_add_val(ecomm, &routermac_ecom, false, false);
ecommunity_str(ecomm);
}
/* converts to an esi
* returns 1 on success, 0 otherwise
* format accepted: AA:BB:CC:DD:EE:FF:GG:HH:II:JJ
* if id is null, check only is done
*/
bool str2esi(const char *str, esi_t *id)
{
unsigned int a[ESI_BYTES];
int i;
if (!str)
return false;
if (sscanf(str, "%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x", a + 0, a + 1,
a + 2, a + 3, a + 4, a + 5, a + 6, a + 7, a + 8, a + 9)
!= ESI_BYTES) {
/* error in incoming str length */
return false;
}
/* valid mac address */
if (!id)
return true;
for (i = 0; i < ESI_BYTES; ++i)
id->val[i] = a[i] & 0xff;
return true;
}
char *ecom_mac2str(char *ecom_mac)
{
char *en;
en = ecom_mac;
en += 2;
return prefix_mac2str((struct ethaddr *)en, NULL, 0);
}
/* Fetch router-mac from extended community */
bool bgp_attr_rmac(struct attr *attr, struct ethaddr *rmac)
{
uint32_t i = 0;
struct ecommunity *ecom;
ecom = bgp_attr_get_ecommunity(attr);
if (!ecom || !ecom->size)
return false;
/* If there is a router mac extended community, set RMAC in attr */
for (i = 0; i < ecom->size; i++) {
uint8_t *pnt = NULL;
uint8_t type = 0;
uint8_t sub_type = 0;
pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
type = *pnt++;
sub_type = *pnt++;
if (!(type == ECOMMUNITY_ENCODE_EVPN
&& sub_type == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC))
continue;
memcpy(rmac, pnt, ETH_ALEN);
return true;
}
return false;
}
/*
* return true if attr contains default gw extended community
*/
uint8_t bgp_attr_default_gw(struct attr *attr)
{
struct ecommunity *ecom;
uint32_t i;
ecom = bgp_attr_get_ecommunity(attr);
if (!ecom || !ecom->size)
return 0;
/* If there is a default gw extendd community return true otherwise
* return 0 */
for (i = 0; i < ecom->size; i++) {
uint8_t *pnt;
uint8_t type, sub_type;
pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
type = *pnt++;
sub_type = *pnt++;
if ((type == ECOMMUNITY_ENCODE_OPAQUE
&& sub_type == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW))
return 1;
}
return 0;
}
/*
* Fetch and return the DF preference and algorithm from
* DF election extended community, if present, else 0.
*/
uint16_t bgp_attr_df_pref_from_ec(struct attr *attr, uint8_t *alg)
{
struct ecommunity *ecom;
uint32_t i;
uint16_t df_pref = 0;
*alg = EVPN_MH_DF_ALG_SERVICE_CARVING;
ecom = bgp_attr_get_ecommunity(attr);
if (!ecom || !ecom->size)
return 0;
for (i = 0; i < ecom->size; i++) {
uint8_t *pnt;
uint8_t type, sub_type;
pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
type = *pnt++;
sub_type = *pnt++;
if (!(type == ECOMMUNITY_ENCODE_EVPN
&& sub_type == ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION))
continue;
*alg = (*pnt++) & ECOMMUNITY_EVPN_SUBTYPE_DF_ALG_BITS;
pnt += 3;
pnt = ptr_get_be16(pnt, &df_pref);
(void)pnt; /* consume value */
break;
}
return df_pref;
}
/*
* Fetch and return the sequence number from MAC Mobility extended
* community, if present, else 0.
*/
uint32_t bgp_attr_mac_mobility_seqnum(struct attr *attr, uint8_t *sticky)
{
struct ecommunity *ecom;
uint32_t i;
uint8_t flags = 0;
ecom = bgp_attr_get_ecommunity(attr);
if (!ecom || !ecom->size)
return 0;
/* If there is a MAC Mobility extended community, return its
* sequence number.
* TODO: RFC is silent on handling of multiple MAC mobility extended
* communities for the same route. We will bail out upon the first
* one.
*/
for (i = 0; i < ecom->size; i++) {
const uint8_t *pnt;
uint8_t type, sub_type;
uint32_t seq_num;
pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
type = *pnt++;
sub_type = *pnt++;
if (!(type == ECOMMUNITY_ENCODE_EVPN
&& sub_type == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY))
continue;
flags = *pnt++;
if (flags & ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY)
*sticky = 1;
else
*sticky = 0;
pnt++;
pnt = ptr_get_be32(pnt, &seq_num);
(void)pnt; /* consume value */
return seq_num;
}
return 0;
}
/*
* return true if attr contains router flag extended community
*/
void bgp_attr_evpn_na_flag(struct attr *attr,
uint8_t *router_flag, bool *proxy)
{
struct ecommunity *ecom;
uint32_t i;
uint8_t val;
ecom = bgp_attr_get_ecommunity(attr);
if (!ecom || !ecom->size)
return;
/* If there is a evpn na extendd community set router_flag */
for (i = 0; i < ecom->size; i++) {
uint8_t *pnt;
uint8_t type, sub_type;
pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
type = *pnt++;
sub_type = *pnt++;
if (type == ECOMMUNITY_ENCODE_EVPN &&
sub_type == ECOMMUNITY_EVPN_SUBTYPE_ND) {
val = *pnt++;
if (val & ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG)
*router_flag = 1;
if (val & ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG)
*proxy = true;
break;
}
}
}
/* dst prefix must be AF_INET or AF_INET6 prefix, to forge EVPN prefix */
extern int bgp_build_evpn_prefix(int evpn_type, uint32_t eth_tag,
struct prefix *dst)
{
struct evpn_addr *p_evpn_p;
struct prefix p2;
struct prefix *src = &p2;
if (!dst || dst->family == 0)
return -1;
/* store initial prefix in src */
prefix_copy(src, dst);
memset(dst, 0, sizeof(struct prefix));
p_evpn_p = &(dst->u.prefix_evpn);
dst->family = AF_EVPN;
p_evpn_p->route_type = evpn_type;
if (evpn_type == BGP_EVPN_IP_PREFIX_ROUTE) {
p_evpn_p->prefix_addr.eth_tag = eth_tag;
p_evpn_p->prefix_addr.ip_prefix_length = p2.prefixlen;
if (src->family == AF_INET) {
SET_IPADDR_V4(&p_evpn_p->prefix_addr.ip);
memcpy(&p_evpn_p->prefix_addr.ip.ipaddr_v4,
&src->u.prefix4,
sizeof(struct in_addr));
dst->prefixlen = (uint16_t)PREFIX_LEN_ROUTE_TYPE_5_IPV4;
} else {
SET_IPADDR_V6(&p_evpn_p->prefix_addr.ip);
memcpy(&p_evpn_p->prefix_addr.ip.ipaddr_v6,
&src->u.prefix6,
sizeof(struct in6_addr));
dst->prefixlen = (uint16_t)PREFIX_LEN_ROUTE_TYPE_5_IPV6;
}
} else
return -1;
return 0;
}

50
bgpd/bgp_attr_evpn.h Normal file
View file

@ -0,0 +1,50 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* E-VPN attribute handling structure file
* Copyright (C) 2016 6WIND
*/
#ifndef _QUAGGA_BGP_ATTR_EVPN_H
#define _QUAGGA_BGP_ATTR_EVPN_H
#define MAX_ET 0xffffffff
struct attr;
enum overlay_index_type {
OVERLAY_INDEX_TYPE_NONE,
OVERLAY_INDEX_GATEWAY_IP,
OVERLAY_INDEX_ESI,
OVERLAY_INDEX_MAC,
};
/*
* Structure to store ovrelay index for EVPN type-5 route
* This structure stores ESI and Gateway IP overlay index.
* MAC overlay index is stored in the RMAC attribute.
*/
struct bgp_route_evpn {
enum overlay_index_type type;
esi_t eth_s_id;
struct ipaddr gw_ip;
};
extern bool str2esi(const char *str, esi_t *id);
extern char *ecom_mac2str(char *ecom_mac);
extern void bgp_add_routermac_ecom(struct attr *attr,
struct ethaddr *routermac);
extern int bgp_build_evpn_prefix(int type, uint32_t eth_tag,
struct prefix *dst);
extern bool bgp_attr_rmac(struct attr *attr, struct ethaddr *rmac);
extern uint32_t bgp_attr_mac_mobility_seqnum(struct attr *attr,
uint8_t *sticky);
extern uint8_t bgp_attr_default_gw(struct attr *attr);
extern void bgp_attr_evpn_na_flag(struct attr *attr, uint8_t *router_flag,
bool *proxy);
extern uint16_t bgp_attr_df_pref_from_ec(struct attr *attr, uint8_t *alg);
extern bool bgp_route_evpn_same(const struct bgp_route_evpn *e1,
const struct bgp_route_evpn *e2);
#endif /* _QUAGGA_BGP_ATTR_EVPN_H */

630
bgpd/bgp_bfd.c Normal file
View file

@ -0,0 +1,630 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/**
* bgp_bfd.c: BGP BFD handling routines
*
* @copyright Copyright (C) 2015 Cumulus Networks, Inc.
*/
#include <zebra.h>
#include "command.h"
#include "linklist.h"
#include "memory.h"
#include "prefix.h"
#include "frrevent.h"
#include "buffer.h"
#include "stream.h"
#include "vrf.h"
#include "zclient.h"
#include "bfd.h"
#include "lib/json.h"
#include "filter.h"
#include "bgpd/bgpd.h"
#include "bgp_fsm.h"
#include "bgpd/bgp_bfd.h"
#include "bgpd/bgp_debug.h"
#include "bgpd/bgp_vty.h"
#include "bgpd/bgp_packet.h"
DEFINE_MTYPE_STATIC(BGPD, BFD_CONFIG, "BFD configuration data");
extern struct zclient *zclient;
static void bfd_session_status_update(struct bfd_session_params *bsp,
const struct bfd_session_status *bss,
void *arg)
{
struct peer *peer = arg;
if (BGP_DEBUG(bfd, BFD_LIB))
zlog_debug("%s: neighbor %s vrf %s(%u) bfd state %s -> %s",
__func__, peer->conf_if ? peer->conf_if : peer->host,
bfd_sess_vrf(bsp), bfd_sess_vrf_id(bsp),
bfd_get_status_str(bss->previous_state),
bfd_get_status_str(bss->state));
if (bss->state == BSS_DOWN && bss->previous_state == BSS_UP) {
if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)
&& bfd_sess_cbit(bsp) && !bss->remote_cbit) {
if (BGP_DEBUG(bfd, BFD_LIB))
zlog_debug(
"%s BFD DOWN message ignored in the process of graceful restart when C bit is cleared",
peer->host);
return;
}
peer->last_reset = PEER_DOWN_BFD_DOWN;
/* rfc9384 */
if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status))
bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_BFD_DOWN);
BGP_EVENT_ADD(peer->connection, BGP_Stop);
}
if (bss->state == BSS_UP && bss->previous_state != BSS_UP &&
!peer_established(peer->connection)) {
if (!BGP_PEER_START_SUPPRESSED(peer)) {
bgp_fsm_nht_update(peer->connection, peer, true);
BGP_EVENT_ADD(peer->connection, BGP_Start);
}
}
}
void bgp_peer_config_apply(struct peer *p, struct peer_group *pg)
{
struct listnode *n;
struct peer *pn;
struct peer *gconfig;
/* When called on a group, apply to all peers. */
if (CHECK_FLAG(p->sflags, PEER_STATUS_GROUP)) {
for (ALL_LIST_ELEMENTS_RO(p->group->peer, n, pn))
bgp_peer_config_apply(pn, pg);
return;
}
/* No group, just use current configuration. */
if (pg == NULL || pg->conf->bfd_config == NULL) {
bfd_sess_set_timers(p->bfd_config->session,
p->bfd_config->detection_multiplier,
p->bfd_config->min_rx,
p->bfd_config->min_tx);
bfd_sess_set_cbit(p->bfd_config->session, p->bfd_config->cbit);
bfd_sess_set_profile(p->bfd_config->session,
p->bfd_config->profile);
bfd_sess_install(p->bfd_config->session);
return;
}
/*
* Check if the group configuration was overwritten or apply group
* configuration.
*/
gconfig = pg->conf;
/*
* If using default control plane independent configuration,
* then prefer group's (e.g. it means it wasn't manually configured).
*/
if (!p->bfd_config->cbit)
bfd_sess_set_cbit(p->bfd_config->session,
gconfig->bfd_config->cbit);
else
bfd_sess_set_cbit(p->bfd_config->session, p->bfd_config->cbit);
/* If no profile was specified in peer, then use the group profile. */
if (p->bfd_config->profile[0] == 0)
bfd_sess_set_profile(p->bfd_config->session,
gconfig->bfd_config->profile);
else
bfd_sess_set_profile(p->bfd_config->session,
p->bfd_config->profile);
/* If no specific timers were configured, then use the group timers. */
if (p->bfd_config->detection_multiplier == BFD_DEF_DETECT_MULT
|| p->bfd_config->min_rx == BFD_DEF_MIN_RX
|| p->bfd_config->min_tx == BFD_DEF_MIN_TX)
bfd_sess_set_timers(p->bfd_config->session,
gconfig->bfd_config->detection_multiplier,
gconfig->bfd_config->min_rx,
gconfig->bfd_config->min_tx);
else
bfd_sess_set_timers(p->bfd_config->session,
p->bfd_config->detection_multiplier,
p->bfd_config->min_rx,
p->bfd_config->min_tx);
bfd_sess_install(p->bfd_config->session);
}
void bgp_peer_bfd_update_source(struct peer *p)
{
struct bfd_session_params *session = p->bfd_config->session;
const union sockunion *source;
bool changed = false;
int family;
union {
struct in_addr v4;
struct in6_addr v6;
} src, dst;
/* Nothing to do for groups. */
if (CHECK_FLAG(p->sflags, PEER_STATUS_GROUP))
return;
/* Figure out the correct source to use. */
if (CHECK_FLAG(p->flags, PEER_FLAG_UPDATE_SOURCE) && p->update_source)
source = p->update_source;
else
source = p->su_local;
/* Update peer's source/destination addresses. */
bfd_sess_addresses(session, &family, &src.v6, &dst.v6);
if (family == AF_INET) {
if ((source && source->sin.sin_addr.s_addr != src.v4.s_addr) ||
p->connection->su.sin.sin_addr.s_addr != dst.v4.s_addr) {
if (BGP_DEBUG(bfd, BFD_LIB))
zlog_debug("%s: address [%pI4->%pI4] to [%pI4->%pI4]",
__func__, &src.v4, &dst.v4,
source ? &source->sin.sin_addr
: &src.v4,
&p->connection->su.sin.sin_addr);
bfd_sess_set_ipv4_addrs(session,
source ? &source->sin.sin_addr
: NULL,
&p->connection->su.sin.sin_addr);
changed = true;
}
} else {
if ((source && memcmp(&source->sin6, &src.v6, sizeof(src.v6))) ||
memcmp(&p->connection->su.sin6, &dst.v6, sizeof(dst.v6))) {
if (BGP_DEBUG(bfd, BFD_LIB))
zlog_debug("%s: address [%pI6->%pI6] to [%pI6->%pI6]",
__func__, &src.v6, &dst.v6,
source ? &source->sin6.sin6_addr
: &src.v6,
&p->connection->su.sin6.sin6_addr);
bfd_sess_set_ipv6_addrs(session,
source ? &source->sin6.sin6_addr
: NULL,
&p->connection->su.sin6.sin6_addr);
changed = true;
}
}
/* Update interface. */
if (p->nexthop.ifp && bfd_sess_interface(session) == NULL) {
if (BGP_DEBUG(bfd, BFD_LIB))
zlog_debug("%s: interface none to %s", __func__,
p->nexthop.ifp->name);
bfd_sess_set_interface(session, p->nexthop.ifp->name);
changed = true;
}
/*
* Update TTL.
*
* Two cases:
* - We detected that the peer is a hop away from us (remove multi hop).
* (this happens when `p->shared_network` is set to `true`)
* - eBGP multi hop / TTL security changed.
*/
if (!PEER_IS_MULTIHOP(p) && bfd_sess_hop_count(session) > 1) {
if (BGP_DEBUG(bfd, BFD_LIB))
zlog_debug("%s: TTL %d to 1", __func__,
bfd_sess_hop_count(session));
bfd_sess_set_hop_count(session, 1);
changed = true;
}
if (PEER_IS_MULTIHOP(p) && p->ttl != bfd_sess_hop_count(session)) {
if (BGP_DEBUG(bfd, BFD_LIB))
zlog_debug("%s: TTL %d to %d", __func__,
bfd_sess_hop_count(session), p->ttl);
bfd_sess_set_hop_count(session, p->ttl);
changed = true;
}
/* Update VRF. */
if (bfd_sess_vrf_id(session) != p->bgp->vrf_id) {
if (BGP_DEBUG(bfd, BFD_LIB))
zlog_debug(
"%s: VRF %s(%d) to %s(%d)", __func__,
bfd_sess_vrf(session), bfd_sess_vrf_id(session),
vrf_id_to_name(p->bgp->vrf_id), p->bgp->vrf_id);
bfd_sess_set_vrf(session, p->bgp->vrf_id);
changed = true;
}
if (changed)
bfd_sess_install(session);
}
/**
* Reset BFD configuration data structure to its defaults settings.
*/
static void bgp_peer_bfd_reset(struct peer *p)
{
/* Set defaults. */
p->bfd_config->detection_multiplier = BFD_DEF_DETECT_MULT;
p->bfd_config->min_rx = BFD_DEF_MIN_RX;
p->bfd_config->min_tx = BFD_DEF_MIN_TX;
p->bfd_config->cbit = false;
p->bfd_config->profile[0] = 0;
}
void bgp_peer_configure_bfd(struct peer *p, bool manual)
{
/* Groups should not call this. */
assert(!CHECK_FLAG(p->sflags, PEER_STATUS_GROUP));
/* Already configured, skip it. */
if (p->bfd_config) {
/* If manually active update flag. */
if (!p->bfd_config->manual)
p->bfd_config->manual = manual;
return;
}
/* Allocate memory for configuration overrides. */
p->bfd_config = XCALLOC(MTYPE_BFD_CONFIG, sizeof(*p->bfd_config));
p->bfd_config->manual = manual;
/* Create new session and assign callback. */
p->bfd_config->session = bfd_sess_new(bfd_session_status_update, p);
bgp_peer_bfd_reset(p);
/* Configure session with basic BGP peer data. */
if (p->connection->su.sa.sa_family == AF_INET)
bfd_sess_set_ipv4_addrs(p->bfd_config->session,
p->su_local ? &p->su_local->sin.sin_addr
: NULL,
&p->connection->su.sin.sin_addr);
else
bfd_sess_set_ipv6_addrs(p->bfd_config->session,
p->su_local
? &p->su_local->sin6.sin6_addr
: NULL,
&p->connection->su.sin6.sin6_addr);
bfd_sess_set_vrf(p->bfd_config->session, p->bgp->vrf_id);
bfd_sess_set_hop_count(p->bfd_config->session,
PEER_IS_MULTIHOP(p) ? p->ttl : 1);
if (p->nexthop.ifp)
bfd_sess_set_interface(p->bfd_config->session,
p->nexthop.ifp->name);
}
static void bgp_peer_remove_bfd(struct peer *p)
{
/* Groups should not call this. */
assert(!CHECK_FLAG(p->sflags, PEER_STATUS_GROUP));
/*
* Peer configuration was removed, however we must check if there
* is still a group configuration to keep this running.
*/
if (p->group && p->group->conf->bfd_config) {
p->bfd_config->manual = false;
bgp_peer_bfd_reset(p);
bgp_peer_config_apply(p, p->group);
return;
}
if (p->bfd_config)
bfd_sess_free(&p->bfd_config->session);
XFREE(MTYPE_BFD_CONFIG, p->bfd_config);
}
static void bgp_group_configure_bfd(struct peer *p)
{
struct listnode *n;
struct peer *pn;
/* Peers should not call this. */
assert(CHECK_FLAG(p->sflags, PEER_STATUS_GROUP));
/* Already allocated: do nothing. */
if (p->bfd_config)
return;
p->bfd_config = XCALLOC(MTYPE_BFD_CONFIG, sizeof(*p->bfd_config));
/* Set defaults. */
p->bfd_config->detection_multiplier = BFD_DEF_DETECT_MULT;
p->bfd_config->min_rx = BFD_DEF_MIN_RX;
p->bfd_config->min_tx = BFD_DEF_MIN_TX;
for (ALL_LIST_ELEMENTS_RO(p->group->peer, n, pn))
bgp_peer_configure_bfd(pn, false);
}
static void bgp_group_remove_bfd(struct peer *p)
{
struct listnode *n;
struct peer *pn;
/* Peers should not call this. */
assert(CHECK_FLAG(p->sflags, PEER_STATUS_GROUP));
/* Already freed: do nothing. */
if (p->bfd_config == NULL)
return;
/* Free configuration and point to `NULL`. */
XFREE(MTYPE_BFD_CONFIG, p->bfd_config);
/* Now that it is `NULL` recalculate configuration for all peers. */
for (ALL_LIST_ELEMENTS_RO(p->group->peer, n, pn)) {
if (pn->bfd_config->manual)
bgp_peer_config_apply(pn, NULL);
else
bgp_peer_remove_bfd(pn);
}
}
void bgp_peer_remove_bfd_config(struct peer *p)
{
if (CHECK_FLAG(p->sflags, PEER_STATUS_GROUP))
bgp_group_remove_bfd(p);
else
bgp_peer_remove_bfd(p);
}
/*
* bgp_bfd_peer_config_write - Write the peer BFD configuration.
*/
void bgp_bfd_peer_config_write(struct vty *vty, const struct peer *peer,
const char *addr)
{
/*
* Always show group BFD configuration, but peer only when explicitly
* configured.
*/
if ((!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)
&& peer->bfd_config->manual)
|| CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
#if HAVE_BFDD > 0
vty_out(vty, " neighbor %s bfd\n", addr);
#else
vty_out(vty, " neighbor %s bfd %d %d %d\n", addr,
peer->bfd_config->detection_multiplier,
peer->bfd_config->min_rx, peer->bfd_config->min_tx);
#endif /* HAVE_BFDD */
}
if (peer->bfd_config->profile[0])
vty_out(vty, " neighbor %s bfd profile %s\n", addr,
peer->bfd_config->profile);
if (peer->bfd_config->cbit)
vty_out(vty, " neighbor %s bfd check-control-plane-failure\n",
addr);
}
/*
* bgp_bfd_show_info - Show the peer BFD information.
*/
void bgp_bfd_show_info(struct vty *vty, const struct peer *peer,
json_object *json_neigh)
{
bfd_sess_show(vty, json_neigh, peer->bfd_config->session);
}
DEFUN (neighbor_bfd,
neighbor_bfd_cmd,
"neighbor <A.B.C.D|X:X::X:X|WORD> bfd",
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Enables BFD support\n")
{
int idx_peer = 1;
struct peer *peer;
peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
if (!peer)
return CMD_WARNING_CONFIG_FAILED;
if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
bgp_group_configure_bfd(peer);
else
bgp_peer_configure_bfd(peer, true);
bgp_peer_config_apply(peer, peer->group);
return CMD_SUCCESS;
}
#if HAVE_BFDD > 0
DEFUN_HIDDEN(
#else
DEFUN(
#endif /* HAVE_BFDD */
neighbor_bfd_param,
neighbor_bfd_param_cmd,
"neighbor <A.B.C.D|X:X::X:X|WORD> bfd (2-255) (50-60000) (50-60000)",
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Enables BFD support\n"
"Detect Multiplier\n"
"Required min receive interval\n"
"Desired min transmit interval\n")
{
int idx_peer = 1;
int idx_number_1 = 3;
int idx_number_2 = 4;
int idx_number_3 = 5;
long detection_multiplier, min_rx, min_tx;
struct peer *peer;
peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
if (!peer)
return CMD_WARNING_CONFIG_FAILED;
detection_multiplier = strtol(argv[idx_number_1]->arg, NULL, 10);
min_rx = strtol(argv[idx_number_2]->arg, NULL, 10);
min_tx = strtol(argv[idx_number_3]->arg, NULL, 10);
if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
bgp_group_configure_bfd(peer);
else
bgp_peer_configure_bfd(peer, true);
peer->bfd_config->detection_multiplier = detection_multiplier;
peer->bfd_config->min_rx = min_rx;
peer->bfd_config->min_tx = min_tx;
bgp_peer_config_apply(peer, peer->group);
return CMD_SUCCESS;
}
DEFUN (neighbor_bfd_check_controlplane_failure,
neighbor_bfd_check_controlplane_failure_cmd,
"[no] neighbor <A.B.C.D|X:X::X:X|WORD> bfd check-control-plane-failure",
NO_STR
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"BFD support\n"
"Link dataplane status with BGP controlplane\n")
{
const char *no = strmatch(argv[0]->text, "no") ? "no" : NULL;
int idx_peer = 0;
struct peer *peer;
if (no)
idx_peer = 2;
else
idx_peer = 1;
peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
if (!peer)
return CMD_WARNING_CONFIG_FAILED;
if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
bgp_group_configure_bfd(peer);
else
bgp_peer_configure_bfd(peer, true);
peer->bfd_config->cbit = no == NULL;
bgp_peer_config_apply(peer, peer->group);
return CMD_SUCCESS;
}
DEFUN (no_neighbor_bfd,
no_neighbor_bfd_cmd,
#if HAVE_BFDD > 0
"no neighbor <A.B.C.D|X:X::X:X|WORD> bfd",
#else
"no neighbor <A.B.C.D|X:X::X:X|WORD> bfd [(2-255) (50-60000) (50-60000)]",
#endif /* HAVE_BFDD */
NO_STR
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Disables BFD support\n"
#if HAVE_BFDD == 0
"Detect Multiplier\n"
"Required min receive interval\n"
"Desired min transmit interval\n"
#endif /* !HAVE_BFDD */
)
{
int idx_peer = 2;
struct peer *peer;
peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
if (!peer)
return CMD_WARNING_CONFIG_FAILED;
if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
bgp_group_remove_bfd(peer);
else
bgp_peer_remove_bfd(peer);
return CMD_SUCCESS;
}
#if HAVE_BFDD > 0
DEFUN(neighbor_bfd_profile, neighbor_bfd_profile_cmd,
"neighbor <A.B.C.D|X:X::X:X|WORD> bfd profile BFDPROF",
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"BFD integration\n"
BFD_PROFILE_STR
BFD_PROFILE_NAME_STR)
{
int idx_peer = 1, idx_prof = 4;
struct peer *peer;
peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
if (!peer)
return CMD_WARNING_CONFIG_FAILED;
if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
bgp_group_configure_bfd(peer);
else
bgp_peer_configure_bfd(peer, true);
strlcpy(peer->bfd_config->profile, argv[idx_prof]->arg,
sizeof(peer->bfd_config->profile));
bgp_peer_config_apply(peer, peer->group);
return CMD_SUCCESS;
}
DEFUN(no_neighbor_bfd_profile, no_neighbor_bfd_profile_cmd,
"no neighbor <A.B.C.D|X:X::X:X|WORD> bfd profile [BFDPROF]",
NO_STR
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"BFD integration\n"
BFD_PROFILE_STR
BFD_PROFILE_NAME_STR)
{
int idx_peer = 2;
struct peer *peer;
peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
if (!peer)
return CMD_WARNING_CONFIG_FAILED;
if (!peer->bfd_config)
return CMD_SUCCESS;
if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
bgp_group_configure_bfd(peer);
else
bgp_peer_configure_bfd(peer, true);
peer->bfd_config->profile[0] = 0;
bgp_peer_config_apply(peer, peer->group);
return CMD_SUCCESS;
}
#endif /* HAVE_BFDD */
void bgp_bfd_init(struct event_loop *tm)
{
/* Initialize BFD client functions */
bfd_protocol_integration_init(zclient, tm);
/* "neighbor bfd" commands. */
install_element(BGP_NODE, &neighbor_bfd_cmd);
install_element(BGP_NODE, &neighbor_bfd_param_cmd);
install_element(BGP_NODE, &neighbor_bfd_check_controlplane_failure_cmd);
install_element(BGP_NODE, &no_neighbor_bfd_cmd);
#if HAVE_BFDD > 0
install_element(BGP_NODE, &neighbor_bfd_profile_cmd);
install_element(BGP_NODE, &no_neighbor_bfd_profile_cmd);
#endif /* HAVE_BFDD */
}

65
bgpd/bgp_bfd.h Normal file
View file

@ -0,0 +1,65 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/**
* bgp_bfd.h: BGP BFD definitions and structures
*
* @copyright Copyright (C) 2015 Cumulus Networks, Inc.
*/
#ifndef _QUAGGA_BGP_BFD_H
#define _QUAGGA_BGP_BFD_H
#define PEER_IS_MULTIHOP(peer) \
((((peer)->sort == BGP_PEER_IBGP) && !(peer)->shared_network) \
|| is_ebgp_multihop_configured((peer)))
extern void bgp_bfd_init(struct event_loop *tm);
extern void bgp_bfd_peer_config_write(struct vty *vty, const struct peer *peer,
const char *addr);
/**
* Show BFD information helper.
*
* \param vty the VTY pointer.
* \param peer the BGP configuration pointer.
* \param use_json unused.
* \param json_neigh JSON object when called as JSON command.
*/
extern void bgp_bfd_show_info(struct vty *vty, const struct peer *peer,
json_object *json_neigh);
/**
* When called on a group it applies configuration to all peers in that group,
* otherwise just applies the configuration to a single peer.
*
* This function should be called when configuration changes either on group
* or peer.
*
* \param p the BGP peer pointer.
* \param pg the BGP group to copy configuration from (it is usually
* `p->group` exception when copying new group configuration
* see `peer_group2peer_config_copy` function case).
*/
extern void bgp_peer_config_apply(struct peer *p, struct peer_group *pg);
/**
* Allocates and configure BFD session for peer. If it is already configured,
* then it does nothing.
*
* Always call `bgp_peer_config_apply` afterwards if you need the changes
* immediately applied.
*/
extern void bgp_peer_configure_bfd(struct peer *p, bool manual);
/**
* Removes BFD configuration from either peer or peer group.
*/
extern void bgp_peer_remove_bfd_config(struct peer *p);
/**
* Special function to handle the case of changing source address. This
* happens when the peer/group is configured with `neigbor X update-source Y`.
*/
extern void bgp_peer_bfd_update_source(struct peer *p);
#endif /* _QUAGGA_BGP_BFD_H */

2972
bgpd/bgp_bmp.c Normal file

File diff suppressed because it is too large Load diff

311
bgpd/bgp_bmp.h Normal file
View file

@ -0,0 +1,311 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* BMP support.
* Copyright (C) 2018 Yasuhiro Ohara
* Copyright (C) 2019 David Lamparter for NetDEF, Inc.
*/
#ifndef _BGP_BMP_H_
#define _BGP_BMP_H_
#include "zebra.h"
#include "typesafe.h"
#include "pullwr.h"
#include "qobj.h"
#include "resolver.h"
#define BMP_VERSION_3 3
#define BMP_LENGTH_POS 1
/* BMP message types */
#define BMP_TYPE_ROUTE_MONITORING 0
#define BMP_TYPE_STATISTICS_REPORT 1
#define BMP_TYPE_PEER_DOWN_NOTIFICATION 2
#define BMP_TYPE_PEER_UP_NOTIFICATION 3
#define BMP_TYPE_INITIATION 4
#define BMP_TYPE_TERMINATION 5
#define BMP_TYPE_ROUTE_MIRRORING 6
#define BMP_READ_BUFSIZ 1024
/* bmp->state */
#define BMP_None 0
#define BMP_PeerUp 2
#define BMP_Run 3
/* This one is for BMP Route Monitoring messages, i.e. delivering updates
* in somewhat processed (as opposed to fully raw, see mirroring below) form.
* RFC explicitly says that we can skip old updates if we haven't sent them out
* yet and another newer update for the same prefix arrives.
*
* So, at most one of these can exist for each (bgp, afi, safi, prefix, peerid)
* tuple; if some prefix is "re-added" to the queue, the existing entry is
* instead moved to the end of the queue. This ensures that the queue size is
* bounded by the BGP table size.
*
* bmp_qlist is the queue itself while bmp_qhash is used to efficiently check
* whether a tuple is already on the list. The queue is maintained per
* bmp_target.
*
* refcount = number of "struct bmp *" whose queue position is before this
* entry, i.e. number of BMP sessions where we still want to send this out.
* Decremented on send so we know when we're done with an entry (i.e. this
* always happens from the front of the queue.)
*/
PREDECL_DLIST(bmp_qlist);
PREDECL_HASH(bmp_qhash);
struct bmp_queue_entry {
struct bmp_qlist_item bli;
struct bmp_qhash_item bhi;
struct prefix p;
uint64_t peerid;
afi_t afi;
safi_t safi;
size_t refcount;
/* initialized only for L2VPN/EVPN (S)AFIs */
struct prefix_rd rd;
};
/* This is for BMP Route Mirroring, which feeds fully raw BGP PDUs out to BMP
* receivers. So, this goes directly off packet RX/TX handling instead of
* grabbing bits from tables.
*
* There is *one* queue for each "struct bgp *" where we throw everything on,
* with a size limit. Refcount works the same as for monitoring above.
*/
PREDECL_LIST(bmp_mirrorq);
struct bmp_mirrorq {
struct bmp_mirrorq_item bmi;
size_t refcount;
uint64_t peerid;
struct timeval tv;
size_t len;
uint8_t data[0];
};
enum {
BMP_AFI_INACTIVE = 0,
BMP_AFI_NEEDSYNC,
BMP_AFI_SYNC,
BMP_AFI_LIVE,
};
PREDECL_LIST(bmp_session);
struct bmp_active;
struct bmp_targets;
/* an established BMP session to a peer */
struct bmp {
struct bmp_session_item bsi;
struct bmp_targets *targets;
struct bmp_active *active;
int socket;
char remote[SU_ADDRSTRLEN + 6];
struct event *t_read;
struct pullwr *pullwr;
int state;
/* queue positions must remain synced with refcounts in the items.
* Whenever appending a queue item, we need to know the correct number
* of "struct bmp *" that want it, and when moving these positions
* ahead we need to make sure that refcount is decremented. Also, on
* disconnects we need to walk the queue and drop our reference.
*/
struct bmp_queue_entry *locrib_queuepos;
struct bmp_queue_entry *queuepos;
struct bmp_mirrorq *mirrorpos;
bool mirror_lost;
/* enum BMP_AFI_* */
uint8_t afistate[AFI_MAX][SAFI_MAX];
/* counters for the various BMP packet types */
uint64_t cnt_update, cnt_mirror;
/* number of times this peer wasn't fast enough in consuming the
* mirror queue
*/
uint64_t cnt_mirror_overruns;
struct timeval t_up;
/* synchronization / startup works by repeatedly finding the next
* table entry, the sync* fields note down what we sent last
*/
struct prefix syncpos;
struct bgp_dest *syncrdpos;
uint64_t syncpeerid;
afi_t syncafi;
safi_t syncsafi;
};
/* config & state for an active outbound connection. When the connection
* succeeds, "bmp" is set up.
*/
PREDECL_SORTLIST_UNIQ(bmp_actives);
#define BMP_DFLT_MINRETRY 30000
#define BMP_DFLT_MAXRETRY 720000
struct bmp_active {
struct bmp_actives_item bai;
struct bmp_targets *targets;
struct bmp *bmp;
char *hostname;
int port;
unsigned minretry, maxretry;
char *ifsrc;
union sockunion addrsrc;
struct resolver_query resq;
unsigned curretry;
unsigned addrpos, addrtotal;
union sockunion addrs[8];
int socket;
const char *last_err;
struct event *t_timer, *t_read, *t_write;
};
/* config & state for passive / listening sockets */
PREDECL_SORTLIST_UNIQ(bmp_listeners);
struct bmp_listener {
struct bmp_listeners_item bli;
struct bmp_targets *targets;
union sockunion addr;
int port;
struct event *t_accept;
int sock;
};
/* bmp_targets - plural since it may contain multiple bmp_listener &
* bmp_active items. If they have the same config, BMP session should be
* put in the same targets since that's a bit more effective.
*/
PREDECL_SORTLIST_UNIQ(bmp_targets);
struct bmp_targets {
struct bmp_targets_item bti;
struct bmp_bgp *bmpbgp;
struct bgp *bgp;
char *name;
struct bmp_listeners_head listeners;
char *acl_name;
char *acl6_name;
#define BMP_STAT_DEFAULT_TIMER 60000
int stat_msec;
/* only supporting:
* - IPv4 / unicast & multicast & VPN
* - IPv6 / unicast & multicast & VPN
* - L2VPN / EVPN
*/
#define BMP_MON_PREPOLICY (1 << 0)
#define BMP_MON_POSTPOLICY (1 << 1)
#define BMP_MON_LOC_RIB (1 << 2)
uint8_t afimon[AFI_MAX][SAFI_MAX];
bool mirror;
struct bmp_actives_head actives;
struct event *t_stats;
struct bmp_session_head sessions;
struct bmp_qhash_head updhash;
struct bmp_qlist_head updlist;
struct bmp_qhash_head locupdhash;
struct bmp_qlist_head locupdlist;
uint64_t cnt_accept, cnt_aclrefused;
bool stats_send_experimental;
QOBJ_FIELDS;
};
DECLARE_QOBJ_TYPE(bmp_targets);
/* per struct peer * data. Lookup by peer->qobj_node.nid, created on demand,
* deleted in peer_backward hook. */
PREDECL_HASH(bmp_peerh);
struct bmp_bgp_peer {
struct bmp_peerh_item bpi;
uint64_t peerid;
/* struct peer *peer; */
uint8_t *open_rx;
size_t open_rx_len;
uint8_t *open_tx;
size_t open_tx_len;
};
/* per struct bgp * data */
PREDECL_HASH(bmp_bgph);
#define BMP_PEER_DOWN_NO_RELEVANT_EVENT_CODE 0x00
struct bmp_bgp {
struct bmp_bgph_item bbi;
struct bgp *bgp;
struct bmp_targets_head targets;
struct bmp_mirrorq_head mirrorq;
size_t mirror_qsize, mirror_qsizemax;
size_t mirror_qsizelimit;
};
enum {
BMP_PEERDOWN_LOCAL_NOTIFY = 1,
BMP_PEERDOWN_LOCAL_FSM = 2,
BMP_PEERDOWN_REMOTE_NOTIFY = 3,
BMP_PEERDOWN_REMOTE_CLOSE = 4,
BMP_PEERDOWN_ENDMONITOR = 5,
};
enum {
BMP_STATS_PFX_REJECTED = 0,
BMP_STATS_PFX_DUP_ADV = 1,
BMP_STATS_PFX_DUP_WITHDRAW = 2,
BMP_STATS_UPD_LOOP_CLUSTER = 3,
BMP_STATS_UPD_LOOP_ASPATH = 4,
BMP_STATS_UPD_LOOP_ORIGINATOR = 5,
BMP_STATS_UPD_LOOP_CONFED = 6,
BMP_STATS_SIZE_ADJ_RIB_IN = 7,
BMP_STATS_SIZE_LOC_RIB = 8,
BMP_STATS_SIZE_ADJ_RIB_IN_SAFI = 9,
BMP_STATS_SIZE_LOC_RIB_IN_SAFI = 10,
BMP_STATS_UPD_7606_WITHDRAW = 11,
BMP_STATS_PFX_7606_WITHDRAW = 12,
BMP_STATS_UPD_DUP = 13,
BMP_STATS_FRR_NH_INVALID = 65531,
};
DECLARE_MGROUP(BMP);
#endif /*_BGP_BMP_H_*/

277
bgpd/bgp_btoa.c Normal file
View file

@ -0,0 +1,277 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* BGP dump to ascii converter
* Copyright (C) 1999 Kunihiro Ishiguro
*/
#include <zebra.h>
#include <fcntl.h>
#include "zebra.h"
#include "stream.h"
#include "log.h"
#include "prefix.h"
#include "command.h"
#include "memory.h"
#include "privs.h"
#include "filter.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_dump.h"
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_aspath.h"
/* privileges */
static zebra_capabilities_t _caps_p[] = {
ZCAP_BIND, ZCAP_NET_RAW, ZCAP_NET_ADMIN,
};
struct zebra_privs_t bgpd_privs = {
#if defined(FRR_USER) && defined(FRR_GROUP)
.user = FRR_USER,
.group = FRR_GROUP,
#endif
#ifdef VTY_GROUP
.vty_group = VTY_GROUP,
#endif
.caps_p = _caps_p,
.cap_num_p = array_size(_caps_p),
.cap_num_i = 0,
};
enum MRT_MSG_TYPES {
MSG_NULL,
MSG_START, /* sender is starting up */
MSG_DIE, /* receiver should shut down */
MSG_I_AM_DEAD, /* sender is shutting down */
MSG_PEER_DOWN, /* sender's peer is down */
MSG_PROTOCOL_BGP, /* msg is a BGP packet */
MSG_PROTOCOL_RIP, /* msg is a RIP packet */
MSG_PROTOCOL_IDRP, /* msg is an IDRP packet */
MSG_PROTOCOL_RIPNG, /* msg is a RIPNG packet */
MSG_PROTOCOL_BGP4PLUS, /* msg is a BGP4+ packet */
MSG_PROTOCOL_BGP4PLUS_01, /* msg is a BGP4+ (draft 01) packet */
MSG_PROTOCOL_OSPF, /* msg is an OSPF packet */
MSG_TABLE_DUMP /* routing table dump */
};
static void attr_parse(struct stream *s, uint16_t len)
{
unsigned int flag;
unsigned int type;
uint16_t length;
uint16_t lim;
lim = s->getp + len;
printf("%s s->getp %zd, len %d, lim %d\n", __func__, s->getp, len, lim);
while (s->getp < lim) {
flag = stream_getc(s);
type = stream_getc(s);
if (flag & BGP_ATTR_FLAG_EXTLEN)
length = stream_getw(s);
else
length = stream_getc(s);
printf("FLAG: %d\n", flag);
printf("TYPE: %d\n", type);
printf("Len: %d\n", length);
switch (type) {
case BGP_ATTR_ORIGIN: {
uint8_t origin;
origin = stream_getc(s);
printf("ORIGIN: %d\n", origin);
} break;
case BGP_ATTR_AS_PATH: {
struct aspath *aspath;
aspath = aspath_parse(s, length, 1,
bgp_get_asnotation(NULL));
printf("ASPATH: %s\n", aspath->str);
aspath_free(aspath);
} break;
case BGP_ATTR_NEXT_HOP: {
struct in_addr nexthop;
nexthop.s_addr = stream_get_ipv4(s);
printf("NEXTHOP: %pI4\n", &nexthop);
} break;
default:
stream_getw_from(s, length);
break;
}
}
}
int main(int argc, char **argv)
{
int ret;
int fd;
struct stream *s;
time_t now;
int type;
int subtype;
size_t len;
int source_as;
int dest_as;
ifindex_t ifindex;
int family;
struct in_addr sip;
struct in_addr dip;
uint16_t viewno, seq_num;
struct prefix_ipv4 p;
char tbuf[32];
s = stream_new(10000);
if (argc != 2) {
fprintf(stderr, "Usage: %s FILENAME\n", argv[0]);
exit(1);
}
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
fprintf(stdout,
"%% Can't open configuration file %s due to '%s'.\n",
argv[1], safe_strerror(errno));
exit(1);
}
while (1) {
stream_reset(s);
ret = stream_read(s, fd, 12);
if (ret != 12) {
if (!ret)
printf("END OF FILE\n");
else if (ret < 0)
printf("ERROR OF READ\n");
else
printf("UNDERFLOW\n");
break;
}
/* Extract header. */
now = stream_getl(s);
type = stream_getw(s);
subtype = stream_getw(s);
len = stream_getl(s);
printf("TIME: %s", ctime_r(&now, tbuf));
/* printf ("TYPE: %d/%d\n", type, subtype); */
if (type == MSG_PROTOCOL_BGP4MP)
printf("TYPE: BGP4MP");
else if (type == MSG_PROTOCOL_BGP4MP_ET)
printf("TYPE: BGP4MP_ET");
else if (type == MSG_TABLE_DUMP)
printf("TYPE: MSG_TABLE_DUMP");
else
printf("TYPE: Unknown %d", type);
if (type == MSG_TABLE_DUMP)
switch (subtype) {
case AFI_IP:
printf("/AFI_IP\n");
break;
case AFI_IP6:
printf("/AFI_IP6\n");
break;
default:
printf("/UNKNOWN %d", subtype);
break;
}
else {
switch (subtype) {
case BGP4MP_STATE_CHANGE:
printf("/CHANGE\n");
break;
case BGP4MP_MESSAGE:
printf("/MESSAGE\n");
break;
case BGP4MP_ENTRY:
printf("/ENTRY\n");
break;
case BGP4MP_SNAPSHOT:
printf("/SNAPSHOT\n");
break;
default:
printf("/UNKNOWN %d", subtype);
break;
}
}
printf("len: %zd\n", len);
ret = stream_read(s, fd, len);
if (ret != (int)len) {
if (!ret)
printf("END OF FILE 2\n");
else if (ret < 0)
printf("ERROR OF READ 2\n");
else
printf("UNDERFLOW 2\n");
break;
}
/* printf ("now read %d\n", len); */
if (type == MSG_TABLE_DUMP) {
uint8_t status;
time_t originated;
struct in_addr peer;
uint16_t attrlen;
viewno = stream_getw(s);
seq_num = stream_getw(s);
printf("VIEW: %d\n", viewno);
printf("SEQUENCE: %d\n", seq_num);
/* start */
while (s->getp < len - 16) {
p.prefix.s_addr = stream_get_ipv4(s);
p.prefixlen = stream_getc(s);
printf("PREFIX: %pI4/%d\n", &p.prefix,
p.prefixlen);
status = stream_getc(s);
originated = stream_getl(s);
peer.s_addr = stream_get_ipv4(s);
source_as = stream_getw(s);
printf("FROM: %pI4 AS%d\n", &peer, source_as);
printf("ORIGINATED: %s", ctime_r(&originated,
tbuf));
attrlen = stream_getw(s);
printf("ATTRLEN: %d\n", attrlen);
attr_parse(s, attrlen);
printf("STATUS: 0x%x\n", status);
}
} else {
source_as = stream_getw(s);
dest_as = stream_getw(s);
printf("source_as: %d\n", source_as);
printf("dest_as: %d\n", dest_as);
ifindex = stream_getw(s);
family = stream_getw(s);
printf("ifindex: %d\n", ifindex);
printf("family: %d\n", family);
sip.s_addr = stream_get_ipv4(s);
dip.s_addr = stream_get_ipv4(s);
printf("saddr: %pI4\n", &sip);
printf("daddr: %pI4\n", &dip);
printf("\n");
}
}
close(fd);
return 0;
}

Some files were not shown because too many files have changed in this diff Show more