Compare commits

..
48 Commits
Author SHA1 Message Date
Tobias Waldekranz 257f1df1d5 test: case: user_scripts: Add test
Verify that:
- Scripts are executed when the service is enabled
- Scripts are not enabled when the service is disabled
2024-05-21 22:02:28 +02:00
Tobias Waldekranz 8664c27096 confd: Add support for running user scripts
Add a new service that, when enabled, will execute run-parts on
`/cfg/user-scripts.d`.

This strikes a balance between two conflicting objectives:

1. There should be no implicit way to schedule arbitrary code
   execution on the device, i.e. no default run-parts directory.

2. It is very useful to have a way of scheduling arbitrary code
   execution on the device, e.g. being able to install a debug script
   on a production image.

With this feature, we still meet (1), since the feature has to be
explicitly enabled in the startup-config; but we also fulfill (2),
since we can easily enable it when needed.
2024-05-21 22:02:28 +02:00
Joachim Wiberg c7c1b5b12d .github: build aarch64 target as well in regression workflow
- Add aarch64 to build matrix on Reggie
 - Rename platform -> target in all workflows

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-05-21 10:10:16 +02:00
Joachim Wiberg 2f4cdd0d9c Fix 'ip addr show proto NAME'
- Add support for proto by name in ip addr show
 - Add missing static protos, needed for show

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-05-21 09:00:10 +02:00
Joachim Wiberg f1e7fda956 doc: revise developer's guide a bit wrt. packge overrides
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-05-21 09:00:10 +02:00
Tobias Waldekranz 7bb48ece49 test: Reinstate podman as default runner
Partial revert of 23469bbe3b. When running tests in a GitHub action,
we do not want to run as host root user. Therefore, restore podman as
the default runner, but make sure to prefer docker when attaching to
the host network namespace.
2024-05-20 16:21:06 +02:00
Tobias Waldekranz 508e2287b8 doc: testing: Sync documentation with latest changes
Update docs to match the recent developments in the test system.

Spin off the architecture description to its own document, and focus
testing.md on the day-to-day usage.
2024-05-20 16:21:06 +02:00
Tobias Waldekranz 03d48e9aab test: Unify the different test modes to a smaller number of targets
Now that tests in the host's network namespce can be run inside the
container, simplify test.mk for increased reusability.
2024-05-20 16:21:06 +02:00
Tobias Waldekranz d7c41489e0 test: Always run tests inside container
When we want to test a `make run` instance, or a physical topology,
start the docker in the host's network namespace instead.
2024-05-20 16:21:06 +02:00
Tobias Waldekranz 897faf08d9 qemu: Fix off-by-one in topology generation
When interface naming switched to use 1-based indices instead of
0-based, the topology generation was overlooked thus breaking tests
against a `make run` instance.

Bring everything back in sync.
2024-05-20 16:21:06 +02:00
Tobias Waldekranz 3ad20dfd5b test: Drop support for running tests with podman
When testing in the host's namespace, only docker seems to offer the
required capabilities (due to its daemon/client architecture).

Qeneth tests can still be run in podman. Users will have to install
the docker wrappers for podman to make use of this.
2024-05-20 16:21:06 +02:00
Tobias Waldekranz 3f2ae3f3fc test: case: igmp_basic: Speed up positive reception tests
Use must{,_not}_receive instead of the general sniffer, so that we can
continue the test as soon as we see the transmission at the receiver,
rather than always waiting the full 5s.

While we're here:
- Show the port mappings in the top block-comment
- Define a few more steps to show the user what is going on
2024-05-20 16:21:06 +02:00
Tobias Waldekranz f94815cf46 test: case: static_multicast_filters: Speed up positive reception tests
Use must{,_not}_receive instead of the general sniffer, so that we can
continue the test as soon as we see the transmission at the receiver,
rather than always waiting the full 5s.

While we're here:
- Show the port mappings in the top block-comment
- Define a few more steps to show the user what is going on
- Avoid global variable references in set_static_multicast_filter()
2024-05-20 16:21:06 +02:00
Tobias Waldekranz 2190c3bc52 test: netns: Add must{,_not}_receive
In test cases, it is common that we want to verify that a certain type
of traffic reaches a namespace. This has, up to this point, been
solved by the generic sniffer.Sniffer.

Try to improve the two most common use-cases...
1. Test if a flow reaches a namespace
2. Ensure that a flow does not reach a namespace
...by always exiting as soon as the first packet is received.

This saves time on positive tests, and the synchronous API allows it
to compose well with infamy.parallel().
2024-05-20 16:21:06 +02:00
Tobias Waldekranz e65625e32f test: Add support for attaching to devices via SSH
Support the familiar subprocess.run API, and runsh from netns.py.

This should only be used for debugging, or in the rare instances when
we want to test functionality that is not available via NETCONF.
2024-05-20 16:21:06 +02:00
Tobias Waldekranz af65bbe7f9 test: furl: Cap time spent waiting for HTTP requests
Without an explicit timeout, urllib will wait indefinitely for a
connection to be made.
2024-05-20 16:21:06 +02:00
Tobias Waldekranz 1dbe09b428 test: Spellchecking 2024-05-20 16:21:06 +02:00
Tobias Waldekranz 232c2329af test: netns: Let the default ping timeout be tuned in the topology
QEMU systems seem work fine with the 5s timeout, whereas some physical
devices can't quite keep up with that.

Let the slower systems indicate that they need a bit more time without
punishing faster ones with a longer default.
2024-05-20 16:21:06 +02:00
Tobias Waldekranz 772019db22 test: env: Add support for querying global attributes
This will let other Infamy submodules tune their behavior based on the
particular test system they are running on.

Attributes are defined by creating graph-global attributes with an
"ix_" prefix.
2024-05-20 16:21:06 +02:00
Tobias Waldekranz 52c6c2274e test: topology: Add helper to retrieve graph-global attributes
This will allow us to define attributes that are specific to a
particular test system.
2024-05-20 16:21:06 +02:00
Tobias Waldekranz e53f3362be test: case: routing_basic: Restructure to reuse existing namespace
Rather than setting up and tearing down two identical namespaces,
create it once and use it for the duration of the test.
2024-05-20 16:21:06 +02:00
Tobias Waldekranz c3945a996c test: case: static_routing: Restructure to reuse existing namespace
Rather than setting up and tearing down two identical namespaces,
create it once and use it for the duration of the test. (This is
probably a documentation issue with Infamy: there is no requirement
that test.steps are direct descendants of the test, but until this
point there was no example of this in the source base)

In the process of this, it was also discovered that no positive ping
test was done over v6, so that has been added.
2024-05-20 16:21:06 +02:00
Tobias Waldekranz c2d357a185 test: Use stable MAC addresses when creating MACVLANs
To work around the issue detailed in the method's doc comment.
2024-05-20 16:21:06 +02:00
Tobias Waldekranz 74b0679cf0 test: case: container_*: Use standard ping tests 2024-05-20 16:21:06 +02:00
Tobias Waldekranz dc6dffbff9 test: case: bridge_*: Use standard ping tests 2024-05-20 16:21:06 +02:00
Tobias Waldekranz 4c6dfdf600 test: Run negative ping tests in parallel
Rather than having to wait for each timeout sequentially, run each
test in parallel.
2024-05-20 16:21:06 +02:00
Tobias Waldekranz b62fd627f0 test: Never buffer output
Avoid having log messages stuck in a pipe when stdout is not a tty,
e.g. when running a suite of tests using 9pm.

Two benefits:
- Nicer experience when interactively running a suite
- Timestamps inserted by 9pm are more useful
2024-05-20 16:21:06 +02:00
Tobias Waldekranz 177b65587b test: case: routing_basic: Remove bridge from figure
This a routed setup, no bridge is used.
2024-05-20 16:21:06 +02:00
Tobias Waldekranz b5679aa603 test: Improve robustness of pinging from within namespaces
It turns out that the semantics of ping's -c option does not match our
expectations. The manual says:

> Stop after sending count ECHO_REQUEST packets. With deadline option,
> ping waits for count ECHO_REPLY packets, until the timeout expires.

But in fact, something like an ICMP_DEST_UNREACH will also count as a
response, meaning that the process will terminate with non-zero
exitcode even though the deadline has not yet been crossed, rather
than keep waiting for an ECHO_REPLY.

To add some extra flavor: this is only the case for iputils' ping,
Busybox's implementation will hold out for a response until the
deadline expires.

Therefore, wrap the ping in an ugly retry loop.

Also, return the full available context from must{,_not}_reach on
failures, instead of just the output. This allows us to show the exit
code in the top-level exception handler.
2024-05-20 16:21:06 +02:00
Tobias Waldekranz fb2bd2c2b7 test: Whitespace cleanup 2024-05-20 16:21:06 +02:00
Tobias Waldekranz b9907fa7ae test: Add nsenter helper script to enter Infamy namespaces
When a test starts an isolated MACVLAN namespace, it is often useful
to interactively join that namespace to investigate issues. To do
this, we need to find the "sleeper" process backing the namespace, and
`nsenter` into that process's network namespace.

Add a script that automates this process.

To start a shell in the netns of the MACVLAN stacked on top of d1b,
start a shell in the test environment:

    infix/test $ ./shell
    13:37:00 infamy0:test # ./nsenter d1b
    13:37:01 infamy0(netns:d1b):test #
2024-05-20 16:21:06 +02:00
Tobias Waldekranz 7d6792a638 test: Always log logical-to-physical mappings
For tests using a logical topology, log the resulting mapping to the
physical one. This way, we always have a record of the entire
environment.
2024-05-20 16:21:06 +02:00
Tobias Waldekranz b6cf866f52 test: Log the output of failing subprocesses
It is not uncommon for subprocesses to fail when developing new tests,
but the default representation of CalledProcessError does not print
the stdout of the program, only that it failed.

Catch this common case when a test is about to fail, and make sure to
log any output it produced.
2024-05-20 16:21:06 +02:00
Tobias Waldekranz 2c72aaf6e1 test: Move all test related targets from board/x86_64 to test/
The list of binaries needed inside the test environment is the only
architecture specific bit, so refactor that out to a separate
variable.
2024-05-20 16:21:06 +02:00
Tobias WaldekranzandJoachim Wiberg b5f2252650 kernel: Latest fixes
- Add missing locking around standard counter groups
- Fix reference counting issue with IGMPv3/MLDv2 reports

Found as part of debugging the regression test suite on hardware.
2024-05-20 16:15:23 +02:00
Joachim Wiberg ca026a37fc Add link to Latest Build
[skip ci]

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-05-18 12:11:37 +02:00
Joachim WibergandTobias Waldekranz abd8621e92 doc: split container host interface, add w/ phys-address example
The container host interface text was a bit hard to understand.  This
patch splits it up in two parts, basic routed setup and an advanced
with two interfaces.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-05-18 09:58:19 +02:00
Joachim WibergandTobias Waldekranz 0df778a5f6 confd: fix custom mac support for veth interfaces
This patch adds support for setting the mac address already when
creating veth interfaces.  Necessary to cooperate with containers.

Fixes #453

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-05-18 09:58:19 +02:00
Joachim WibergandTobias Waldekranz 0a7f8ce216 confd: always create factory bridges with multicast snooping enabled
The confd `gen-interfaces` script is called for some customers with a
default to create a bridge with all, or a subset of all, ports.  The
safe default, which incidentally coincides with the same customer's
requirements, is to have IGMP/MLD snooping enabled by default.

Fixes #454

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-05-18 09:58:19 +02:00
Joachim WibergandTobias Waldekranz 7e2d433857 Prepare ChangeLog for next release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-05-18 09:58:19 +02:00
Joachim Wiberg 413470569e doc: clarify with before and after
[skip ci]

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-05-17 16:54:09 +02:00
Joachim Wiberg b2adf639ff doc: initial section on developing new features for confd
[skip ci]

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-05-17 16:42:06 +02:00
Joachim Wiberg 3ac1dfbf4c .github: ensure clean and recursive submodules
Minor change, mostly for consistency across repos.

[skip ci]

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-05-17 14:38:57 +02:00
Joachim Wiberg 975a7b466c .github: introduce issue templates
We already have a pull request template.  This is a propsal to add issue
templates as well.  These yml templates change the behavior of reporting
issues to more a of a pre-formatted form with guard rails.

It is still possible to report blank issues the old way.

[skip ci]

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-05-16 13:32:35 +02:00
Joachim Wiberg bdefe9452c .github: revise and update contributing guidelines
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-05-16 13:32:35 +02:00
Joachim Wiberg 9211fba6d5 .github: simplify pull-request template slightly
Referencing/Linking to issues is now supported in the GitHub GUI.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-05-16 13:32:35 +02:00
Joachim Wiberg 1d58d2058d package/gencert: declare dependency on pkg-config and libopenssl
Plain openssl is a meta-package, gencert requires libopenssl v3.x and
later, and use pkg-config to detect its presence.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-05-16 13:32:35 +02:00
Joachim WibergandMattias Walström 23dc237403 Switch to PAM for system authentication
- Add sshd 'UsePAM yes'
 - Buildroot automatically adds and enables:
   - /etc/pam.d/ with authentication for login, sshd, and sudo
   - PAM support in BusyBox login

