Adding upstream version 10.1.1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
abd9495f94
commit
3124f89aed
7332 changed files with 1661972 additions and 0 deletions
227
.clang-format
Normal file
227
.clang-format
Normal 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
9
.dockerignore
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
.git
|
||||||
|
**/*.a
|
||||||
|
**/*.o
|
||||||
|
**/*.la
|
||||||
|
**/*.lo
|
||||||
|
**/*.so
|
||||||
|
**/.libs
|
||||||
|
docker/alpine/pkgs
|
||||||
|
docker/centos/pkgs
|
3
.flake8
Normal file
3
.flake8
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[flake8]
|
||||||
|
max-line-length = 88
|
||||||
|
extend-ignore = E203
|
24
.git-blame-ignore-revs
Normal file
24
.git-blame-ignore-revs
Normal 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
78
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal 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
8
.github/PULL_REQUEST_TEMPLATE/pr.md
vendored
Normal 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
46
.github/commitlint.config.js
vendored
Normal 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
20
.github/workflows/base-branch-label.yml
vendored
Normal 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
28
.github/workflows/behind-base.yml
vendored
Normal 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
163
.github/workflows/build-test-docker.yml
vendored
Normal 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
30
.github/workflows/commitlint.yml
vendored
Normal 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
21
.github/workflows/conflicts.yml
vendored
Normal 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."
|
53
.github/workflows/docker-daily-master.yml
vendored
Normal file
53
.github/workflows/docker-daily-master.yml
vendored
Normal 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
17
.github/workflows/freeze.yml
vendored
Normal 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
|
22
.github/workflows/mergifyio_backport.yml
vendored
Normal file
22
.github/workflows/mergifyio_backport.yml
vendored
Normal 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
26
.github/workflows/size-label.yml
vendored
Normal 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
29
.github/workflows/stale.yml
vendored
Normal 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
121
.gitignore
vendored
Normal 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
2
.isort.cfg
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[settings]
|
||||||
|
profile = black
|
9
.pylintrc
Normal file
9
.pylintrc
Normal 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
40
.travis.yml
Normal 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
16
COPYING
Normal 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
363
Makefile.am
Normal 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
83
README.md
Normal 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
1
alpine/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/APKBUILD
|
70
alpine/APKBUILD.in
Normal file
70
alpine/APKBUILD.in
Normal 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
6
alpine/frr.post-deinstall
Executable 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
4
alpine/frr.pre-deinstall
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
/etc/init.d/frr stop
|
||||||
|
exit 0
|
10
alpine/frr.pre-install
Executable file
10
alpine/frr.pre-install
Executable 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
8
babeld/.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
*
|
||||||
|
!*.c
|
||||||
|
!*.h
|
||||||
|
!LICENCE
|
||||||
|
!Makefile
|
||||||
|
!subdir.am
|
||||||
|
!.gitignore
|
||||||
|
*_clippy.c
|
10
babeld/Makefile
Normal file
10
babeld/Makefile
Normal 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
48
babeld/babel_errors.c
Normal 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
22
babeld/babel_errors.h
Normal 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
90
babeld/babel_filter.c
Normal 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
16
babeld/babel_filter.h
Normal 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
1357
babeld/babel_interface.c
Normal file
File diff suppressed because it is too large
Load diff
120
babeld/babel_interface.h
Normal file
120
babeld/babel_interface.h
Normal 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
377
babeld/babel_main.c
Normal 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
29
babeld/babel_main.h
Normal 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
253
babeld/babel_zebra.c
Normal 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
17
babeld/babel_zebra.h
Normal 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
896
babeld/babeld.c
Normal 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
102
babeld/babeld.h
Normal 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
239
babeld/kernel.c
Normal 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
32
babeld/kernel.h
Normal 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
1922
babeld/message.c
Normal file
File diff suppressed because it is too large
Load diff
86
babeld/message.h
Normal file
86
babeld/message.h
Normal 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
343
babeld/neighbour.c
Normal 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
47
babeld/neighbour.h
Normal 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
205
babeld/net.c
Normal 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
16
babeld/net.h
Normal 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
301
babeld/resend.c
Normal 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
48
babeld/resend.h
Normal 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
1129
babeld/route.c
Normal file
File diff suppressed because it is too large
Load diff
112
babeld/route.h
Normal file
112
babeld/route.h
Normal 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
152
babeld/source.c
Normal 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
34
babeld/source.h
Normal 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
50
babeld/subdir.am
Normal 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
429
babeld/util.c
Normal 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
139
babeld/util.h
Normal 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
214
babeld/xroute.c
Normal 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
29
babeld/xroute.h
Normal 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
2
bfdd/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# ignore binary files
|
||||||
|
bfdd
|
10
bfdd/Makefile
Normal file
10
bfdd/Makefile
Normal 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
2069
bfdd/bfd.c
Normal file
File diff suppressed because it is too large
Load diff
849
bfdd/bfd.h
Normal file
849
bfdd/bfd.h
Normal 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
1770
bfdd/bfd_packet.c
Normal file
File diff suppressed because it is too large
Load diff
157
bfdd/bfdctl.h
Normal file
157
bfdd/bfdctl.h
Normal 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
401
bfdd/bfdd.c
Normal 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
718
bfdd/bfdd_cli.c
Normal 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
490
bfdd/bfdd_nb.c
Normal 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
213
bfdd/bfdd_nb.h
Normal 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
847
bfdd/bfdd_nb_config.c
Normal 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
360
bfdd/bfdd_nb_state.c
Normal 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
1051
bfdd/bfdd_vty.c
Normal file
File diff suppressed because it is too large
Load diff
369
bfdd/bfddp_packet.h
Normal file
369
bfdd/bfddp_packet.h
Normal 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
592
bfdd/config.c
Normal 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
844
bfdd/control.c
Normal 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
1176
bfdd/dplane.c
Normal file
File diff suppressed because it is too large
Load diff
114
bfdd/event.c
Normal file
114
bfdd/event.c
Normal 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
986
bfdd/ptm_adapter.c
Normal 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
50
bfdd/subdir.am
Normal 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
3
bgpd/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
bgpd
|
||||||
|
bgp_btoa
|
||||||
|
bgpd.conf
|
10
bgpd/Makefile
Normal file
10
bgpd/Makefile
Normal 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
517
bgpd/bgp_addpath.c
Normal 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
74
bgpd/bgp_addpath.h
Normal 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
43
bgpd/bgp_addpath_types.h
Normal 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
229
bgpd/bgp_advertise.c
Normal 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
165
bgpd/bgp_advertise.h
Normal 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
2481
bgpd/bgp_aspath.c
Normal file
File diff suppressed because it is too large
Load diff
171
bgpd/bgp_aspath.h
Normal file
171
bgpd/bgp_aspath.h
Normal 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
5383
bgpd/bgp_attr.c
Normal file
File diff suppressed because it is too large
Load diff
652
bgpd/bgp_attr.h
Normal file
652
bgpd/bgp_attr.h
Normal 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
302
bgpd/bgp_attr_evpn.c
Normal 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
50
bgpd/bgp_attr_evpn.h
Normal 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
630
bgpd/bgp_bfd.c
Normal 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
65
bgpd/bgp_bfd.h
Normal 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
2972
bgpd/bgp_bmp.c
Normal file
File diff suppressed because it is too large
Load diff
311
bgpd/bgp_bmp.h
Normal file
311
bgpd/bgp_bmp.h
Normal 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
277
bgpd/bgp_btoa.c
Normal 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
Loading…
Add table
Reference in a new issue