Also, prepare for adding RADIUS authentication support to ietf-system
the only tricky part is testing against a RADIUS server.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-05-15 16:06:08 +02:00
88 changed files with 1605 additions and 798 deletions
+36 -20
View File
@@ -11,7 +11,7 @@ Talking about code and problems first is often the best way to get started
before submitting a pull request.
When submitting a bug report, patch, or pull request, please start by
stating the version the change is made against, what it does, and why.
stating the version the change is made against, what it does, *and why*.
Please take care to ensure you follow the project coding style and the
commit message format. If you follow these recommendations you help
@@ -21,13 +21,28 @@ the maintainer(s) and make it easier for them to include your code.
Coding Style
------------
> **Tip:** Always submit code that follows the style of surrounding code!
Before jumping into code, remember to **document new features** and bug
fixes. Both the manual and ChangeLog are in the `doc/` sub-directory
and it is expected that you provide a human-readable summary for the
release notes (ChangeLog) and at least a configuration example in the
manual for new features.
First of all, lines are allowed to be longer than 72 characters these
days. In fact, there exist no enforced maximum, but keeping it around
100 chars is OK.
> **Tip:** consider ["Readme driven development"][RDD] for new features.
> It is amazing how many flaws in your own bright ideas come to bare
> when you suddenly have to explain them to someone else!
The coding style itself is otherwise strictly Linux [KNF][].
We expect code contributions for:
- C code in [Linux Coding Style][Linux]
- Python code should follow [PEP-8][]
> **However,** always submit code that follows the style of surrounding
> code! Legacy takes precedence, and remember, we read code a lot more
> than write it, so legibility is important.
As a final note, lines are allowed to be longer than 72 characters these
days. There is no enforced maximum, but the team usually keep it around
100 characters for both C and Python.
Commit Messages
@@ -37,22 +52,21 @@ Commit messages exist to track *why* a change was made. Try to be as
clear and concise as possible in your commit messages, and always, be
proud of your work and set up a proper GIT identity for your commits:
git config --global user.name "Jane Doe"
git config --global user.email jane.doe@example.com
$ git config --global user.name "Jacky Linker"
$ git config --global user.email jacky.linker@example.com
Example commit message from the [Pro Git][gitbook] online book, notice
how `git commit -s` is used to automatically add a `Signed-off-by`:
Brief, but clear and concise summary of changes
subsystem: brief, but clear and concise summary of changes
More detailed explanatory text, if necessary. Wrap it to about 72
characters or so. In some contexts, the first line is treated as
the subject of an email and the rest of the text as the body. The
blank line separating the ummary from the body is critical (unless
you omit the body entirely); tools like rebase can get confused if
you run the two together.
empty line separating summary from body is critical. Tools like
rebase can get confused if the empty line is missing.
Further paragraphs come after blank lines.
Further paragraphs should be separated with empty lines.
- Bullet points are okay, too
@@ -60,17 +74,19 @@ how `git commit -s` is used to automatically add a `Signed-off-by`:
by a single space, with blank lines in between, but conventions
vary here
Signed-off-by: Jane Doe <jane.doe@example.com>
Signed-off-by: Jacky Linker <jacky.linker@example.com>
Code of Conduct
---------------
It is expected of everyone to respect the [Code of Conduct][conduct].
The *"maintainers have the right and responsibility to remove, edit,
or reject comments, commits, code, wiki edits, issues, and other
contributions that are not aligned to this Code of Conduct."*
The *"maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, discussion forum threads, issues, and
other contributions that are not aligned to this Code of Conduct."*
[KNF]: https://en.wikipedia.org/wiki/Kernel_Normal_Form
[gitbook]: https://git-scm.com/book/ch5-2.html
[conduct]: CODE-OF-CONDUCT.md
[Linux]: https://www.kernel.org/doc/html/v6.9/process/coding-style.html
[PEP-8]: https://peps.python.org/pep-0008/
[RDD]: https://tom.preston-werner.com/2010/08/23/readme-driven-development
[gitbook]: https://git-scm.com/book/ch5-2.html
[conduct]: CODE-OF-CONDUCT.md
+69
View File
@@ -0,0 +1,69 @@
# https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema
name: "\U0001F41E Bug report"
description: Create a bug report
labels:
- "bug"
- "triage"
body:
- type: markdown
attributes:
value: |
**Thank :heart: you for taking the time to fill out this bug report!**
We kindly ask that you search to see if an issue [already exists](https://github.com/kernelkit/infix/issues?q=is%3Aissue+sort%3Acreated-desc+) for the bug you encountered.
- type: textarea
attributes:
label: Current Behavior
description: |
A clear and concise description of the issue you're experiencing.
value: |
For code snippets, logs, commands, etc., please use triple backticks:
```
admin@infix-c0-ff-ee:/> show log
May 15 07:21:02 infix-00-00-00 container[3192]: Failed creating container test from curios-httpd-v24.03.0
- (press h for help or q to quit)
```
validations:
required: true
- type: textarea
attributes:
label: Expected Behavior
description: |
A clear and concise description of what you expected to happen.
value: |
```
admin@infix-c0-ff-ee:/> show log
May 15 07:21:02 infix-c0-ff-ee container[3192]: Successfully created container test from curios-httpd-v24.03.0
- (press h for help or q to quit)
```
validations:
required: true
- type: textarea
attributes:
label: Steps To Reproduce
description: |
Steps to reproduce the issue. For example:
1. Built from source
2. Upgrade to latest release, vYY.MM.P
3. Factory reset
4. Enable feature
5. Check the logs/show command/operational status
validations:
required: false
- type: textarea
attributes:
label: Additional information
description: |
- Relevant parts of `startup-config`
- Output from `show interfaces`, if applicable
- Other observations, screenshots, log files ...
**Tip:** You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations:
required: false
+7
View File
@@ -0,0 +1,7 @@
contact_links:
- name: "🔍 Look for my Issue"
url: https://github.com/kernelkit/infix/issues?q=is%3Aissue+sort%3Acreated-desc+
about: Thank ❤️ you for your time! We kindly ask that you first check if an issue already exists.
- name: "💬 Discussions and Q&A"
url: https://github.com/kernelkit/infix/discussions
about: Discussions are perfect for quick questions, bouncing ideas, and things that may be a bug.
@@ -0,0 +1,45 @@
# https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema
name: "\U0001F6E0 Feature request"
description: Suggest a new feature, idea or improvement of the OS
labels:
- "feature"
- "triage"
body:
- type: markdown
attributes:
value: |
**Thank :heart: you for taking the time to fill out this feature request!**
We kindly ask that you search to see if an issue [already exists](https://github.com/kernelkit/infix/issues?q=is%3Aissue+sort%3Acreated-desc+) for your feature. We are also happy to accept contributions from our users. For details see [CONTRIBUTING](https://github.com/kernelkit/infix/blob/master/.github/CONTRIBUTING.md).
- type: textarea
attributes:
label: Description
description: |
A clear and concise description of the problem or missing feature.
**Example:** *I'm always frustrated when [...]*, or *I'd like to see support for [...]*
validations:
required: true
- type: textarea
attributes:
label: Additional Information
description: |
Any other related information, e.g. existing YANG model(s), or possibly screenshots about the feature request here.
validations:
required: false
- type: textarea
attributes:
label: General Information
description: |
Appended to all feature requests for future readers.
value: |
Anyone can help out by [sponsoring][1] development of new features or [contributing][2] pull requests.
Please use this issue for discussions related to the feature.
[1]: https://github.com/kernelkit/infix/blob/main/.github/SUPPORT.md
[2]: https://github.com/kernelkit/infix/blob/main/.github/CONTRIBUTING.md
validations:
required: true
+1 -7
View File
@@ -1,5 +1,3 @@
<!--- **Summarize** your changes in the title above -->
## Description
<!--
@@ -15,7 +13,7 @@
## Checklist
Tick relevant boxes, this PR is-a or has-a:
Tick *relevant* boxes, this PR is-a or has-a:
- [ ] Bugfix
- [ ] Regression tests
@@ -31,7 +29,3 @@ Tick relevant boxes, this PR is-a or has-a:
- [ ] Documentation content changes
- [ ] Other (please describe):
## References
<!-- Please list references to related issue(s) -->
+11 -9
View File
@@ -8,21 +8,22 @@ on:
jobs:
build:
name: Build Infix ${{ matrix.platform }}
name: Build Infix ${{ matrix.target }}
runs-on: [ self-hosted, latest ]
strategy:
matrix:
platform: [aarch64, x86_64]
target: [aarch64, x86_64]
fail-fast: false
steps:
- uses: actions/checkout@v4
with:
submodules: 'true'
clean: true
submodules: recursive
- name: Set Build Variables
id: vars
run: |
target=${{ matrix.platform }}
target=${{ matrix.target }}
echo "dir=infix-$target" >> $GITHUB_OUTPUT
echo "tgz=infix-$target.tar.gz" >> $GITHUB_OUTPUT
@@ -38,14 +39,14 @@ jobs:
uses: actions/cache@v4
with:
path: .ccache/
key: ccache-${{ matrix.platform }}-${{ hashFiles('.git/modules/buildroot/HEAD', 'package/*/*.hash') }}
key: ccache-${{ matrix.target }}-${{ hashFiles('.git/modules/buildroot/HEAD', 'package/*/*.hash') }}
restore-keys: |
ccache-${{ matrix.platform }}-
ccache-${{ matrix.target }}-
ccache-
- name: Configure & Build
run: |
target=${{ matrix.platform }}_defconfig
target=${{ matrix.target }}_defconfig
echo "Building $target ..."
make $target
make
@@ -58,13 +59,14 @@ jobs:
tar chfz ${{ steps.vars.outputs.tgz }} ${{ steps.vars.outputs.dir }}
- name: Test
if: matrix.platform == 'x86_64'
if: matrix.target == 'x86_64'
run: |
make test
- uses: actions/upload-artifact@v4
with:
path: output/${{ steps.vars.outputs.tgz }}
name: artifact-${{ matrix.platform }}
name: artifact-${{ matrix.target }}
release:
if: ${{github.repository_owner == 'kernelkit' && github.ref_name == 'main'}}
+18 -9
View File
@@ -15,13 +15,18 @@ concurrency:
jobs:
build:
name: Regression Testing
name: Regression Testing ${{ matrix.target }}
runs-on: [ self-hosted, regression ]
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
strategy:
matrix:
target: [aarch64, x86_64]
fail-fast: false
steps:
- uses: actions/checkout@v4
with:
submodules: 'true'
clean: true
submodules: recursive
- name: Clean up cruft ...
run: |
@@ -30,7 +35,7 @@ jobs:
- name: Set Build Variables
id: vars
run: |
target=x86_64
target=${{ matrix.target }}
echo "dir=infix-$target" >> $GITHUB_OUTPUT
echo "tgz=infix-$target.tar.gz" >> $GITHUB_OUTPUT
@@ -38,23 +43,24 @@ jobs:
uses: actions/cache@v4
with:
path: dl/
key: dl-netconf-${{ hashFiles('.git/modules/buildroot/refs/heads/master', 'package/*/*.hash') }}
key: dl-netconf-${{ hashFiles('.git/modules/buildroot/HEAD', 'configs/*', 'package/*/*.hash') }}
restore-keys: |
dl-netconf-
dl-
- name: Restore Cache of .ccache/
uses: actions/cache@v4
with:
path: .ccache/
key: ccache-x86_64-${{ hashFiles('.git/modules/buildroot/refs/head/master', 'package/*/*.hash') }}
key: ccache-x86_64-${{ hashFiles('.git/modules/buildroot/HEAD', 'package/*/*.hash') }}
restore-keys: |
ccache-x86_64-
ccache-${{ matrix.target }}-
ccache-
- name: Configure NETCONF
- name: Configure ${{ matrix.target }}
run: |
make x86_64_defconfig
target=${{ matrix.target }}_defconfig
echo "Building $target ..."
make $target
- name: Unit Test
run: |
@@ -70,12 +76,15 @@ jobs:
mv images ${{ steps.vars.outputs.dir }}
ln -s ${{ steps.vars.outputs.dir }} images
tar cfz ${{ steps.vars.outputs.tgz }} ${{ steps.vars.outputs.dir }}
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
path: output/${{ steps.vars.outputs.tgz }}
name: artifact-${{ matrix.target }}
- name: Regression Test
if: matrix.target == 'x86_64'
run: |
make test
+9 -8
View File
@@ -20,16 +20,17 @@ on:
jobs:
build:
if: github.repository == 'kernelkit/infix' && startsWith(github.ref, 'refs/tags/')
name: Build Infix ${{ github.ref_name }} [${{ matrix.platform }}]
name: Build Infix ${{ github.ref_name }} [${{ matrix.target }}]
runs-on: [ self-hosted, release ]
strategy:
matrix:
platform: [aarch64, x86_64]
target: [aarch64, x86_64]
fail-fast: false
steps:
- uses: actions/checkout@v4
with:
submodules: 'true'
clean: true
submodules: recursive
- name: Set Release Variables
id: vars
@@ -41,7 +42,7 @@ jobs:
fi
echo "ver=${ver}" >> $GITHUB_OUTPUT
fver=${ver#v}
target=${{ matrix.platform }}-${fver}
target=${{ matrix.target }}-${fver}
echo "dir=infix-$target" >> $GITHUB_OUTPUT
echo "tgz=infix-$target.tar.gz" >> $GITHUB_OUTPUT
@@ -57,16 +58,16 @@ jobs:
uses: actions/cache@v4
with:
path: .ccache/
key: ccache-${{ matrix.platform }}-${{ hashFiles('.git/modules/buildroot/HEAD', 'package/*/*.hash') }}
key: ccache-${{ matrix.target }}-${{ hashFiles('.git/modules/buildroot/HEAD', 'package/*/*.hash') }}
restore-keys: |
ccache-${{ matrix.platform }}-
ccache-${{ matrix.target }}-
ccache-
- name: Configure & Build
env:
INFIX_RELEASE: ${{ steps.vars.outputs.ver }}
run: |
target=${{ matrix.platform }}_defconfig
target=${{ matrix.target }}_defconfig
echo "Building $target ..."
make $target
make
@@ -87,7 +88,7 @@ jobs:
- uses: actions/upload-artifact@v4
with:
name: artifact-${{ matrix.platform }}
name: artifact-${{ matrix.target }}
path: output/*.tar.gz
release:
+2 -1
View File
@@ -100,7 +100,7 @@ and testing, but can also be used for evaluation and demo purposes. For
more information, see: [Infix in Virtual Environments](doc/virtual.md).
> See the [GitHub Releases](https://github.com/kernelkit/infix/releases)
> page for our pre-built images. The *Latest Build* has the bleeding
> page for our pre-built images. The *[Latest Build][]* has bleeding
> edge images, if possible we recommend using a versioned release.
>
> For *customer specific builds* of Infix, see your product repository.
@@ -116,6 +116,7 @@ more information, see: [Infix in Virtual Environments](doc/virtual.md).
[1]: https://buildroot.org/
[2]: https://www.sysrepo.org/
[3]: doc/cli/introduction.md
[Latest Build]: https://github.com/kernelkit/infix/releases/tag/latest
[License]: https://en.wikipedia.org/wiki/GPL_license
[License Badge]: https://img.shields.io/badge/License-GPL%20v2-blue.svg
[GitHub]: https://github.com/kernelkit/infix/actions/workflows/build.yml/
+4 -4
View File
@@ -312,10 +312,10 @@ generate_dot()
{
[ "$CONFIG_QEMU_NET_TAP" = "y" ] || return
hostports="<qtap0> qtap0"
targetports="<e0> e0"
edges="host:qtap0 -- target:e0 [kind=mgmt];"
for tap in $(seq 1 $(($CONFIG_QEMU_NET_TAP_N - 1))); do
hostports="<qtap1> qtap1"
targetports="<e1> e1"
edges="host:qtap1 -- target:e1 [kind=mgmt];"
for tap in $(seq 2 $(($CONFIG_QEMU_NET_TAP_N - 1))); do
hostports="$hostports | <qtap$tap> qtap$tap "
targetports="$targetports | <e$tap> e$tap "
edges="$edges host:qtap$tap -- target:e$tap;"
@@ -1,3 +1,7 @@
0 unspec
1 kernel_lo
2 kernel_ra
3 kernel_ll
4 static
5 dhcp
6 random
@@ -0,0 +1 @@
UsePAM yes
-34
View File
@@ -1,34 +0,0 @@
test-dir := $(BR2_EXTERNAL_INFIX_PATH)/test
INFIX_TESTS ?= $(test-dir)/case/all.yaml
test-env = $(test-dir)/env \
-f $(BINARIES_DIR)/infix-x86_64.img \
-f $(BINARIES_DIR)/infix-x86_64-disk.img \
-f $(BINARIES_DIR)/OVMF.fd \
-p $(BINARIES_DIR)/infix-x86_64.pkg \
$(1) $(2)
test-env-qeneth = $(call test-env,-q $(test-dir)/virt/quad,$(1))
test-env-run = $(call test-env,-C -t $(BINARIES_DIR)/qemu.dot,$(1))
test test-qeneth:
$(call test-env-qeneth,\
$(BR2_EXTERNAL_INFIX_PATH)/test/9pm/9pm.py \
$(INFIX_TESTS))
test-sh test-qeneth-sh:
$(call test-env-qeneth,-i /bin/sh)
test-run: | ~/.infix-test-venv
$(call test-env-run,\
$(BR2_EXTERNAL_INFIX_PATH)/test/9pm/9pm.py \
$(INFIX_TESTS))
test-run-sh: | ~/.infix-test-venv
$(call test-env-run,/bin/sh)
test-run-play: | ~/.infix-test-venv
$(call test-env-run,$(test-dir)/case/meta/play.py)
~/.infix-test-venv:
$(test-dir)/docker/init-venv.sh $(test-dir)/docker/pip-requirements.txt
.PHONY: test test-sh test-qeneth test-qeneth-sh test-run test-run-sh test-run-play
+2
View File
@@ -62,6 +62,8 @@ BR2_PACKAGE_LIBOPENSSL_BIN=y
BR2_PACKAGE_LIBCURL_CURL=y
BR2_PACKAGE_NETOPEER2_CLI=y
BR2_PACKAGE_NSS_MDNS=y
BR2_PACKAGE_LINUX_PAM=y
BR2_PACKAGE_LIBPAM_RADIUS_AUTH=y
BR2_PACKAGE_ONIGURUMA=y
BR2_PACKAGE_AVAHI=y
BR2_PACKAGE_AVAHI_DAEMON=y
+2
View File
@@ -73,6 +73,8 @@ BR2_PACKAGE_LIBOPENSSL_BIN=y
BR2_PACKAGE_LIBCURL_CURL=y
BR2_PACKAGE_NETOPEER2_CLI=y
BR2_PACKAGE_NSS_MDNS=y
BR2_PACKAGE_LINUX_PAM=y
BR2_PACKAGE_LIBPAM_RADIUS_AUTH=y
BR2_PACKAGE_ONIGURUMA=y
BR2_PACKAGE_AVAHI=y
BR2_PACKAGE_AVAHI_DAEMON=y
+2
View File
@@ -57,6 +57,8 @@ BR2_PACKAGE_LIBOPENSSL_BIN=y
BR2_PACKAGE_LIBCURL_CURL=y
BR2_PACKAGE_NETOPEER2_CLI=y
BR2_PACKAGE_NSS_MDNS=y
BR2_PACKAGE_LINUX_PAM=y
BR2_PACKAGE_LIBPAM_RADIUS_AUTH=y
BR2_PACKAGE_ONIGURUMA=y
BR2_PACKAGE_AVAHI=y
BR2_PACKAGE_AVAHI_DAEMON=y
+40
View File
@@ -4,6 +4,45 @@ Change Log
All notable changes to the project are documented in this file.
[v24.05.0][UNRELEASED]
----------------------
### Changes
- Default web landing page refactored into a Buildroot package to make
it possible to overload from customer repos.
- Enable DCB support in aarch64 kernel (for EtherType prio override)
- Topology mapper improvements, including option for deterministic
reproduction of logical to physical mappings
- New version of `gencert` tool, for self signed HTTPS certificates.
This allows dropping dependency on building a host rust toolchain
- Issue #374: add timestamps to dagger .log files
- Add small delay in U-Boot to allow stopping boot on reference boards
- Document how to provision the bootloader and Infix on a blank board
- Use initial hostname from `/etc/os-release` as configuration fallback
- Update documentation for use of VETH pairs in containers
- Issue #454: create bridges in `factory-config` with IGMP/MLD snooping
enabled by default
- Add support for optionally running user scripts from
`/cfg/user-scripts.d`
### Fixes
- Add missing LICENSE hash for factory reset tool
- Fix #424: regression, root user can log in without password
- Fix build regressions in `cn9130_crb_boot_defconfig` caused by upgrade
to Buildroot v2024.02 and recent multi-key support in RAUC and U-Boot
- Fix provisioning script after changes to make GRUB loading more robust
- Fix missing `/etc/resolv.conf`, as noticed by `avahi-daemon`, when a
user calls `no system` from the CLI
- Fix #428: loss of admin account after upgrade to v24.04
- Fix #429: failing to load `startup-config` does not trigger the fail
secure mode, causing the system to end up in an undefined state
- Fix #453: fix inconsistent behavior of custom MAC address (interface
`phys-address` for VETH pairs. Allows fixed MAC in containers
- Fix locking issue with standard counter groups on `mv88e6xxx`
- Fix MDB/ATU synchronization issue from IGMPv3/MLDv2 reports on
`mv88e6xxx` systems
[v24.04.2][] - 2024-05-15
-------------------------
@@ -881,6 +920,7 @@ Supported YANG models in addition to those used by sysrepo and netopeer:
[buildroot]: https://buildroot.org/
[UNRELEASED]: https://github.com/kernelkit/infix/compare/v24.04.0...HEAD
[v24.05.0]: https://github.com/kernelkit/infix/compare/v24.04.0...v24.05.0
[v24.04.2]: https://github.com/kernelkit/infix/compare/v24.04.1...v24.04.2
[v24.04.1]: https://github.com/kernelkit/infix/compare/v24.04.0...v24.04.1
[v24.04.0]: https://github.com/kernelkit/infix/compare/v24.02.0...v24.04.0
+60 -19
View File
@@ -390,12 +390,30 @@ network:
### Container Host Interface
Another common use-case is to move a network interface into the network
namespace of a container. Which the container bridge network type does
behind the scenes with one end of the automatically created VETH pair.
This works with regular Ethernet interfaces as well, but here we will
use a VETH pair as an example along with a regular bridge (where other
Ethernet interfaces may live as well).
Another common use-case is to move network interfaces into the network
namespace of a container[^3]. This of course works with plain Ethernet
interfaces as well, but here we will use one end of a VETH pair as an
example.
[^3]: Something which the container bridge network type does behind the
scenes with one end of an automatically created VETH pair.
#### Routed Setup
In this routed setup we reserve 192.168.0.0/24 for the network between
the host and the `ntpd` container.
_____________
| |
veth0 .1 | .2 eth0 |
\ 192.168.0.0/24 | / |
'--------------------' [ntpd] |
|_____________|
Configuration is a straight-forward VETH pair setup where we name the
container-end of pair `ntpd`. This is just a convenience for us when
reading the configuration later. The *real action* happens on the last
line where we declare the `ntpd` end as a container network interface:
admin@example:/config/> edit interface veth0
admin@example:/config/interface/veth0/> set veth peer ntpd
@@ -403,23 +421,49 @@ Ethernet interfaces may live as well).
admin@example:/config/interface/veth0/> end
admin@example:/config/> edit interface ntpd
admin@example:/config/interface/ntpd/> set ipv4 address 192.168.0.2 prefix-length 24
admin@example:/config/interface/ntpd/> set phys-address 00:c0:ff:ee:00:01
admin@example:/config/interface/ntpd/> set container-network
This is a routed setup, where we reserve 192.168.0.0/24 for the network
between the host and the `ntpd` container. A perhaps more common case
is to put `veth0` as a port in a bridge with other physical ports. The
point of the routed case is that port forwarding from the container in
this case is limited to a single interface, not *all interfaces* as is
the default in the masquerading container bridge setup.
> Notice how you can also set a custom MAC address at the same time.
When a container has multiple host interfaces it can often be useful to
have a default route installed. This can be added from the host with a
`0.0.0.0/0` route on one of the interfaces. The following is an example
when adding a second VETH pair to the container:
Adding the interface to the container is the same as before, but since
everything for host interfaces is set up in the interfaces context, we
can take a bit of a shortcut.
admin@example:/config/container/ntpd/> set network ntpd
admin@example:/config/container/ntpd/> leave
The point of the routed case is that port forwarding from the container
in this case is limited to a single interface, not *all interfaces* as
is the default in the masquerading container bridge setup.
#### Bridged Setup
A perhaps more common case is to bridge the other end of the VETH pair
with other physical ports. In this section we show how to add a new
pair to give our container two interfaces:
_______________
| | .1 br0
veth0 .1 | .2 eth0 | / \
\ 192.168.0.0/24 | / eth1 .2 | veth1b e1
'------------------' \ | 192.168.1.0/24 /
| [ntpd] '------------------'
|_______________|
We start by adding the second VETH pair:
admin@example:/config/> edit interface veth1a
admin@example:/config/interface/veth1a/> set veth peer veth1b
admin@example:/config/interface/veth1a/> set ipv4 address 192.168.1.2 prefix-length 24
> The LAN bridge (br0) in this example has IP address 192.168.1.1.
When a container has multiple host interfaces it can often be useful to
have a default route installed. This can be added from the host with a
`0.0.0.0/0` route on one of the interfaces:
admin@example:/config/interface/veth1a/> set container-network route 0.0.0.0/0 gateway 192.168.1.1
admin@example:/config/interface/veth1a/> show
type veth;
@@ -439,9 +483,6 @@ Please note, container network routes require the base interface also
have a static IP address set. Setting only the route, but no address,
means the route is skipped.
> The LAN bridge (br0) in this example has IP address 192.168.1.1.
### Host Networking
The third use-case is host networking, this is where a container share
+41 -5
View File
@@ -97,8 +97,10 @@ To see available defconfigs for supported targets, use:
Development
-----------
When changing a package, locally kept sources, or when using [`local.mk`](override-package.md),
you only want to rebuild the parts you have modified:
Developing with Infix is the same as [developing with Buildroot][4].
When working with a package, be it locally kept sources, or when using
[`local.mk`](override-package.md), you only want to rebuild the parts
you have modified:
make foo-rebuild
@@ -106,7 +108,7 @@ or
make foo-reconfigure
or, when nothing seems to bite:
or, as a last resort when nothing seems to bite:
make foo-dirclean foo-rebuild
@@ -119,6 +121,38 @@ This rebuilds (and installs) `foo` and `bar`, the `all` target calls
on Buildroot to finalize the target filesystem and generate the images.
The final `run` argument is explained below.
### `confd`
The Infix `src/confd/` is the engine of the system. Currently it is a
plugin for `systemd-plugind` and contains XPath subscriptions to all the
supported YANG models.
There are essentially two ways of adding support for a new YANG model:
- The [sysrepo way][3], or
- The Infix way, using libsrx (the `lydx_*()` functions)
The former is well documented in sysrepo, and the latter is best taught
by example, e.g., `src/confd/src/infix-dhcp.c`. Essentially libsrx is a
way of traversing the libyang tree instead of fetching changes by XPath.
When working with `confd` you likely want to enable full debug mode,
this is how you do it:
1. Open the file `package/confd/confd.conf`
2. Uncomment the first line `set DEBUG=1`
3. Change the following line to add `-v3` at the end
[S12345] sysrepo-plugind -f -p /run/confd.pid -n -- Configuration daemon
to:
[S12345] sysrepo-plugind -f -p /run/confd.pid -n -v3 -- Configuration daemon
Now you can rebuild `confd`, just as described above, and restart Infix:
make confd-rebuild all run
Testing
-------
@@ -142,8 +176,8 @@ and then use GitHub to create a *Pull Reqeuest*.
For this to work as painlessly as possible:
1. Fork Infix to your own user or organization[^1]
2. Fork all the Infix submodules, e.g., `buildroot` to your own user
or organization as well
2. Fork all the Infix submodules, e.g., `kernelkit/buildroot` to your
own user or organization as well
3. Clone your fork of Infix to your laptop/workstation
If you use a GitHub organization you get the added benefit of having
@@ -170,3 +204,5 @@ $ git submodule update --init
[0]: https://github.com/kernelkit/infix/releases
[1]: https://buildroot.org/downloads/manual/manual.html
[2]: https://github.com/wkz/qeneth
[3]: https://netopeer.liberouter.org/doc/sysrepo/master/html/dev_guide.html
[4]: https://buildroot.org/downloads/manual/manual.html#_developer_guide
+53 -28
View File
@@ -1,50 +1,75 @@
Package override
Package Override
================
This guide demonstrates how the `local.mk` file is utilized to override
a Linux Buildroot package. The example of `tcpdump` serves to illustrate
This guide demonstrates how the `local.mk` file is utilized to override
a Linux Buildroot package. As an example we use `tcpdump` to illustrate
this process.
In an instance such as `Infix` using tcpdump, the `local.mk` file is modified
as shown below:
> For a comprehensive guide to utilizing Buildroot during development,
> including the `<pkg>_OVERRIDE_SRCDIR` mechanism, shown below, please
> see [Using Buildroot during development][1] in the official docs.
Setup
-----
Since the `output/` directory is often wiped for rebuilds, make sure you
keep the file `local.mk` in the top directory and only symlink it to your
build directory:
~$ cd infix/
~/infix(main)$ touch local.mk
~/infix/output(main)$ ln -s ../local.mk .
~/infix/output(main)$ cd ..
Override
--------
Now edit the file:
~/infix(main)$ editor local.mk
Add an override for `tcpdump`. The file is a Makefile snippet so you
can add a lot of things. Comment out lines with the UNIX comment `#`
character if needed:
```
TCPDUMP_OVERRIDE_SRCDIR = /path/to/tcpdump/repo
```
and stored in the `output/` folder, alongside the `.config` file:
```
user@PC:~/infix$ll output/ | grep -e 'local.mk' -e '.config'
-rw-r--r-- 1 group user 119936 Nov 10 18:04 .config
-rw-r--r-- 1 group user 43 Nov 10 18:25 local.mk
```
Building
--------
The execution of `make tcpdump-rebuild all` triggers a process where
Buildroot synchronizes the tcpdump source code from the specified override directory
to `output/build/tcpdump-custom`, followed by the rebuilding of the entire project.
The execution of `make tcpdump-rebuild all` triggers a process where
Buildroot synchronizes the tcpdump source code from the specified
override directory to `output/build/tcpdump-custom`, followed by the
rebuilding of the entire project.
```
user@PC:~/infix$ make tcpdump-rebuild all
~/infix$(main)$ make tcpdump-rebuild all
```
Buildroot follows a process of downloading and processing tarballs:
extraction, configuration, compilation, and installation. The source
for each package is extracted to a temporary build directory:
output/build/<package>-<version> # e.g., tcpdump-4.99.4
Let's have a look at what we got:
```
user@PC:~/infix$ ll /output/build/ | grep tcpdump
~/infix$(main)$ ll /output/build/ | grep tcpdump
drwxr-xr-x 7 group user 20480 Nov 10 18:26 tcpdump-4.99.4/
drwxr-xr-x 7 group user 12288 Nov 10 18:28 tcpdump-custom/
```
Buildroot follows a process of downloading and processing tarballs
(extraction, configuration, compilation, and installation).
The source code is stored in a temporary directory:
`output/build/<package>-<version>` (i.e. `tcpdump-4.99.4/`),
which is removed and recreated with each `make` command. That is why
the direct modifications in the `output/build` directory are generally
**not recommended**.
As long as your local override is in place, Buildroot will use your
custom version.
To manage the development changes more effectively, where the package source code
remains untouched, Buildroot incorporates the `<pkg>_OVERRIDE_SRCDIR` feature.
> **Remember:** the build directory is ephemeral, so be careful to
> change any of the files therein. It can be useful though during
> debugging, but just make sure to learn the difference between the
> various Buildroot commands to build, clean, reconfigure, etc.
For a comprehensive understanding of utilizing Buildroot during development,
including detailed elaboration on the `<pkg>_OVERRIDE_SRCDIR` feature,
refer to section 8.13.6 in [Using Buildroot during development](https://nightly.buildroot.org/).
[1]: https://buildroot.org/downloads/manual/manual.html#_using_buildroot_during_development
+246
View File
@@ -0,0 +1,246 @@
Test System Architecture
========================
Tenets
------
- **Keep overhead to a minimum**. Tests should be fast to both write
and run. Ideally, the developer should _want_ to add tests early in
the development cycle because they instinctively feel that that is
the quickest route to arrive at a correct and robust implementation.
- **Both physical and virtual hardware matters**. Infix is primarily
deployed on physical hardware, so being able to run the test suite
on real devices is crucial to guarantee a high quality product. At
the same time, there is much value in running the same suite on
virtual hardware, as it makes it easy to catch regressions early.
It is also much more practical and economical to build large virtual
networks than physical ones.
- **Avoid CLI scipting & scraping**. Reliably interacting with a DUT
over a serial line in a robust way is _very_ hard to get right.
Given that we have a proper API (RESTCONF), we should leverage that
when testing. Front-ends can be tested by other means.
Overview
--------
![Infix Testing Architecture](img/testing-overview.svg)
The test system is made up of several independent components, which
are typically used in concert to run a full test suite.
### Test Cases
A test case is an executable, receiving the physical topology as a
positional argument, which produces [TAP][] compliant output on its
`stdout`. I.e., it is executed in the following manner:
test-case [OPTS] <physical-topology>
Test cases are typically written in Python, using the
[Infamy](#infamy) library. Ultimately though, it can be implemented
in any language, as long as it matches the calling convention above.
### Infamy
Rather than having each test case come up with its own implementation
of how to map topologies, how to push NETCONF data to a device, etc.,
we provide a library of functions to take care of all that, dubbed
"Infamy". When adding a new test case, ask yourself if any parts of
it might belong in Infamy as a generalized component that can be
reused by other tests.
Some of the core functions provided by Infamy are:
- Mapping a logical topology to a physical one
- Finding and attaching to a device over an Ethernet interface, using
NETCONF
- Pushing/pulling NETCONF data to/from a device
- Generating TAP compliant output
### 9PM
To run multiple tests, we employ [9PM][]. It let's us define test
suites as simple YAML files. Suites can also be hierarchically
structured, with a suite being made up of other suites, etc.
It also validates the TAP output, making sure to catch early exits
from a case, and produces a nice summary report.
### `/test/env`
A good way to ensure that nobody ever runs the test suite is to make
it _really_ hard to do so. `/test/env`'s job is instead to make it
very _easy_ to create a reproducible environment in which tests can be
executed.
Several technologies are leveraged to accomplish this:
- **Containers**: The entire execution is optionally done inside a
standardized `docker` container environment. This ensures that the
software needed to run the test suite is always available, no matter
which distribution the user is running on their machine.
- **Python Virtual Environments**: To make sure that the expected
versions of all Python packages are available, the execution is
wrapped inside a `venv`. This is true for containerized executions,
where the container comes with a pre-installed environment, but it
can also be sourced from the host system when running outside of the
container.
- **Virtual Test Topology**: Using [Qeneth][], the environment can
optionally be started with a virtual topology of DUTs to run the
tests on.
> `docker` is the only supported container environment when running
> tests in the host's network namespace. When running on a virtual
> Qeneth topology, `podman` may also be used by installing the
> `podman-docker` package from your host system's distro.
Physical and Logical Topologies
-------------------------------
Imagine that we want to create a test with three DUTs; one acting as a
DHCP server, and the other two as DHCP clients - with all three having
a management connection to the host PC running the test. In other
words, the test requires a _logical_ topology like the one below.
<img align="right" src="img/testing-log.dot.svg" alt="Example Logical Topology">
```dot
graph "dhcp-client-server" {
host [
label="host | { <c1> c1 | <srv> srv | <c2> c2 }",
kind="controller",
];
server [
label="{ <mgmt> mgmt } | server | { <c1> c1 | <c2> c2 }",
kind="infix",
];
client1 [
label="{ <mgmt> mgmt } | client1 | { <srv> srv }",
kind="infix",
];
client2 [
label="{ <mgmt> mgmt } | client2 | { <srv> srv }",
kind="infix",
];
host:srv -- server:mgmt
host:c1 -- client1:mgmt
host:c2 -- client2:mgmt
server:c1 -- client1:srv;
server:c2 -- client2:srv;
}
```
When running in a virtualized environment, one could simply create a
setup that matches the test's logical topology. But in scenarios when
devices are physical systems, connected by real copper cables, this is
not possible (unless you have some wicked L1 relay matrix thingy).
Instead, the test implementation does not concern itself with the
exact nodes used to run the test, only that the _logical_ topology can
be _mapped_ to some subset of the _physical_ topology. In
mathematical terms, the physical topology must contain a subgraph that
is _isomorphic_ to the logical topology.
Standing on the shoulders of giants (i.e. people with mathematics
degrees), we can deploy well-known algorithms to find such subgraphs.
Continuing our example, let's say we want to run our DHCP test on the
_physical_ topology below.
<img align="right" src="img/testing-phy.dot.svg" alt="Example Physical Topology">
```dot
graph "quad-ring" {
host [
label="host | { <d1a> d1a | <d1b> d1b | <d1c> d1c | <d2a> d2a | <d2b> d2b | <d2c> d2c | <d3a> d3a | <d3b> d3b | <d3c> d3c | <d4a> d4a | <d4b> d4b | <d4c> d4c }",
kind="controller",
];
dut1 [
label="{ <e1> e1 | <e2> e2 | <e3> e3 } | dut1 | { <e4> e4 | <e5> e5 }",
kind="infix",
];
dut2 [
label="{ <e1> e1 | <e2> e2 | <e3> e3 } | dut2 | { <e4> e4 | <e5> e5 }",
kind="infix",
];
dut3 [
label="{ <e1> e1 | <e2> e2 | <e3> e3 } | dut3 | { <e4> e4 | <e5> e5 }",
kind="infix",
];
dut4 [
label="{ <e1> e1 | <e2> e2 | <e3> e3 } | dut4 | { <e4> e4 | <e5> e5 }",
kind="infix",
];
host:d1a -- dut1:e1
host:d1b -- dut1:e2
host:d1c -- dut1:e3
host:d2a -- dut2:e1
host:d2b -- dut2:e2
host:d2c -- dut2:e3
host:d3a -- dut3:e1
host:d3b -- dut3:e2
host:d3c -- dut3:e3
host:d4a -- dut4:e1
host:d4b -- dut4:e2
host:d4c -- dut4:e3
dut1:e5 -- dut2:e4
dut2:e5 -- dut3:e4
dut3:e5 -- dut4:e4
dut4:e5 -- dut1:e4
}
```
Our test (in fact, all tests) receives the physical topology as an
input parameter, and then maps the desired logical topology onto it,
producing a mapping from logical nodes and ports to their physical
counterparts.
```dot
{
"client1": "dut1",
"client1:mgmt": "dut1:e1",
"client1:srv": "dut1:e4",
"client2": "dut3",
"client2:mgmt": "dut3:e2",
"client2:srv": "dut3:e5",
"host": "host",
"host:c1": "host:d1a",
"host:c2": "host:d3b",
"host:srv": "host:d4c",
"server": "dut4",
"server:c1": "dut4:e5",
"server:c2": "dut4:e4",
"server:mgmt": "dut4:e3"
}
```
With this information, the test knows that, in this particular
environment, the server should be managed via the port called `d4c` on
the node called `host`; that the port connected to the server on
`client1` is `e4` on `dut1`, etc. Thereby separating the
implementation of the test from any specific physical setup.
Testcases are not required to use a logical topology; they may choose
to accept whatever physical topology its given, and dynamically
determine the DUTs to use for testing. As an example, an STP test
could accept an arbitrary physical topology, run the STP algorithm on
it offline, enable STP on all DUTs, and then verify that the resulting
spanning tree matches the expected one.
[9PM]: https://github.com/rical/9pm
[Qeneth]: https://github.com/wkz/qeneth
[TAP]: https://testanything.org/
+98 -291
View File
@@ -7,287 +7,57 @@ that one or more DUTs are configured over NETCONF; the resulting
network is then black-box tested by injecting and inspecting network
traffic at various points.
This document is intended to be a practical guide on how to run,
develop and debug tests. There is a separate document describing the
[Test System Architecture](test-arch.md).
TL;DR
-----
Build Infix and run the test suite on a set of virtual Infix nodes.
Modes of Testing
----------------
### Virtual Devices
By default, tests are run on a topology made up of virtual Infix nodes
using [Qeneth][]. To run the full regression test suite: build Infix
for `x86_64`:
$ make x86_64_defconfig
$ make
$ make test
To run a subset of tests, e.g., only the DHCP client tests:
### Physical Devices
$ make test INFIX_TESTS=case/infix_dhcp/all.yaml
To run the tests on a preexisting topology from the host's network
namespace, specify the `host` `TEST_MODE`:
> **Note:** see the below section [Quick Start Guide][] for how to
> connect to the test system, debug and develop tests, and more.
$ make TEST_MODE=host test
This typically used when testing on physical hardware. By default the
topology will be sourced from `/etc/infamy.dot`, but this can be
overwritten by setting the `TOPOLOGY` variable:
$ make TEST_MODE=host TOPOLOGY=~/my-topology.dot test
Tenets
------
### `make run` Devices
- **Keep overhead to a minimum**. Tests should be fast to both write
and run. Ideally, the developer should _want_ to add tests early in
the development cycle because they instinctively feel that that is
the quickest route to arrive at a correct and robust implementation.
Some tests only require a single DUT. These can therefore be run
against an Infix image started from `make run`. This requires that the
instance is configured to use TAP networking.
- **Both physical and virtual hardware matters**. Infix is primarily
deployed on physical hardware, so being able to run the test suite
on real devices is crucial to guarantee a high quality product. At
the same time, there is much value in running the same suite on
virtual hardware, as it makes it easy to catch regressions early.
It is also much more practical and economical to build large virtual
networks than physical ones.
When the instance is running, you can open a separate terminal and run
the subset of the test suite that can be mapped to it:
- **Avoid CLI scipting & scraping**. Reliably interacting with a DUT
over a serial line in a robust way is _very_ hard to get right.
Given that we have a proper API (RESTCONF), we should leverage that
when testing. Front-ends can be tested by other means.
Architectural Overview
----------------------
![Infix Testing Architecture](img/testing-overview.svg)
The test system is made up of several independent components, which
are typically used in concert to run a full test suite.
### Test Cases
A test case is an executable, receiving the physical topology as a
positional argument, which produces [TAP][] compliant output on its
`stdout`. I.e., it is executed in the following manner:
test-case [OPTS] <physical-topology>
Test cases are typically written in Python, using the
[Infamy](#infamy) library. Ultimately though, it can be implemented
in any language, as long as it matches the calling convention above.
### Infamy
Rather than having each test case come up with its own implementation
of how to map topologies, how to push NETCONF data to a device, etc.,
we provide a library of functions to take care of all that, dubbed
"Infamy". When adding a new test case, ask yourself if any parts of
it might belong in Infamy as a generalized component that can be
reused by other tests.
Some of the core functions provided by Infamy are:
- Mapping a logical topology to a physical one
- Finding and attaching to a device over an Ethernet interface, using
NETCONF
- Pushing/pulling NETCONF data to/from a device
- Generating TAP compliant output
### 9PM
To run multiple tests, we employ [9PM][]. It let's us define test
suites as simple YAML files. Suites can also be hierarchically
structured, with a suite being made up of other suites, etc.
It also validates the TAP output, making sure to catch early exits
from a case, and produces a nice summary report.
### `/test/env`
A good way to ensure that nobody ever runs the test suite is to make
it _really_ hard to do so. `/test/env`'s job is instead to make it
very _easy_ to create a reproducible environment in which tests can be
executed.
Several technologies are leveraged to accomplish this:
- **Containers**: The entire execution is optionally done inside a
standardized container environment, using either `podman` or
`docker`. This ensures that the software needed to run the test
suite is always available, no matter which distribution the user is
running on their machine.
- **Python Virtual Environments**: To make sure that the expected
versions of all Python packages are available, the execution is
wrapped inside a `venv`. This is true for containerized executions,
where the container comes with a pre-installed environment, but it
can also be sourced from the host system when running outside of the
container.
- **Virtual Test Topology**: Using [Qeneth][], the environment can
optionally be started with a virtual topology of DUTs to run the
tests on.
$ make TEST_MODE=run test
Interactive Usage
-----------------
Some tests only require a single DUT. These can therefore be run
against an Infix image started from `make run`. When the instance is
running, you can open a separate terminal and run `make test-run`, to
run the subset of the test suite that can be mapped to it.
Both `make test` and `make test-run` targets have a respective target
with a `-sh` suffix. These can be used to start an interactive session
in the reproducible environment, which is usually much easier to work
with during a debugging session.
Inside the reproducible environment, a wrapper for Qeneth is created for
the running network's directory. E.g., calling `qeneth status` inside a
`make test-sh` environment show the expected status information.
> **Note:** for more information and help writing a test, see the below
> [Quick Start Guide][].
Physical and Logical Topologies
-------------------------------
Imagine that we want to create a test with three DUTs; one acting as a
DHCP server, and the other two as DHCP clients - with all three having
a management connection to the host PC running the test. In other
words, the test requires a _logical_ topology like the one below.
<img align="right" src="img/testing-log.dot.svg" alt="Example Logical Topology">
```dot
graph "dhcp-client-server" {
host [
label="host | { <c1> c1 | <srv> srv | <c2> c2 }",
kind="controller",
];
server [
label="{ <mgmt> mgmt } | server | { <c1> c1 | <c2> c2 }",
kind="infix",
];
client1 [
label="{ <mgmt> mgmt } | client1 | { <srv> srv }",
kind="infix",
];
client2 [
label="{ <mgmt> mgmt } | client2 | { <srv> srv }",
kind="infix",
];
host:srv -- server:mgmt
host:c1 -- client1:mgmt
host:c2 -- client2:mgmt
server:c1 -- client1:srv;
server:c2 -- client2:srv;
}
```
When running in a virtualized environment, one could simply create a
setup that matches the test's logical topology. But in scenarios when
devices are physical systems, connected by real copper cables, this is
not possible (unless you have some wicked L1 relay matrix thingy).
Instead, the test implementation does not concern itself with the
exact nodes used to run the test, only that the _logical_ topology can
be _mapped_ to some subset of the _physical_ topology. In
mathematical terms, the physical topology must contain a subgraph that
is _isomorphic_ to the logical topology.
Standing on the shoulders of giants (i.e. people with mathematics
degrees), we can deploy well-known algorithms to find such subgraphs.
Continuing our example, let's say we want to run our DHCP test on the
_physical_ topology below.
<img align="right" src="img/testing-phy.dot.svg" alt="Example Physical Topology">
```dot
graph "quad-ring" {
host [
label="host | { <d1a> d1a | <d1b> d1b | <d1c> d1c | <d2a> d2a | <d2b> d2b | <d2c> d2c | <d3a> d3a | <d3b> d3b | <d3c> d3c | <d4a> d4a | <d4b> d4b | <d4c> d4c }",
kind="controller",
];
dut1 [
label="{ <e1> e1 | <e2> e2 | <e3> e3 } | dut1 | { <e4> e4 | <e5> e5 }",
kind="infix",
];
dut2 [
label="{ <e1> e1 | <e2> e2 | <e3> e3 } | dut2 | { <e4> e4 | <e5> e5 }",
kind="infix",
];
dut3 [
label="{ <e1> e1 | <e2> e2 | <e3> e3 } | dut3 | { <e4> e4 | <e5> e5 }",
kind="infix",
];
dut4 [
label="{ <e1> e1 | <e2> e2 | <e3> e3 } | dut4 | { <e4> e4 | <e5> e5 }",
kind="infix",
];
host:d1a -- dut1:e1
host:d1b -- dut1:e2
host:d1c -- dut1:e3
host:d2a -- dut2:e1
host:d2b -- dut2:e2
host:d2c -- dut2:e3
host:d3a -- dut3:e1
host:d3b -- dut3:e2
host:d3c -- dut3:e3
host:d4a -- dut4:e1
host:d4b -- dut4:e2
host:d4c -- dut4:e3
dut1:e5 -- dut2:e4
dut2:e5 -- dut3:e4
dut3:e5 -- dut4:e4
dut4:e5 -- dut1:e4
}
```
Our test (in fact, all tests) receives the physical topology as an
input parameter, and then maps the desired logical topology onto it,
producing a mapping from logical nodes and ports to their physical
counterparts.
```dot
{
"client1": "dut1",
"client1:mgmt": "dut1:e1",
"client1:srv": "dut1:e4",
"client2": "dut3",
"client2:mgmt": "dut3:e2",
"client2:srv": "dut3:e5",
"host": "host",
"host:c1": "host:d1a",
"host:c2": "host:d3b",
"host:srv": "host:d4c",
"server": "dut4",
"server:c1": "dut4:e5",
"server:c2": "dut4:e4",
"server:mgmt": "dut4:e3"
}
```
With this information, the test knows that, in this particular
environment, the server should be managed via the port called `d4c` on
the node called `host`; that the port connected to the server on
`client1` is `e4` on `dut1`, etc. Thereby separating the
implementation of the test from any specific physical setup.
Testcases are not required to use a logical topology; they may choose
to accept whatever physical topology its given, and dynamically
determine the DUTs to use for testing. As an example, an STP test
could accept an arbitrary physical topology, run the STP algorithm on
it offline, enable STP on all DUTs, and then verify that the resulting
spanning tree matches the expected one.
Quick Start Guide
-----------------
When developing a test, instead of blindly coding in Python and running
`make test` over and over, start a test shell:
When developing and debugging tests, the overhead of repeatedly
setting up and tearing down the test environment can quickly start to
weigh you down. In these situation, you can start an interactive test
environment:
$ make test-sh
Info: Generating topology
@@ -299,13 +69,28 @@ When developing a test, instead of blindly coding in Python and running
Info: Launching dut4
11:42:52 infamy0:test #
The example above uses the default mode (`qeneth`), but the `host` and
`run` modes also support the `test-sh` target.
It takes a little while to start up, but then we have a shell prompt
inside the container running Infamy. It's a very limited environment,
but it has enough to easily run single tests, connect to the virtual
devices, and *step* your code. Let's run a test:
devices, and *step* your code.
### Running Subsets of Tests
Each test case is a separate executable, which can be run without
arguments:
11:42:53 infamy0:test # ./case/infix_dhcp/dhcp_basic.py
To run a suite of tests, e.g., only the DHCP client tests, pass the
suite as an argument to [9PM][]:
11:42:53 infamy0:test # ./9pm/9pm.py case/infix_dhcp/all.yaml
### Connecting to Infamy
The test system runs in a Docker container, so to get a shell prompt in
@@ -313,6 +98,7 @@ The test system runs in a Docker container, so to get a shell prompt in
with a helper script for this:
$ ./test/shell
11:42:53 infamy0:test #
By default it connect to the latest started Infamy instance. If you for
some reason run multiple instances of Infamy the `shell` script takes an
@@ -320,6 +106,8 @@ optional argument "system", which is the hostname of the container you
want to connect to:
$ ./test/shell infamy2
11:42:53 infamy2:test #
### Connecting to a DUT
@@ -369,10 +157,47 @@ You can also connect to the console of a DUT from within a `shell`:
> the BusyBox version so you press 'e' + enter instead of 'q' to quit.
### Debug a Test
### Attaching to MACVLANs
First, add a Python `breakpoint()` to your test and run it from your
`make test-sh` Infamy instance:
To fully isolate the host's interfaces from one another, many tests
will stack a MACVLAN on an interface, which is then placed in a
separate network namespace.
It is often useful to attach to those namespaces, so that you can
interactively inject traffic into the test setup.
First, connect to the Infamy instance in question:
$ ./test/shell
11:43:19 infamy0:test #
Then, attach to the MACVLAN namespace by running the `nsenter` helper
script from the test directory, supplying the base interface name as
the first argument:
11:43:19 infamy0:test # ./nsenter d1b
11:43:20 infamy0(d1b):test #
By default, an interactive shell is started, but you can also supply
another command:
11:43:19 infamy0:test # ./nsenter d1b ip -br addr
lo UNKNOWN 127.0.0.1/8 ::1/128
iface@if7 UP 10.0.0.1/24 fe80::38d0:88ff:fe77:b7cd/64
You can now freely debug the network activity of your test and the
responses from the DUT.
### Using the Python Debugger
The built in `breakpoint()` function in Python is very useful when you
want to run a test case to a certain point at which you might want to
interactively inspect either the test's or the device's state.
Simply insert a call to `breakpoint()` at the point of interest in
your test and run it as normal. Once Python executes the call, it will
drop you into the Python debugger:
11:42:58 infamy0:test # ./case/infix_dhcp/dhcp_basic.py
# Starting (2024-02-10 11:42:59)
@@ -382,32 +207,15 @@ First, add a Python `breakpoint()` to your test and run it from your
> /home/jocke/src/infix/test/case/infix_dhcp/dhcp_basic.py(44)<module>()
(Pdb)
You are now in the Python debugger, Pdb.
At this point you have full access to the test's state, but it is also
an opportunity to inspect the state of the DUTs (e.g. via their
console or over SSH).
### Debug a Test in a MacVLAN
It is also possible to run a test under Pdb from the get-go, if you
want to setup breakpoints without modifying the source, or simply step
through the code:
Most tests use some sort of network connection to the DUTs. From a test
shell you cannot see or debug that connection by default. So you need
to employ a little trick.
Open another terminal, and start a test shell in your Infamy instance:
$ ./test/shell
11:43:19 infamy0:test #
MacVLAN interfaces created by Infamy run in another network namespace,
to enter it we can look for a sleeper process:
11:43:20 infamy0:test # nsenter -n U -t $(pidof sleep infinity)
You are now one step further down:
infamy0:/home/jocke/src/infix/test# ip -br a
lo UNKNOWN 127.0.0.1/8 ::1/128
iface@if7 UP 10.0.0.1/24 fe80::38d0:88ff:fe77:b7cd/64
You can now freely debug the network activity of your test and the
responses from the DUT.
11:42:58 infamy0:test # python -m pdb case/infix_dhcp/dhcp_basic.py
### Deterministic Topology Mappings
@@ -451,4 +259,3 @@ the whole suite) with identical topology mappings:
[9PM]: https://github.com/rical/9pm
[Qeneth]: https://github.com/wkz/qeneth
[TAP]: https://testanything.org/
+1 -1
View File
@@ -10,7 +10,7 @@ GENCERT_SITE = $(BR2_EXTERNAL_INFIX_PATH)/src/gencert
GENCERT_LICENSE = ISC
GENCERT_LICENSE_FILES = LICENSE
GENCERT_REDISTRIBUTE = NO
GENCERT_DEPENDENCIES = openssl
GENCERT_DEPENDENCIES = host-pkgconf libopenssl
GENCERT_AUTORECONF = YES
define GENCERT_CONF_ENV
@@ -0,0 +1 @@
task [2] run-parts /cfg/user-scripts.d -- Running user startup scripts
@@ -1,3 +1,20 @@
From 50fafdc63dc3d6152e2494cc693871e9bd04bfe5 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Tue, 5 Mar 2024 09:41:46 +0100
Subject: [PATCH 1/2] iplink_bridge: add mcast_flood_always bridge option
Organization: Addiva Elektronik
- Break out boolopt handling to simplify parsing and setting
- Add set/get support for mcast_flood_always
- Add get support for mst_enabled
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
include/uapi/linux/if_bridge.h | 1 +
ip/iplink_bridge.c | 75 +++++++++++++++++++++++++---------
man/man8/ip-link.8.in | 12 ++++++
3 files changed, 68 insertions(+), 20 deletions(-)
diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index adb75f7d..611ab413 100644
--- a/include/uapi/linux/if_bridge.h
@@ -153,3 +170,6 @@ index 6764a9ad..8f77c5ad 100644
.BI mcast_snooping " MULTICAST_SNOOPING "
- turn multicast snooping on
.RI ( MULTICAST_SNOOPING " > 0) "
--
2.34.1
@@ -0,0 +1,31 @@
From 226a8b532fc074165ba76d71c4488629daed2625 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Mon, 20 May 2024 21:35:11 +0200
Subject: [PATCH 2/2] ipaddress: accept symbolic names also for show
Organization: Addiva Elektronik
This is a follow-up to 709063e, which in turn was a follow-up to
bdb8d85, to add support for 'ip addr show proto static', where
'static' is defined in /etc/iproute2/rt_addrprotos.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
ip/ipaddress.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index e536912f..19900157 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -2217,7 +2217,7 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
__u8 proto;
NEXT_ARG();
- if (get_u8(&proto, *argv, 0))
+ if (rtnl_addrprot_a2n(&proto, *argv))
invarg("\"proto\" value is invalid\n", *argv);
filter.have_proto = true;
filter.proto = proto;
--
2.34.1
@@ -1,7 +1,7 @@
From 79abf7872e35080ca5b519bb4ad2acc1fbd96e8c Mon Sep 17 00:00:00 2001
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Thu, 25 Jan 2024 21:36:59 +0100
Subject: [PATCH 01/28] net: phy: add support for PHY LEDs polarity modes
Subject: [PATCH 01/30] net: phy: add support for PHY LEDs polarity modes
Organization: Addiva Elektronik
Add support for PHY LEDs polarity modes. Some PHY require LED to be set
@@ -22,7 +22,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://lore.kernel.org/r/20240125203702.4552-4-ansuelsmth@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/net/phy/phy_device.c | 16 ++++++++++++++++
include/linux/phy.h | 22 ++++++++++++++++++++++
@@ -1,7 +1,7 @@
From 424ddb5d5d8ce06c578cc08cbedf519c42dd8b69 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Mon, 4 Dec 2023 11:08:11 +0100
Subject: [PATCH 02/28] net: mvmdio: Support setting the MDC frequency on XSMI
Subject: [PATCH 02/30] net: mvmdio: Support setting the MDC frequency on XSMI
controllers
Organization: Addiva Elektronik
@@ -14,7 +14,6 @@ Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Tested-by: Andrew Lunn <andrew@lunn.ch>
Link: https://lore.kernel.org/r/20231204100811.2708884-4-tobias@waldekranz.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
drivers/net/ethernet/marvell/mvmdio.c | 44 +++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
@@ -1,7 +1,7 @@
From 337cf21833cef6ab456a8562338de88ab1a84ead Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Thu, 14 Dec 2023 14:50:23 +0100
Subject: [PATCH 03/28] net: dsa: mv88e6xxx: Create API to read a single stat
Subject: [PATCH 03/30] net: dsa: mv88e6xxx: Create API to read a single stat
counter
Organization: Addiva Elektronik
@@ -16,7 +16,6 @@ Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
drivers/net/dsa/mv88e6xxx/chip.c | 162 ++++++++++++++++++-------------
drivers/net/dsa/mv88e6xxx/chip.h | 27 +++---
@@ -1,7 +1,7 @@
From dbdbfd57998b4f8224146ac94cd5d0a577326087 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Thu, 14 Dec 2023 14:50:25 +0100
Subject: [PATCH 04/28] net: dsa: mv88e6xxx: Give each hw stat an ID
Subject: [PATCH 04/30] net: dsa: mv88e6xxx: Give each hw stat an ID
Organization: Addiva Elektronik
With the upcoming standard counter group support, we are no longer
@@ -16,7 +16,6 @@ Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
drivers/net/dsa/mv88e6xxx/chip.c | 138 +++++++++++++++++--------------
1 file changed, 75 insertions(+), 63 deletions(-)
@@ -1,7 +1,7 @@
From 26815572016d9851ec6362f23746035fb933acf3 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Thu, 14 Dec 2023 14:50:26 +0100
Subject: [PATCH 05/28] net: dsa: mv88e6xxx: Add "eth-mac" counter group
Subject: [PATCH 05/30] net: dsa: mv88e6xxx: Add "eth-mac" counter group
support
Organization: Addiva Elektronik
@@ -12,7 +12,6 @@ Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
drivers/net/dsa/mv88e6xxx/chip.c | 39 ++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
@@ -1,7 +1,7 @@
From cc5badac76e76692eb516a5c086fcf54a5a93080 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Thu, 14 Dec 2023 14:50:27 +0100
Subject: [PATCH 06/28] net: dsa: mv88e6xxx: Limit histogram counters to
Subject: [PATCH 06/30] net: dsa: mv88e6xxx: Limit histogram counters to
ingress traffic
Organization: Addiva Elektronik
@@ -27,7 +27,6 @@ Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
drivers/net/dsa/mv88e6xxx/chip.c | 6 +++---
drivers/net/dsa/mv88e6xxx/global1.c | 7 +++----
@@ -1,7 +1,7 @@
From 35ecc84237bfccbe5b93c0aef6c07cc506d43070 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Thu, 14 Dec 2023 14:50:28 +0100
Subject: [PATCH 07/28] net: dsa: mv88e6xxx: Add "rmon" counter group support
Subject: [PATCH 07/30] net: dsa: mv88e6xxx: Add "rmon" counter group support
Organization: Addiva Elektronik
Report the applicable subset of an mv88e6xxx port's counters using
@@ -11,7 +11,6 @@ Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
drivers/net/dsa/mv88e6xxx/chip.c | 42 ++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
@@ -1,13 +1,13 @@
From 7750d0a5bfae46ab783e8e7087b70175860f808c Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Thu, 16 Nov 2023 19:44:32 +0100
Subject: [PATCH 09/28] net: dsa: mv88e6xxx: Add LED infrastructure
Subject: [PATCH 09/30] net: dsa: mv88e6xxx: Add LED infrastructure
Organization: Addiva Elektronik
Parse DT for LEDs and register them for devices that support it,
though no actual implementations exist yet.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/net/dsa/mv88e6xxx/Makefile | 1 +
drivers/net/dsa/mv88e6xxx/chip.c | 5 +-
@@ -1,7 +1,7 @@
From 63fcd626fdb108d2ac07cb0aa16b91fc43894bff Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Thu, 16 Nov 2023 21:59:35 +0100
Subject: [PATCH 10/28] net: dsa: mv88e6xxx: Add LED support for 6393X
Subject: [PATCH 10/30] net: dsa: mv88e6xxx: Add LED support for 6393X
Organization: Addiva Elektronik
Trigger support:
@@ -9,7 +9,7 @@ Trigger support:
- "timer"
- "netdev"
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/net/dsa/mv88e6xxx/chip.c | 1 +
drivers/net/dsa/mv88e6xxx/leds.c | 226 +++++++++++++++++++++++++++++++
@@ -1,7 +1,7 @@
From 0bc6f19d18d7c0e374c824bcc86433b04210a378 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Tue, 12 Mar 2024 10:27:24 +0100
Subject: [PATCH 11/28] net: dsa: mv88e6xxx: Fix timeout on waiting for PPU on
Subject: [PATCH 11/30] net: dsa: mv88e6xxx: Fix timeout on waiting for PPU on
6393X
Organization: Addiva Elektronik
@@ -9,7 +9,7 @@ In a multi-chip setup, delays of up to 750ms are observed before the
device (6393X) signals completion of PPU initialization (Global 1,
register 0, bit 15). Therefore, increase the timeout threshold to 1s.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/net/dsa/mv88e6xxx/chip.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
@@ -1,7 +1,7 @@
From fe60e3ad1e3b43a924c311658b15a1106e813661 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Tue, 16 Jan 2024 16:00:55 +0100
Subject: [PATCH 12/28] net: dsa: Support MDB memberships whose L2 addresses
Subject: [PATCH 12/30] net: dsa: Support MDB memberships whose L2 addresses
overlap
Organization: Addiva Elektronik
@@ -30,7 +30,7 @@ needed to do this is already in place, since it is also needed on CPU
and DSA ports. Thus, "implement" this by simply removing the guards
which previously skipped reference countung on user ports.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
net/dsa/switch.c | 16 ----------------
1 file changed, 16 deletions(-)
@@ -1,7 +1,7 @@
From 5f5bf4a50e98c5aff05fe85994a981253ae85987 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Tue, 19 Sep 2023 18:38:10 +0200
Subject: [PATCH 13/28] net: phy: marvell10g: Support firmware loading on
Subject: [PATCH 13/30] net: phy: marvell10g: Support firmware loading on
88X3310
Organization: Addiva Elektronik
@@ -12,7 +12,7 @@ We have no choice but to bail out of the probe if firmware is not
available, as the device does not have any built-in image on which to
fall back.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/net/phy/marvell10g.c | 161 +++++++++++++++++++++++++++++++++++
1 file changed, 161 insertions(+)
@@ -1,7 +1,7 @@
From 27d5241d3ddf70d65af5f4dd029f99f0fea866a5 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Tue, 21 Nov 2023 20:15:24 +0100
Subject: [PATCH 14/28] net: phy: marvell10g: Fix power-up when strapped to
Subject: [PATCH 14/30] net: phy: marvell10g: Fix power-up when strapped to
start powered down
Organization: Addiva Elektronik
@@ -9,7 +9,7 @@ On devices which are hardware strapped to start powered down (PDSTATE
== 1), make sure that we clear the power-down bit on all units
affected by this setting.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/net/phy/marvell10g.c | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
@@ -1,7 +1,7 @@
From e389ee690ce56ef20863aeabdf389bed842d7f42 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Wed, 15 Nov 2023 20:58:42 +0100
Subject: [PATCH 15/28] net: phy: marvell10g: Add LED support for 88X3310
Subject: [PATCH 15/30] net: phy: marvell10g: Add LED support for 88X3310
Organization: Addiva Elektronik
Pickup the LEDs from the state in which the hardware reset or
@@ -18,7 +18,7 @@ Trigger support:
- "netdev": Offload link or duplex information to the solid behavior;
tx and/or rx activity to blink behavior.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/net/phy/marvell10g.c | 422 +++++++++++++++++++++++++++++++++++
1 file changed, 422 insertions(+)
@@ -1,7 +1,7 @@
From 3113b64290b5b7b20c294535c12ea4c732a02f51 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Tue, 12 Dec 2023 09:51:05 +0100
Subject: [PATCH 16/28] net: phy: marvell10g: Support LEDs tied to a single
Subject: [PATCH 16/30] net: phy: marvell10g: Support LEDs tied to a single
media side
Organization: Addiva Elektronik
@@ -15,7 +15,7 @@ the offloading of the "netdev" trigger, such that LEDs attached to the
RJ45 jack only lights up when a copper link is established, and vice
versa for the SFP cage.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/net/phy/marvell10g.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
@@ -1,7 +1,7 @@
From 2570c2350d3d614a31e14dd87d0e99e8fcb5096b Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Fri, 24 Nov 2023 23:29:55 +0100
Subject: [PATCH 17/28] nvmem: layouts: onie-tlv: Let device probe even when
Subject: [PATCH 17/30] nvmem: layouts: onie-tlv: Let device probe even when
TLV is invalid
Organization: Addiva Elektronik
@@ -14,7 +14,7 @@ be successfully probed.
Therefore, settle for reporting data corruption issues in the log, and
simply refrain from registering any cells in those cases.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/nvmem/layouts/onie-tlv.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
@@ -1,7 +1,7 @@
From 681ce95c15bff94ac83eb26db3c718594f8dfa16 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Mon, 4 Mar 2024 16:47:28 +0100
Subject: [PATCH 18/28] net: bridge: avoid classifying unknown multicast as
Subject: [PATCH 18/30] net: bridge: avoid classifying unknown multicast as
mrouters_only
Organization: Addiva Elektronik
@@ -16,6 +16,7 @@ Because a multicast router should always receive both known and unknown
multicast.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
include/uapi/linux/if_bridge.h | 1 +
net/bridge/br.c | 5 +++++
@@ -1,7 +1,7 @@
From 2482848726a5b8185a04d90891693a1713f894b5 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Tue, 5 Mar 2024 06:44:41 +0100
Subject: [PATCH 19/28] net: bridge: Ignore router ports when forwarding L2
Subject: [PATCH 19/30] net: bridge: Ignore router ports when forwarding L2
multicast
Organization: Addiva Elektronik
@@ -1,10 +1,10 @@
From 25efcdb13ae720ee662f4b6091b270e48ac4d375 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Thu, 21 Mar 2024 19:12:15 +0100
Subject: [PATCH 20/28] net: dsa: Support EtherType based priority overrides
Subject: [PATCH 20/30] net: dsa: Support EtherType based priority overrides
Organization: Addiva Elektronik
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
include/net/dsa.h | 4 ++++
net/dsa/slave.c | 56 +++++++++++++++++++++++++++++++++++++++++++++--
@@ -1,11 +1,11 @@
From 213e9a1d9145723897fbba40fc4afc5b5f70c1de Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Fri, 22 Mar 2024 16:15:43 +0100
Subject: [PATCH 21/28] net: dsa: mv88e6xxx: Support EtherType based priority
Subject: [PATCH 21/30] net: dsa: mv88e6xxx: Support EtherType based priority
overrides
Organization: Addiva Elektronik
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/net/dsa/mv88e6xxx/chip.c | 64 +++++++++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/chip.h | 21 ++++++++++
@@ -1,7 +1,7 @@
From 1c1c81864c9803831c2d5b7df0d7da28e566f3b2 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Wed, 27 Mar 2024 10:10:19 +0100
Subject: [PATCH 22/28] net: phy: Do not resume PHY when attaching
Subject: [PATCH 22/30] net: phy: Do not resume PHY when attaching
Organization: Addiva Elektronik
The PHY should not start negotiating with its link-partner until
@@ -16,7 +16,7 @@ probing (e.g. DSA) would end up with a physical link being
established, even though the corresponding interface was still
administratively down.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/net/phy/phy_device.c | 1 -
1 file changed, 1 deletion(-)
@@ -1,7 +1,7 @@
From b53e5007cd5be42eb5d9a510d63f89c7c6edd9b6 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Wed, 27 Mar 2024 15:52:43 +0100
Subject: [PATCH 23/28] net: dsa: mv88e6xxx: Improve indirect register access
Subject: [PATCH 23/30] net: dsa: mv88e6xxx: Improve indirect register access
perf on 6393
Organization: Addiva Elektronik
@@ -14,7 +14,7 @@ Therefore, add a new set of SMI operations which remaps accesses to
such registers to the corresponding directly addressable register. All
other accesses use the regular indirect interface.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/net/dsa/mv88e6xxx/chip.c | 7 +++
drivers/net/dsa/mv88e6xxx/global1.h | 3 ++
@@ -1,7 +1,7 @@
From f53652205b1c044fa00f896d6422c82ac808f16a Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Thu, 4 Apr 2024 16:36:30 +0200
Subject: [PATCH 24/28] net: bridge: drop delay for applying strict multicast
Subject: [PATCH 24/30] net: bridge: drop delay for applying strict multicast
filtering
Organization: Addiva Elektronik
@@ -19,6 +19,7 @@ A proper fix for upstreaming could be to add a knob to disable the delay.
[2]: https://lore.kernel.org/netdev/20240127175033.9640-1-linus.luessing@c0d3.blue/
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
net/bridge/br_multicast.c | 42 +++++++--------------------------------
net/bridge/br_private.h | 4 +---
@@ -1,7 +1,7 @@
From 56208d4cba23496c09f69961a32b1b9662f06aef Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Mon, 22 Apr 2024 23:18:01 +0200
Subject: [PATCH 25/28] net: dsa: mv88e6xxx: Honor ports being managed via
Subject: [PATCH 25/30] net: dsa: mv88e6xxx: Honor ports being managed via
in-band-status
Organization: Addiva Elektronik
@@ -14,7 +14,7 @@ This state is the default set up by mv88e6xxx_port_setup_mac(), so all
we have to do is to make the phylink MAC callbacks no-ops in cases
when in-band-status is being used.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/net/dsa/mv88e6xxx/chip.c | 6 ++++++
1 file changed, 6 insertions(+)
@@ -1,7 +1,7 @@
From 04023a449a6894c0ec46e246c52e94566b3be4b3 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Wed, 24 Apr 2024 21:35:26 +0200
Subject: [PATCH 26/28] net: dsa: mv88e6xxx: Fix port policy config on 6393X
Subject: [PATCH 26/30] net: dsa: mv88e6xxx: Fix port policy config on 6393X
Organization: Addiva Elektronik
mv88e6393x_port_policy_{read,write} expect the `pointer` argument to
@@ -16,7 +16,7 @@ never enabled on DSA ports, which broke standalone port isolation in
multichip switch trees made up of 6393X decices.
Fixes: 6584b26020fc ("net: dsa: mv88e6xxx: implement .port_set_policy for Amethyst")
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/net/dsa/mv88e6xxx/port.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
@@ -1,7 +1,7 @@
From 7529102af1693138349032b7d1339d91d2b6a578 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Wed, 24 Apr 2024 22:41:04 +0200
Subject: [PATCH 27/28] net: dsa: mv88e6xxx: Limit rsvd2cpu policy to user
Subject: [PATCH 27/30] net: dsa: mv88e6xxx: Limit rsvd2cpu policy to user
ports on 6393X
Organization: Addiva Elektronik
@@ -30,7 +30,7 @@ switch would try to trap it back to the CPU. Given that the CPU is
trusted, instead assume that it indeed meant for the packet to be
forwarded like any other.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/net/dsa/mv88e6xxx/port.c | 31 +++++++++++++++++++++++++------
1 file changed, 25 insertions(+), 6 deletions(-)
@@ -1,7 +1,7 @@
From c446111df33d9d6064395e37235bbbeee422358e Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Mon, 29 Apr 2024 15:14:51 +0200
Subject: [PATCH 28/28] usb: core: adjust log level for unauthorized devices
Subject: [PATCH 28/30] usb: core: adjust log level for unauthorized devices
Organization: Addiva Elektronik
The fact that a USB device currently is not authorized is not an error,
@@ -9,6 +9,7 @@ so let's adjust the log level so these messages slip below radar for the
commonly used 'quiet' log level.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/usb/core/driver.c | 4 ++--
drivers/usb/core/generic.c | 2 +-
@@ -0,0 +1,52 @@
From eeeab4de57f77712ca5f920e03a68aa5e426784c Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Wed, 15 May 2024 13:50:58 +0200
Subject: [PATCH 29/30] net: dsa: mv88e6xxx: Grab register lock during counter
snapshotting
Organization: Addiva Elektronik
This was missing for the standard counter groups. Since no caller
already holds the lock, opt for pushing the locking down into
mv88e6xxx_stats_snapshot() rather than having it duplicated at each
call site.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/net/dsa/mv88e6xxx/chip.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 29691280cff4..4916d8344a5a 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -950,10 +950,15 @@ static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port,
static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
{
+ int err;
+
if (!chip->info->ops->stats_snapshot)
return -EOPNOTSUPP;
- return chip->info->ops->stats_snapshot(chip, port);
+ mv88e6xxx_reg_lock(chip);
+ err = chip->info->ops->stats_snapshot(chip, port);
+ mv88e6xxx_reg_unlock(chip);
+ return err;
}
#define MV88E6XXX_HW_STAT_MAPPER(_fn) \
@@ -1325,10 +1330,7 @@ static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
struct mv88e6xxx_chip *chip = ds->priv;
int ret;
- mv88e6xxx_reg_lock(chip);
-
ret = mv88e6xxx_stats_snapshot(chip, port);
- mv88e6xxx_reg_unlock(chip);
if (ret < 0)
return;
--
2.34.1
@@ -0,0 +1,109 @@
From 52eabcd2cfb30013d43ef72c2ec09c76043a0f4a Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Thu, 16 May 2024 14:51:54 +0200
Subject: [PATCH 30/30] net: bridge: Differentiate MDB additions from
modifications
Organization: Addiva Elektronik
Before this change, the reception of an IGMPv3 report (and analogously
for MLDv2) that adds a new group, would trigger two MDB RTM_NEWMDB
notifications from br_ip4_multicast_igmp3_report():
1. In br_ip4_multicast_add_group() when creating the entry
2. Directly at the end of br_ip4_multicast_igmp3_report(), as the new
group is also deemed to be "changed"
The corresponding switchdev notifications generated by these present a
problem for drivers wanting to reference count group memberships,
since logically there is only a single reference to the group. Indeed,
when the membership eventually times out, only a single RTM_DELMDB is
generated.
Therefore, discriminate new groups from changes to existing groups by
introducing a RTM_SETMDB events to be used in the latter scenario.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
include/uapi/linux/rtnetlink.h | 2 ++
net/bridge/br_mdb.c | 4 ++--
net/bridge/br_multicast.c | 8 ++++----
3 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
index 51c13cf9c5ae..fbc8fd1d5f2c 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -134,6 +134,8 @@ enum {
#define RTM_DELMDB RTM_DELMDB
RTM_GETMDB = 86,
#define RTM_GETMDB RTM_GETMDB
+ RTM_SETMDB = 87,
+#define RTM_SETMDB RTM_SETMDB
RTM_NEWNSID = 88,
#define RTM_NEWNSID RTM_NEWNSID
diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c
index 7305f5f8215c..a2882b126ecb 100644
--- a/net/bridge/br_mdb.c
+++ b/net/bridge/br_mdb.c
@@ -724,7 +724,7 @@ static int br_mdb_replace_group_sg(const struct br_mdb_config *cfg,
else
del_timer(&pg->timer);
- br_mdb_notify(cfg->br->dev, mp, pg, RTM_NEWMDB);
+ br_mdb_notify(cfg->br->dev, mp, pg, RTM_SETMDB);
return 0;
}
@@ -945,7 +945,7 @@ static int br_mdb_replace_group_star_g(const struct br_mdb_config *cfg,
else
del_timer(&pg->timer);
- br_mdb_notify(cfg->br->dev, mp, pg, RTM_NEWMDB);
+ br_mdb_notify(cfg->br->dev, mp, pg, RTM_SETMDB);
if (br_multicast_should_handle_mode(brmctx, cfg->group.proto))
br_multicast_star_g_handle_mode(pg, cfg->filter_mode);
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 8fcd8faf99f3..7ba1eee1a024 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -618,7 +618,7 @@ static void br_multicast_fwd_src_handle(struct net_bridge_group_src *src)
sg_mp = br_mdb_ip_get(src->br, &sg_key.addr);
if (!sg_mp)
return;
- br_mdb_notify(src->br->dev, sg_mp, sg, RTM_NEWMDB);
+ br_mdb_notify(src->br->dev, sg_mp, sg, RTM_SETMDB);
}
}
@@ -885,7 +885,7 @@ static void br_multicast_port_group_expired(struct timer_list *t)
if (WARN_ON(!mp))
goto out;
- br_mdb_notify(br->dev, mp, pg, RTM_NEWMDB);
+ br_mdb_notify(br->dev, mp, pg, RTM_SETMDB);
}
out:
spin_unlock(&br->multicast_lock);
@@ -2888,7 +2888,7 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge_mcast *brmctx,
break;
}
if (changed)
- br_mdb_notify(brmctx->br->dev, mdst, pg, RTM_NEWMDB);
+ br_mdb_notify(brmctx->br->dev, mdst, pg, RTM_SETMDB);
unlock_continue:
spin_unlock(&brmctx->br->multicast_lock);
}
@@ -3030,7 +3030,7 @@ static int br_ip6_multicast_mld2_report(struct net_bridge_mcast *brmctx,
break;
}
if (changed)
- br_mdb_notify(brmctx->br->dev, mdst, pg, RTM_NEWMDB);
+ br_mdb_notify(brmctx->br->dev, mdst, pg, RTM_SETMDB);
unlock_continue:
spin_unlock(&brmctx->br->multicast_lock);
}
--
2.34.1
+1 -1
View File
@@ -226,7 +226,7 @@ sysrepoctl -s $SEARCH \
-i infix-dhcp-client@2024-04-12.yang -g wheel -p 0660 \
-i infix-shell-type@2023-08-21.yang -g wheel -p 0660 \
-i infix-system@2024-04-12.yang -g wheel -p 0660 \
-i infix-services@2024-04-08.yang -g wheel -p 0660 \
-i infix-services@2024-05-21.yang -g wheel -p 0660 \
-i ieee802-ethernet-interface@2019-06-21.yang -g wheel -p 0660 \
-i infix-ethernet-interface@2024-02-27.yang -g wheel -p 0660 \
-I "${INIT_DATA}"
+7
View File
@@ -185,6 +185,13 @@ if [ -n "$bridge" ]; then
},
"ietf-ip:ipv6": {
"enabled": true
},
"infix-interfaces:bridge": {
"multicast": {
"snooping": true,
"querier": "auto",
"query-interval": 125
}
}
EOF
fi
+14 -3
View File
@@ -1376,11 +1376,22 @@ static int netdag_gen_veth(struct dagger *net, struct lyd_node *dif,
if (dagger_should_skip(net, ifname)) {
err = dagger_add_dep(net, ifname, peer);
if (err)
return ERR_IFACE(cif, err, "Unable to add dep \"%s\" to %s",
peer, ifname);
return ERR_IFACE(cif, err, "Unable to add dep \"%s\" to %s", peer, ifname);
} else {
char ifname_args[64] = "", peer_args[64] = "";
dagger_skip_iface(net, peer);
fprintf(ip, "link add dev %s down type veth peer %s\n", ifname, peer);
node = lydx_get_child(dif, "phys-address");
if (node)
snprintf(ifname_args, sizeof(ifname_args), "address %s", lyd_get_value(node));
node = lydx_find_by_name(lyd_parent(cif), "interface", peer);
if (node && (node = lydx_get_child(node, "phys-address")))
snprintf(peer_args, sizeof(peer_args), "address %s", lyd_get_value(node));
fprintf(ip, "link add dev %s %s type veth peer %s %s\n",
ifname, ifname_args, peer, peer_args);
}
return 0;
+19 -1
View File
@@ -25,6 +25,7 @@
SVC(web) \
SVC(ttyd) \
SVC(netbrowse) \
SVC(userscripts) \
SVC(all) /* must be last entry */
typedef enum {
@@ -52,7 +53,7 @@ struct mdns_svc {
};
static const struct srx_module_requirement reqs[] = {
{ .dir = YANG_PATH_, .name = "infix-services", .rev = "2024-04-08" },
{ .dir = YANG_PATH_, .name = "infix-services", .rev = "2024-05-21" },
{ .dir = YANG_PATH_, .name = "ieee802-dot1ab-lldp", .rev = "2022-03-15" },
{ .dir = YANG_PATH_, .name = "infix-lldp", .rev = "2023-08-23" },
{ NULL }
@@ -291,6 +292,21 @@ static int web_change(sr_session_ctx_t *session, uint32_t sub_id, const char *mo
return put(cfg, srv);
}
static int userscripts_change(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
const char *xpath, sr_event_t event, unsigned request_id, void *_confd)
{
struct lyd_node *srv = NULL;
sr_data_t *cfg;
cfg = get(session, event, xpath, &srv, "user-scripts", NULL);
if (!cfg)
return SR_ERR_OK;
svc_enadis(lydx_is_enabled(srv, "enabled"), none, "user-scripts");
return put(cfg, srv);
}
int infix_services_init(struct confd *confd)
{
int rc;
@@ -312,6 +328,8 @@ int infix_services_init(struct confd *confd)
0, netbrowse_change, confd, &confd->sub);
REGISTER_CHANGE(confd->session, "ieee802-dot1ab-lldp", "/ieee802-dot1ab-lldp:lldp",
0, lldp_change, confd, &confd->sub);
REGISTER_CHANGE(confd->session, "infix-services", "/infix-services:user-scripts",
0, userscripts_change, confd, &confd->sub);
return SR_ERR_OK;
fail:
@@ -7,6 +7,10 @@ module infix-services {
contact "kernelkit@googlegroups.com";
description "Infix services, generic.";
revision 2024-05-21 {
description "Add support for user-scripts.";
reference "internal";
}
revision 2024-04-08 {
description "Initial support for web services.";
reference "internal";
@@ -33,6 +37,19 @@ module infix-services {
}
}
container user-scripts {
description "Run scripts in /cfg/user-scripts.d on system startup
CAUTION! Anyone who can write to this directory will be able to
install a permanent backdoor on the system. This could include
a remote attacker with brief window of access.";
leaf enabled {
description "Enable or disable user script execution";
type boolean;
}
}
container web {
description "Web services";
+15 -10
View File
@@ -2,28 +2,33 @@
# shellcheck disable=SC2034,SC2154
# Current container image
INFIX_TEST=ghcr.io/kernelkit/infix-test:1.3
INFIX_TEST=ghcr.io/kernelkit/infix-test:1.4
ixdir=$(readlink -f "$testdir/..")
logdir=$(readlink -f "$testdir/.log")
envdir="$HOME/.infix-test-venv"
qeneth="$testdir/qeneth/qeneth"
runners="podman docker"
build_ps1()
{
echo "\e[1m\$(date \"+%H:%M:%S\") \h$1\e[0m:\W # "
}
#
# Figure out available container runner
#
runner()
{
if which podman >/dev/null; then
runner=podman
elif which docker >/dev/null; then
runner=docker
else
echo "Error: Neither podman or docker is installed, we recommend podman."
exit 1
fi
for r in $runners; do
which $r >/dev/null && {
echo $r
return
}
done
echo "$runner"
echo "ERROR: No container manager found (tried \"$runners\")" >&2
exit 1
}
#
+22 -33
View File
@@ -1,13 +1,13 @@
#!/usr/bin/env python3
# ,------------------------------------------------,
# | [TARGET] |
# | br0 |
# | / \ |
# | target:mgmt target:data0 targetgt:data1 |
# ,------------------------------------------------,
# | [TARGET] |
# | |
# | |
# | target:mgmt target:data0 targetgt:data1 |
# | [192.168.0.1] [10.0.0.1] |
# '------------------------------------------------'
# | | |
# | | |
# | | |
# | | |
# ,----------------------------------------------,
# | host:mgmt host:data0 host:data1 |
# | [192.168.0.10] [10.0.0.10] |
@@ -52,45 +52,34 @@ with infamy.Test() as test:
with test.step("Initialize"):
env = infamy.Env(infamy.std_topology("1x3"))
target = env.attach("target", "mgmt")
with test.step("Configure target physical ports [enable routing]"):
_, tport0 = env.ltop.xlate("target", "data0")
_, tport1 = env.ltop.xlate("target", "data1")
config_target(target, tport0, tport1, True)
with test.step("Test: Ip routing enabled"):
_, hport0 = env.ltop.xlate("host", "data0")
_, hport1 = env.ltop.xlate("host", "data1")
with infamy.IsolatedMacVlan(hport0) as ns0, \
infamy.IsolatedMacVlan(hport1) as ns1 :
with infamy.IsolatedMacVlan(hport0) as ns0, \
infamy.IsolatedMacVlan(hport1) as ns1 :
with test.step("Setup host"):
ns0.addip("192.168.0.10")
ns0.addroute("default", "192.168.0.1")
ns1.addip("10.0.0.10")
ns1.addroute("default", "10.0.0.1")
with test.step("Enable forwarding"):
config_target(target, tport0, tport1, True)
with test.step("Traffic is forwarded"):
ns0.must_reach("10.0.0.10")
ns1.must_reach("192.168.0.10")
with test.step("Configure target physical ports [disable routing]"):
with test.step("Disable forwarding"):
config_target(target, tport0, tport1, False)
config_target(target, tport0, tport1, False)
with test.step("Test: Ip routing disabled"):
with infamy.IsolatedMacVlan(hport0) as ns0, \
infamy.IsolatedMacVlan(hport1) as ns1 :
ns0.addip("192.168.0.10")
ns0.addroute("default", "192.168.0.1")
ns1.addip("10.0.0.10")
ns1.addroute("default", "10.0.0.1")
ns0.must_not_reach("10.0.0.10")
ns1.must_not_reach("192.168.0.10")
with test.step("Traffic is not forwarded"):
infamy.parallel(lambda: ns0.must_not_reach("10.0.0.10"),
lambda: ns1.must_not_reach("192.168.0.10"))
test.succeed()
+15 -19
View File
@@ -188,7 +188,7 @@ def config_remove_routes(target):
new = copy.deepcopy(running)
new["routing"]["control-plane-protocols"].clear()
target.put_diff_dicts("ietf-routing",running,new)
with infamy.Test() as test:
with test.step("Initialize"):
env = infamy.Env(infamy.std_topology("2x2"))
@@ -214,31 +214,27 @@ with infamy.Test() as test:
until(lambda: route.ipv6_route_exist(target1, "2001:db8:3c4d:200::1/128"))
until(lambda: route.ipv6_route_exist(target2, "::/0"))
with test.step("Ping from host to 192.168.200.1 and 2001:db8:3c4d:200::1 (dut2) through dut1"):
_, hport0 = env.ltop.xlate("host", "data1")
with infamy.IsolatedMacVlan(hport0) as ns0:
_, hport0 = env.ltop.xlate("host", "data1")
with infamy.IsolatedMacVlan(hport0) as ns0:
with test.step("Configure host addresses and routes"):
ns0.addip("2001:db8:3c4d:10::2", prefix_length=64, proto="ipv6")
ns0.addroute("2001:db8:3c4d:200::1/128", "2001:db8:3c4d:10::1", proto="ipv6")
ns0.addip("192.168.10.2")
ns0.addroute("192.168.200.1/32", "192.168.10.1")
with test.step("Verify that dut2 is reachable from host over IPv4"):
ns0.must_reach("192.168.200.1")
with test.step("Remove static routes on dut1"):
config_remove_routes(target1);
until(lambda: route.ipv4_route_exist(target1, "192.168.200.1/32") == False)
until(lambda: route.ipv6_route_exist(target1, "2001:db8:3c4d:200::1/128") == False)
with test.step("Verify that dut2 is reachable from host over IPv6"):
ns0.must_reach("2001:db8:3c4d:200::1")
with test.step("Ping from host to 192.168.200.1 and 2001:db8:3c4d:200::1 (dut2) through dut1 (should not be possible)"):
_, hport0 = env.ltop.xlate("host", "data1")
with infamy.IsolatedMacVlan(hport0) as ns0:
ns0.addip("2001:db8:3c4d:10::2", prefix_length=64, proto="ipv6")
ns0.addroute("2001:db8:3c4d:200::1/128", "2001:db8:3c4d:10::1", proto="ipv6")
ns0.addip("192.168.10.2")
ns0.addroute("192.168.200.1/32", "192.168.10.1")
ns0.must_not_reach("192.168.200.1")
ns0.must_not_reach("2001:db8:3c4d:200::1")
with test.step("Remove static routes on dut1"):
config_remove_routes(target1);
until(lambda: route.ipv4_route_exist(target1, "192.168.200.1/32") == False)
until(lambda: route.ipv6_route_exist(target1, "2001:db8:3c4d:200::1/128") == False)
with test.step("Verify that dut2 is no longer reachable"):
infamy.parallel(lambda: ns0.must_not_reach("192.168.200.1"),
lambda: ns0.must_not_reach("2001:db8:3c4d:200::1"))
test.succeed()
@@ -42,7 +42,7 @@ with infamy.Test() as test:
}
})
with test.step(f"Verify {NAME} continer has started"):
with test.step(f"Verify {NAME} container has started"):
c = infamy.Container(target)
until(lambda: c.running(NAME), attempts=10)
+3 -10
View File
@@ -80,7 +80,7 @@ with infamy.Test() as test:
}
})
with test.step(f"Verify {NAME} continer has started"):
with test.step(f"Verify {NAME} container has started"):
c = infamy.Container(target)
until(lambda: c.running(NAME), attempts=10)
@@ -89,15 +89,8 @@ with infamy.Test() as test:
url = infamy.Furl(URL)
with infamy.IsolatedMacVlan(hport) as ns:
connectivity = ns.runsh(f"""
ip link set iface up
ip addr add {OURIP}/24 dev iface
ping -c1 -w5 {DUTIP} || exit 1
""")
if connectivity.returncode:
print(connectivity.stdout)
test.fail()
ns.addip(OURIP)
ns.must_reach(DUTIP)
until(lambda: url.nscheck(ns, "Kilroy was here"), attempts=10)
+3 -10
View File
@@ -59,7 +59,7 @@ with infamy.Test() as test:
}
})
with test.step(f"Verify {NAME} continer has started"):
with test.step(f"Verify {NAME} container has started"):
c = infamy.Container(target)
until(lambda: c.running(NAME), attempts=10)
@@ -68,15 +68,8 @@ with infamy.Test() as test:
url = infamy.Furl(URL)
with infamy.IsolatedMacVlan(hport) as ns:
connectivity = ns.runsh(f"""
ip link set iface up
ip addr add {OURIP}/24 dev iface
ping -c1 -w5 {DUTIP} || exit 1
""")
if connectivity.returncode:
print(connectivity.stdout)
test.fail()
ns.addip(OURIP)
ns.must_reach(DUTIP)
until(lambda: url.nscheck(ns, "It works"), attempts=10)
+3 -10
View File
@@ -83,7 +83,7 @@ with infamy.Test() as test:
}
})
with test.step(f"Verify {NAME} continer has started"):
with test.step(f"Verify {NAME} container has started"):
c = infamy.Container(target)
until(lambda: c.running(NAME), attempts=10)
@@ -92,15 +92,8 @@ with infamy.Test() as test:
url = infamy.Furl(URL)
with infamy.IsolatedMacVlan(hport) as ns:
connectivity = ns.runsh(f"""
ip link set iface up
ip addr add {OURIP}/24 dev iface
ping -c1 -w5 {DUTIP} || exit 1
""")
if connectivity.returncode:
print(connectivity.stdout)
test.fail()
ns.addip(OURIP)
ns.must_reach(DUTIP)
until(lambda: url.nscheck(ns, "It works"), attempts=10)
+2 -12
View File
@@ -46,17 +46,7 @@ with infamy.Test() as test:
_, hport = env.ltop.xlate("host", "data")
with infamy.IsolatedMacVlan(hport) as ns:
pingtest = ns.runsh("""
set -ex
ip link set iface up
ip addr add 10.0.0.1/24 dev iface
ping -c1 -w5 10.0.0.2 || exit 1
""")
if pingtest.returncode:
print(pingtest.stdout)
test.fail()
ns.addip("10.0.0.1")
ns.must_reach("10.0.0.2")
test.succeed()
@@ -1,11 +1,11 @@
#!/usr/bin/env python3
# ,-------------------------------------, ,-------------------------------------,
# | dut1:data2 | | dut2:data2 |
# | br0 ----------|-------|--------- br0 |
# | / \ | | / \ |
# |dut1:mgmt dut1:data0 dut1:data1 | | dut2:data0 dut2:data1 dut2:mgmt |
# ,-------------------------------------, ,-------------------------------------,
# | dut1:data2 | | dut2:data2 |
# | br0 ----------|-------|--------- br0 |
# | / \ | | / \ |
# |dut1:mgmt dut1:data0 dut1:data1 | | dut2:data0 dut2:data1 dut2:mgmt |
# '-------------------------------------' '-------------------------------------'
# | | | | | |
# | | | | | |
# | | | | | |
# ,-----------------------------------------------------------------------------------------,
# | host:mgmt0 host:data0 host:data1 host:data2 host:data3 host:mgmt1 |
@@ -125,7 +125,7 @@ with infamy.Test() as test:
with test.step("Ping host:data20 [10.0.0.3] and host:data21 [10.0.0.4]"\
"from host:data11 [10.0.0.2]"):
_, hport11 = env.ltop.xlate("host", "data11")
_, hport20 = env.ltop.xlate("host", "data20")
_, hport21 = env.ltop.xlate("host", "data21")
@@ -140,5 +140,5 @@ with infamy.Test() as test:
ns11.must_reach("10.0.0.3")
ns11.must_reach("10.0.0.4")
test.succeed()
+2 -12
View File
@@ -65,17 +65,7 @@ with infamy.Test() as test:
_, hport = env.ltop.xlate("host", "data")
with infamy.IsolatedMacVlan(hport) as ns:
pingtest = ns.runsh("""
set -ex
ip link set iface up
ip addr add 10.0.0.1/24 dev iface
ping -c1 -w10 10.0.0.2 || exit 1
""")
if pingtest.returncode:
print(pingtest.stdout)
test.fail()
ns.addip("10.0.0.1")
ns.must_reach("10.0.0.2")
test.succeed()
+3 -13
View File
@@ -117,18 +117,8 @@ with infamy.Test() as test:
_, hport = env.ltop.xlate("host", "data1")
with infamy.IsolatedMacVlan(hport) as ns:
pingtest = ns.runsh("""
set -ex
ip link set iface up
ip addr add 10.0.0.1/24 dev iface
ping -c1 -w5 10.0.0.2 || exit 1
ping -c1 -w5 10.0.0.3 || exit 1
""")
if pingtest.returncode:
print(pingtest.stdout)
test.fail()
ns.addip("10.0.0.1")
ns.must_reach("10.0.0.2")
ns.must_reach("10.0.0.3")
test.succeed()
@@ -1,11 +1,11 @@
#!/usr/bin/env python3
# ,-------------------------------------, ,-------------------------------------,
# | dut1:data2 | | dut2:data2 |
# | br0 ----------|-------|--------- br0 |
# | / \ | | / \ |
# |dut1:mgmt dut1:data0 dut1:data1 | | dut2:data0 dut2:data1 dut2:mgmt |
# ,-------------------------------------, ,-------------------------------------,
# | dut1:data2 | | dut2:data2 |
# | br0 ----------|-------|--------- br0 |
# | / \ | | / \ |
# |dut1:mgmt dut1:data0 dut1:data1 | | dut2:data0 dut2:data1 dut2:mgmt |
# '-------------------------------------' '-------------------------------------'
# | | | | | |
# | | | | | |
# | | | | | |
# ,-----------------------------------------------------------------------------------------,
# | host:mgmt0 host:data0 host:data1 host:data2 host:data3 host:mgmt1 |
@@ -13,7 +13,7 @@
# | (ns10) (ns11) (ns20) (ns21) |
# | |
# | [ HOST ] |
# '-----------------------------------------------------------------------------------------'
# '-----------------------------------------------------------------------------------------'
import infamy
@@ -135,7 +135,7 @@ with infamy.Test() as test:
with test.step("Ping host:data20 [10.0.0.3] from host:data10 [10.0.0.1] through <bridge-vlan-10>" \
" and host:data21 [10.0.0.4] from host:data11 [10.0.0.2] through <bridge-vlan-20>"):
_, hport10 = env.ltop.xlate("host", "data10")
_, hport11 = env.ltop.xlate("host", "data11")
_, hport20 = env.ltop.xlate("host", "data20")
@@ -154,9 +154,9 @@ with infamy.Test() as test:
ns10.must_reach("10.0.0.3")
ns11.must_reach("10.0.0.4")
ns10.must_not_reach("10.0.0.4")
ns11.must_not_reach("10.0.0.3")
ns10.must_not_reach("10.0.0.2")
ns11.must_not_reach("10.0.0.1")
infamy.parallel(lambda: ns10.must_not_reach("10.0.0.4"),
lambda: ns11.must_not_reach("10.0.0.3"),
lambda: ns10.must_not_reach("10.0.0.2"),
lambda: ns11.must_not_reach("10.0.0.1"))
test.succeed()
+2 -12
View File
@@ -73,17 +73,7 @@ with infamy.Test() as test:
_, hport = env.ltop.xlate("host", "data")
with infamy.IsolatedMacVlan(hport) as ns:
pingtest = ns.runsh("""
set -ex
ip link set iface up
ip addr add 10.0.0.1/24 dev iface
ping -c1 -w10 10.0.0.2 || exit 1
""")
if pingtest.returncode:
print(pingtest.stdout)
test.fail()
ns.addip("10.0.0.1")
ns.must_reach("10.0.0.2")
test.succeed()
+38 -40
View File
@@ -1,17 +1,20 @@
#!/usr/bin/env python3
# 10.0.0.1
# +--------------+
# | DUT |
# | |
# +----------+---\
# | | \
# | | \
# | | \
# | | \
# 10.0.0.2 | 10.0.0.3| \ 10.0.0.4
# +----------+ +---+------+ \ +-------------+
# | msend | | mreceive | + no join |
# +-------+ +----------+ +-------------+
#
# Add join the group 224.1.1.1 from data1 on the host. Send to that
# group from `msend`, verify that `mrecv` receives it and that `!memb`
# does not.
#
# .1
# .---------------------------.
# | DUT |
# '-data0-----data1-----data2-'
# | | |
# | | | 10.0.0.0/24
# | | |
# .-data0-. .-data1-. .-data2-.
# | msend | | mrecv | | !memb |
# '-------' '-------' '-------'
# .2 .3 .4
import infamy
import time
@@ -75,13 +78,15 @@ with infamy.Test() as test:
}
})
with test.step("Check multicast receieved on correct port"):
_, hsend = env.ltop.xlate("host", "data0")
_, hreceive = env.ltop.xlate("host", "data1")
_, hnojoin = env.ltop.xlate("host", "data2")
with infamy.IsolatedMacVlan(hsend) as send_ns, \
infamy.IsolatedMacVlan(hreceive) as receive_ns, \
infamy.IsolatedMacVlan(hnojoin) as nojoin_ns:
_, hsend = env.ltop.xlate("host", "data0")
_, hreceive = env.ltop.xlate("host", "data1")
_, hnojoin = env.ltop.xlate("host", "data2")
with infamy.IsolatedMacVlan(hsend) as send_ns, \
infamy.IsolatedMacVlan(hreceive) as receive_ns, \
infamy.IsolatedMacVlan(hnojoin) as nojoin_ns:
with test.step("Setup sender and receivers"):
send_ns.addip("10.0.0.2")
receive_ns.addip("10.0.0.3")
nojoin_ns.addip("10.0.0.4")
@@ -89,25 +94,18 @@ with infamy.Test() as test:
receive_ns.must_reach("10.0.0.1")
nojoin_ns.must_reach("10.0.0.1")
sender = mcast.MCastSender(send_ns, "224.1.1.1")
receiver = mcast.MCastReceiver(receive_ns, "224.1.1.1")
snif_nojoin = infamy.Sniffer(nojoin_ns, "host 224.1.1.1")
snif_receiver = infamy.Sniffer(receive_ns, "host 224.1.1.1")
with sender:
with snif_receiver:
time.sleep(5)
with snif_nojoin:
time.sleep(5)
assert(snif_receiver.packets() != "")
assert(snif_nojoin.packets() != "")
print("As expected, unregistered multicast is received on both ports")
print("Starting sender")
with mcast.MCastSender(send_ns, "224.1.1.1"):
with test.step("Verify that the group is flooded on all ports"):
infamy.parallel(lambda: receive_ns.must_receive("ip dst 224.1.1.1"),
lambda: nojoin_ns.must_receive("ip dst 224.1.1.1"))
with receiver:
with snif_receiver,snif_nojoin:
time.sleep(5)
assert(snif_nojoin.packets() == "")
print("As expected, registered multicast is NOT forwarded to non-member port")
assert(snif_receiver.packets() != "")
print("As expected, registered multicast is forwarded to the member port")
print("Subscribe to the group")
with mcast.MCastReceiver(receive_ns, "224.1.1.1"):
with test.step("Verify that the group is still forwarded to the member"):
receive_ns.must_receive("ip dst 224.1.1.1")
test.succeed()
with test.step("Verify that the group is no longer forwarded to the non-member"):
nojoin_ns.must_not_receive("ip dst 224.1.1.1")
test.succeed()
@@ -1,18 +1,20 @@
#!/usr/bin/env python3
# 10.0.0.1
# +--------------+
# | DUT |
# | |
# +----------+---\
# | | \
# | | \
# | | \
# | | \
# 10.0.0.2 | 10.0.0.3| \ 10.0.0.4
# +----------+ +---+------+ \ +-------------+
# | msend | | Static | + no join |
# +----------+ | MC-filter| +-------------+
# +----------+
#
# Add static filter on DUT for 224.1.1.1 containing only data1. Send
# to that group from `msend`, verify that `mrecv` receives it and that
# `!memb` does not.
#
# .1
# .---------------------------.
# | DUT |
# '-data0-----data1-----data2-'
# | | |
# | | | 10.0.0.0/24
# | | |
# .-data0-. .-data1-. .-data2-.
# | msend | | mrecv | | !memb |
# '-------' '-------' '-------'
# .2 .3 .4
import infamy
import time
@@ -20,32 +22,31 @@ import infamy.multicast as mcast
import infamy.iface as iface
from infamy.util import until
def set_static_multicast_filter(target, address):
target.put_config_dict("ietf-interfaces",
{
"interfaces": {
"interface": [
{
"name": "br0",
"infix-interfaces:bridge": {
"multicast-filters": {
"multicast-filter": [
{
"group": "224.1.1.1",
"ports": [
{
"port": mreceive
}
]
}
],
},
}
def set_static_multicast_filter(target, address, port):
target.put_config_dict("ietf-interfaces", {
"interfaces": {
"interface": [
{
"name": "br0",
"infix-interfaces:bridge": {
"multicast-filters": {
"multicast-filter": [
{
"group": address,
"ports": [
{
"port": port,
}
]
}
})
]
}
],
},
}
}
]
}
})
with infamy.Test() as test:
with test.step("Initialize"):
env = infamy.Env(infamy.std_topology("1x4"))
@@ -54,7 +55,7 @@ with infamy.Test() as test:
_, mreceive = env.ltop.xlate("target", "data1")
_, nojoin = env.ltop.xlate("target", "data2")
with test.step("Configure device"):
with test.step("Configure device without static filter"):
target.put_config_dict("ietf-interfaces",
{
"interfaces": {
@@ -103,13 +104,14 @@ with infamy.Test() as test:
}
})
with test.step("Check multicast receieved on correct port"):
_, hsend = env.ltop.xlate("host", "data0")
_, hreceive = env.ltop.xlate("host", "data1")
_, hnojoin = env.ltop.xlate("host", "data2")
with infamy.IsolatedMacVlan(hsend) as send_ns, \
infamy.IsolatedMacVlan(hreceive) as receive_ns, \
infamy.IsolatedMacVlan(hnojoin) as nojoin_ns:
_, hsend = env.ltop.xlate("host", "data0")
_, hreceive = env.ltop.xlate("host", "data1")
_, hnojoin = env.ltop.xlate("host", "data2")
with infamy.IsolatedMacVlan(hsend) as send_ns, \
infamy.IsolatedMacVlan(hreceive) as receive_ns, \
infamy.IsolatedMacVlan(hnojoin) as nojoin_ns:
with test.step("Setup sender and receivers"):
send_ns.addip("10.0.0.2")
receive_ns.addip("10.0.0.3")
nojoin_ns.addip("10.0.0.4")
@@ -117,26 +119,20 @@ with infamy.Test() as test:
receive_ns.must_reach("10.0.0.1")
nojoin_ns.must_reach("10.0.0.1")
sender = mcast.MCastSender(send_ns, "224.1.1.1")
snif_nojoin = infamy.Sniffer(nojoin_ns, "host 224.1.1.1")
snif_receiver = infamy.Sniffer(receive_ns, "host 224.1.1.1")
with sender:
with snif_receiver:
time.sleep(5)
with snif_nojoin:
time.sleep(5)
assert(snif_receiver.packets() != "")
assert(snif_nojoin.packets() != "")
print("As expected, unregistered multicast is received on both ports")
print("Starting sender")
with mcast.MCastSender(send_ns, "224.1.1.1"):
with test.step("Verify that the group is flooded on all ports"):
infamy.parallel(lambda: receive_ns.must_receive("ip dst 224.1.1.1"),
lambda: nojoin_ns.must_receive("ip dst 224.1.1.1"))
set_static_multicast_filter(target, "224.1.1.1")
with test.step("Install group filter"):
set_static_multicast_filter(target, "224.1.1.1", mreceive)
until(lambda: iface.exist_bridge_multicast_filter(target, "224.1.1.1", mreceive, "br0"))
with snif_receiver,snif_nojoin:
time.sleep(5)
assert(snif_nojoin.packets() == "")
print("As expected, registered multicast is NOT forwarded to non-member port")
assert(snif_receiver.packets() != "")
print("As expected, registered multicast is forwarded to the member port")
with test.step("Verify that the group is still forwarded to the member"):
receive_ns.must_receive("ip dst 224.1.1.1")
with test.step("Verify that the group is no longer forwarded to the non-member"):
nojoin_ns.must_not_receive("ip dst 224.1.1.1")
test.succeed()
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
#
import random
import string
import infamy
def attach(env):
return (env.attach("target", "mgmt"), env.attach("target", "mgmt", "ssh"))
with infamy.Test() as test:
with test.step("Initialize"):
env = infamy.Env(infamy.std_topology("1x1"))
target, tgtssh = attach(env)
cookie = "".join((random.choices(string.ascii_lowercase, k=16)))
script = f"/cfg/user-scripts.d/infamy-user-script-test.sh"
output = f"/tmp/{cookie}.cookie"
with test.step(f"Install {script}"):
tgtssh.runsh(f"""
set -e
mkdir -p $(dirname {script})
printf '#!/bin/sh\necho -n {cookie} >{output}\n' >{script}
chmod +x {script}
""")
with test.step("Enable user scripts and reboot"):
target.put_config_dict("infix-services", {
"user-scripts": {
"enabled": True
}
})
target.copy("running", "startup")
target.reboot()
infamy.util.wait_boot(target) or test.fail()
target, tgtssh = attach(env)
with test.step(f"Verify that {script} has run"):
cat = tgtssh.runsh(f"cat {output}").stdout
assert cat == cookie, f"Read back {repr(cat)}, expected {repr(cookie)}"
with test.step("Disable user scripts and reboot"):
target.put_config_dict("infix-services", {
"user-scripts": {
"enabled": False
}
})
target.copy("running", "startup")
target.reboot()
infamy.util.wait_boot(target) or test.fail()
target, tgtssh = attach(env)
with test.step(f"Verify that {script} has not run"):
exists = tgtssh.run(["test", "-f", f"{output}"]).returncode == 0
assert not exists, f"Unexpectedly found {output}"
with test.step(f"Remove {script}"):
tgtssh.runsh(f"rm {script}")
test.succeed()
+2
View File
@@ -12,12 +12,14 @@ RUN apk add --no-cache \
libc-dev \
libyang-dev \
linux-headers \
openssh-client \
python3-dev \
qemu-img \
qemu-system-x86_64 \
ruby-mustache \
socat \
squashfs-tools \
sshpass \
tcpdump \
tshark \
make
+17 -5
View File
@@ -17,12 +17,17 @@ usage: test/env [<OPTS>] -f <IMAGE> -q <QENETH-DIR> <COMMAND> [<ARGS>...]
-c
Clean up cruft, lingering images, old containers, unused volumes.
Used by GitHub action to prevent issues with runnign tests.
Used by GitHub action to prevent issues with running tests.
-C
Don't containerize the command, run it directly in the current
namespaces
-D
Prefer Docker over podman. This is implied when -t is specified,
as podman does not allow the necessary network permissions to be
granted to the container
-f <IMAGE>
Specify images required for test, squashfs image is required
if testing with kernel. bios and disk image is required if
@@ -47,7 +52,8 @@ usage: test/env [<OPTS>] -f <IMAGE> -q <QENETH-DIR> <COMMAND> [<ARGS>...]
-t <TOPOLOGY>
Rather than starting a qeneth network, attach to this existing
topology. Mostly useful together with -C
topology. If the command is containerized, it will be launched
in the host's network namespace
EOF
}
@@ -111,7 +117,7 @@ containerize=yes
[ -c /dev/kvm ] && kvm="--device=/dev/kvm"
files=
while getopts "cCf:hiKp:q:t:" opt; do
while getopts "cCDf:hiKp:q:t:" opt; do
case ${opt} in
c)
$(runner) image prune -af
@@ -122,6 +128,9 @@ while getopts "cCf:hiKp:q:t:" opt; do
C)
containerize=
;;
D)
runners="docker podman"
;;
f)
files="$files $OPTARG"
;;
@@ -139,9 +148,12 @@ while getopts "cCf:hiKp:q:t:" opt; do
;;
q)
qenethdir="$OPTARG"
network="--sysctl net.ipv6.conf.all.disable_ipv6=0"
;;
t)
topology="$OPTARG"
network="--network host --volume $topology:$topology:ro"
runners="docker podman"
;;
*)
>&2 echo "Unknown option -$opt"
@@ -160,14 +172,14 @@ if [ "$containerize" ]; then
--env PYTHONHASHSEED=${PYTHONHASHSEED:-$(shuf -i 0-$(((1 << 32) - 1)) -n 1)} \
--env VIRTUAL_ENV_DISABLE_PROMPT=yes \
--env PROMPT_DIRTRIM="$ixdir" \
--env PS1='\e[1m$(date "+%H:%M:%S") \h\e[0m:\W # ' \
--env PS1="$(build_ps1)" \
--expose 9001-9010 --publish-all \
--hostname "$(name)" \
--name "$(name)" \
$interactive \
--rm \
--security-opt seccomp=unconfined \
--sysctl net.ipv6.conf.all.disable_ipv6=0 \
$network \
--tty \
--volume "$ixdir":"$ixdir" \
--workdir "$ixdir/test" \
+1 -1
View File
@@ -6,7 +6,7 @@ from .furl import Furl
from .netns import IsolatedMacVlan
from .sniffer import Sniffer
from .tap import Test
from .util import until
from .util import parallel, until
def std_topology(name):
return os.path.realpath(
+36 -8
View File
@@ -5,7 +5,13 @@ import pydot
import shlex
import sys
from . import neigh, netconf, tap, topology
from . import neigh, netconf, ssh, tap, topology
class NullEnv:
def attr(self, _, default=None):
return default
ENV = NullEnv()
class ArgumentParser(argparse.ArgumentParser):
def __init__(self, ltop):
@@ -35,7 +41,24 @@ class Env(object):
if not self.ltop.map_to(self.ptop):
raise tap.TestSkip()
def attach(self, node, port, factory_default=True):
print(repr(self.ltop))
global ENV
ENV = self
def attr(self, name, default=None):
val = self.ptop.get_attr("ix_" + name)
if val == None:
return default
try:
return int(val, 0)
except ValueError:
pass
return val
def attach(self, node, port, protocol="netconf", factory_default=True):
if self.ltop:
mapping = self.ltop.mapping[node]
node, port = self.ltop.xlate(node, port)
@@ -54,9 +77,14 @@ class Env(object):
if not mgmtip:
raise Exception(f"Failed, cannot find mgmt IP for {node}")
return netconf.Device(
location=netconf.Location(cport, mgmtip, password),
mapping=mapping,
yangdir=self.args.yangdir,
factory_default=factory_default
)
if protocol == "netconf":
return netconf.Device(
location=netconf.Location(cport, mgmtip, password),
mapping=mapping,
yangdir=self.args.yangdir,
factory_default=factory_default
)
elif protocol == "ssh":
return ssh.Device(ssh.Location(mgmtip, password))
raise Exception(f"Unsupported management procotol \"{protocol}\"")
+2 -2
View File
@@ -9,10 +9,10 @@ class Furl:
"""Create new URL checker"""
self.url = urllib.parse.quote(url, safe='/:')
def check(self, needle):
def check(self, needle, timeout=10):
"""Connect to web server URL, fetch body and check for needle"""
try:
with urllib.request.urlopen(self.url) as response:
with urllib.request.urlopen(self.url, timeout=timeout) as response:
text = response.read().decode('utf-8')
#print(text)
return needle in text
+65 -12
View File
@@ -1,7 +1,11 @@
import ctypes
import multiprocessing
import subprocess
import os
import random
import subprocess
import time
from . import env
__libc = ctypes.CDLL(None)
CLONE_NEWUSER = 0x10000000
@@ -17,6 +21,7 @@ class IsolatedMacVlan:
def __init__(self, parent, ifname="iface", lo=True):
self.sleeper = None
self.parent, self.ifname, self.lo = parent, ifname, lo
self.ping_timeout = env.ENV.attr("ping_timeout", 5)
def __enter__(self):
self.sleeper = subprocess.Popen(["unshare", "-r", "-n", "sh", "-c",
@@ -28,6 +33,7 @@ class IsolatedMacVlan:
subprocess.run(["ip", "link", "add",
"dev", self.ifname,
"link", self.parent,
"address", self._stable_mac(),
"netns", str(self.sleeper.pid),
"type", "macvlan"], check=True)
self.runsh(f"""
@@ -51,8 +57,31 @@ class IsolatedMacVlan:
def __exit__(self, val, typ, tb):
self.sleeper.kill()
self.sleeper.wait()
time.sleep(0.5)
def __ns_call(self, fn, tx):
def _stable_mac(self):
"""Generate address for MACVLAN
By default, the kernel will assign a random address. This
causes issues when the parent interface is an Intel X710 NIC,
which will add those addresses to its FDB, but never remove
them when the interface is deleted.
Work around the issue by generating an address that is pseudo
random, to avoid address conflicts; yet stable across
instantiations for any given parent interface name, to avoid
the resource exhaustion issue.
"""
random.seed(self.parent)
a = list(random.randbytes(6))
a[0] |= 0x02
a[0] &= ~0x01
return \
f"{a[0]:02x}:{a[1]:02x}:{a[2]:02x}:" + \
f"{a[3]:02x}:{a[4]:02x}:{a[5]:02x}"
def _ns_call(self, fn, tx):
pid = self.sleeper.pid
uns = os.open(f"/proc/{pid}/ns/user", os.O_RDONLY)
@@ -69,7 +98,7 @@ class IsolatedMacVlan:
def call(self, fn):
rx, tx = multiprocessing.Pipe(duplex=False)
proc = multiprocessing.Process(target=self.__ns_call, args=(fn, tx))
proc = multiprocessing.Process(target=self._ns_call, args=(fn, tx))
proc.start()
ret = rx.recv()
rx.close()
@@ -119,7 +148,7 @@ class IsolatedMacVlan:
p=proto[3]
self.runsh(f"""
set -ex
set -ex
ip link set iface up
ip -{p} addr add {addr}/{prefix_length} dev iface
""", check=True)
@@ -136,15 +165,39 @@ class IsolatedMacVlan:
result.append(l)
return result
def ping(self, daddr, count=1, timeout=5, interval=2, check=False):
return self.runsh(f"""set -ex; ping -c {count} -w {timeout} -i {interval} {daddr}""", check=check)
def ping(self, daddr, timeout=None):
timeout = timeout if timeout else self.ping_timeout
return self.run(["timeout", str(timeout), "/bin/sh"], text=True, check=True,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
input=f"while :; do ping -c1 -w1 {daddr} && break; done")
def must_reach(self, *args, **kwargs):
res = self.ping(*args, **kwargs)
if res.returncode != 0:
raise Exception(res.stdout)
self.ping(*args, **kwargs)
def must_not_reach(self, *args, **kwargs):
res = self.ping(*args, **kwargs)
if res.returncode == 0:
raise Exception(res.stdout)
try:
res = self.ping(*args, **kwargs)
except subprocess.CalledProcessError as e:
return
raise Exception(res)
def must_receive(self, expr, timeout=None, ifname=None, must=True):
ifname = ifname if ifname else self.ifname
timeout = timeout if timeout else self.ping_timeout
tshark = self.run(["tshark", "-nl", f"-i{ifname}",
f"-aduration:{timeout}", "-c1", expr],
stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True, check=True)
needle = "1 packet captured" if must else "0 packets captured"
if needle not in tshark.stdout:
raise Exception(tshark)
def must_not_receive(self, *args, **kwargs):
self.must_receive(*args, **kwargs, must=False)
+46
View File
@@ -0,0 +1,46 @@
import subprocess
from dataclasses import dataclass
@dataclass
class Location:
host: str
password: str
username: str = "admin"
port: int = 22
class Device(object):
def __init__(self, location: Location):
self.location = location
def _mangle_subprocess_args(self, args, kwargs):
if not args:
return
args = list(args)
if type(args[0]) == str:
if kwargs.get("shell"):
args[0] = ["/bin/sh", "-c", args[0]]
kwargs["shell"] = False
else:
args[0] = [args[0]]
args[0] = [ "ssh",
"-oStrictHostKeyChecking no",
"-oUserKnownHostsFile /dev/null",
"-oLogLevel QUIET",
f"-l{self.location.username}",
self.location.host ] + args[0]
if self.location.password:
args[0] = [ "sshpass" , f"-p{self.location.password}" ] + args[0]
return args, kwargs
def run(self, *args, **kwargs):
args, kwargs = self._mangle_subprocess_args(args, kwargs)
return subprocess.run(*args, **kwargs)
def runsh(self, script, *args, **kwargs):
return self.run("/bin/sh", text=True, input=script,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, *args, **kwargs)
+11
View File
@@ -1,5 +1,6 @@
import contextlib
import datetime
import subprocess
import sys
import traceback
@@ -15,21 +16,29 @@ class Test:
def __enter__(self):
now = datetime.datetime.now().strftime("%F %T")
self.out.write(f"# Starting ({now})\n")
self.out.flush()
return self
def __exit__(self, _, e, __):
now = datetime.datetime.now().strftime("%F %T")
self.out.write(f"# Exiting ({now})\n")
self.out.flush()
if not e:
self._not_ok("Missing explicit test result\n")
else:
if type(e) in (TestPass, TestSkip):
self.out.write(f"{self.steps}..{self.steps}\n")
self.out.flush()
raise SystemExit(0)
traceback.print_exception(e, file=self.commenter)
if type(e) is subprocess.CalledProcessError:
print("Failing subprocess stdout:\n", e.stdout)
elif len(e.args) and type(e.args[0]) is subprocess.CompletedProcess:
print("Failing subprocess stdout:\n", e.args[0].stdout)
raise SystemExit(1)
@contextlib.contextmanager
@@ -53,6 +62,7 @@ class Test:
def _report(self, tag, msg):
self.steps += 1
self.out.write(f"{tag} {self.steps}{msg}\n")
self.out.flush()
def _ok(self, msg="", directive=None):
if msg:
@@ -106,6 +116,7 @@ class CommentWriter:
data = data + "\n"
self.f.write(data)
self.flush()
def flush(self):
return self.f.flush()
+28 -8
View File
@@ -1,6 +1,15 @@
import networkx as nx
from networkx.algorithms import isomorphism
def _qstrip(text):
if text == None:
return None
if text.startswith("\"") and text.endswith("\""):
return text[1:-1]
return text
def map_edges(les, pes):
acc = []
les = sorted(list(les.values()), key=lambda x: x.get("kind", ""), reverse=True)
@@ -22,14 +31,6 @@ def match_edge(pes, les):
class Topology:
def __init__(self, dotg):
def _qstrip(text):
if text == None:
return None
if text.startswith("\"") and text.endswith("\""):
return text[1:-1]
return text
self.dotg = dotg
self.g = nx.MultiGraph()
@@ -51,6 +52,22 @@ class Topology:
attrs[dn] = dp
self.g.add_edge(sn, dn, **attrs)
def __repr__(self):
if not self.mapping:
return ""
out = ""
for n in self.mapping:
out += f"{n + ':':<8} {self.mapping[n][None]}\n"
for e in self.mapping[n]:
if not e:
continue
out += f" {e + ':':<8} {self.mapping[n][e]}\n"
return out
def map_to(self, phy):
mapper = isomorphism.MultiGraphMatcher(phy.g, self.g,
edge_match=match_edge,
@@ -126,6 +143,9 @@ class Topology:
def get_infixen(self):
return self.get_nodes(lambda _, attrs: attrs.get("kind") == "infix")
def get_attr(self, name, default=None):
return _qstrip(self.dotg.get_attributes().get(name, default))
# Support calling this script like so...
#
# python3 topology.py <physical> <logical>
+28
View File
@@ -1,7 +1,35 @@
import time
import threading
import infamy.neigh
import infamy.netconf as netconf
class ParallelFn(threading.Thread):
def __init__(self, group=None, target=None, name=None,
args=(), kwargs={}, Verbose=None):
threading.Thread.__init__(self, group, target, name, args, kwargs)
self._exc, self._return = None, None
def run(self):
if self._target is not None:
try:
self._return = self._target(*self._args,
**self._kwargs)
except Exception as e:
self._exc = e
def join(self, *args):
threading.Thread.join(self, *args)
if self._exc:
raise self._exc
return self._return
def parallel(*fns):
ths = [ParallelFn(target=fn) for fn in fns]
[th.start() for th in ths]
return [th.join() for th in ths]
def until(fn, attempts=10, interval=1):
for attempt in range(attempts):
if fn():
Executable
+24
View File
@@ -0,0 +1,24 @@
#!/bin/sh
set -e
testdir=$(dirname "$(readlink -f "$0")")
. "$testdir/.env"
lower=$1
shift
[ $# -gt 0 ] || set sh
for pid in $(pgrep -fx "sleep infinity"); do
ifidxs=$(nsenter -t $pid -U -n ip -j link show | \
jq '.[] | select(has("link_netnsid")) | .link_index')
for ifidx in $ifidxs; do
ifname=$(ip -j link show | jq -r ".[] | select(.ifindex == $ifidx) | .ifname")
if [ "$ifname" = "$lower" ]; then
exec nsenter -t $pid -U -n env PS1="$(build_ps1 "(netns:$ifname)")" "$@"
fi
done
done
echo Found no namespace with interface stacked on $lower >&2
exit 1
+21 -3
View File
@@ -1,8 +1,26 @@
test-dir := $(BR2_EXTERNAL_INFIX_PATH)/test
ninepm := $(BR2_EXTERNAL_INFIX_PATH)/test/9pm/9pm.py
UNIT_TESTS ?= $(test-dir)/case/all-repo.yaml $(test-dir)/case/all-unit.yaml
UNIT_TESTS ?= $(test-dir)/case/all-repo.yaml $(test-dir)/case/all-unit.yaml
INFIX_TESTS ?= $(test-dir)/case/all.yaml
TEST_MODE ?= qeneth
mode-qeneth := -q $(test-dir)/virt/quad
mode-host := -t $(or $(TOPOLOGY),/etc/infamy.dot)
mode-run := -t $(BINARIES_DIR)/qemu.dot
mode := $(mode-$(TEST_MODE))
binaries-$(ARCH) := $(addprefix infix-$(ARCH),.img -disk.img .pkg)
binaries-x86_64 += OVMF.fd
binaries := $(foreach bin,$(binaries-$(ARCH)),-f $(BINARIES_DIR)/$(bin))
test:
$(test-dir)/env $(mode) $(binaries) $(ninepm) $(INFIX_TESTS)
test-sh:
$(test-dir)/env $(mode) $(binaries) -i /bin/sh
test-unit:
$(test-dir)/env $(test-dir)/9pm/9pm.py $(UNIT_TESTS)
$(test-dir)/env $(ninepm) $(UNIT_TESTS)
.PHONY: test-unit
.PHONY: test test-sh test-unit