Merge pull request #833 from kernelkit/mdns-settings

Expand mDNS settings
This commit is contained in:
Tobias Waldekranz
2024-12-04 15:01:47 +01:00
committed by GitHub
25 changed files with 796 additions and 139 deletions
@@ -1,3 +0,0 @@
net.ipv4.ip_forward=1
net.ipv4.ip_forward_update_priority=0
net.ipv6.conf.all.forwarding=1
@@ -1 +1,32 @@
# Router defaults
net.ipv4.conf.default.rp_filter=0
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.lo.rp_filter=0
net.ipv4.icmp_errors_use_inbound_ifaddr=1
net.ipv4.conf.all.ignore_routes_with_linkdown=1
# Use neigh information on selection of nexthop for multipath hops
net.ipv4.fib_multipath_use_neigh=1
# Sane ARP defaults for a switch/router
net.ipv4.conf.default.arp_announce=2
net.ipv4.conf.all.arp_announce=2
net.ipv4.conf.default.arp_notify=1
net.ipv4.conf.all.arp_notify=1
net.ipv4.conf.default.arp_ignore=1
net.ipv4.conf.all.arp_ignore=1
# IP Routing
net.ipv4.ip_forward=1
net.ipv4.ip_forward_update_priority=0
net.ipv4.conf.all.forwarding=0
net.ipv4.conf.default.forwarding=0
# Multicast group subscriptions
net.ipv4.igmp_max_memberships=1000
net.ipv4.neigh.default.mcast_solicit=10
@@ -1,5 +1,18 @@
# Router defaults
net.ipv6.route.max_size=131072
net.ipv6.conf.all.ignore_routes_with_linkdown=1
# IP Routing
net.ipv6.conf.all.forwarding=1
net.ipv6.conf.default.forwarding=0
# IPv6 SLAAC
net.ipv6.conf.all.autoconf=0
net.ipv6.conf.default.autoconf=0
# Keep permanent addresses on an admin down
net.ipv6.conf.all.keep_addr_on_down=1
net.ipv6.conf.default.keep_addr_on_down=1
# Multicast group subscriptions
net.ipv6.mld_max_msf=512
+145
View File
@@ -0,0 +1,145 @@
Many of the defaults here are are taken from the Frr recommendations [1].
Below are relevant excerpts from the kernel documentation.
accept_ra, accept Router Advertisements; autoconfigure using them, also
determines whether or not to transmit Router Solicitations.
If and only if the functional setting is to accept Router
Advertisements, Router Solicitations will be transmitted.
0 - Do not accept Router Advertisements.
1 - Accept Router Advertisements if forwarding is disabled.
2 - Overrule forwarding behaviour. Accept Router Advertisements even
if forwarding is enabled.
Default:
- enabled if local forwarding is disabled
- disabled if local forwarding is enabled
accept_ra_pinfo, learn Prefix Information in Router Advertisement.
Default:
- enabled if accept_ra is enabled
- disabled if accept_ra is disabled
autoconf, autoconfigure IPv6 addresses using Prefix Information in
Router Advertisements.
Default:
- enabled if accept_ra_pinfo is enabled
- disabled if accept_ra_pinfo is disabled
arp_announce, define restriction level for announcing the local source
address from IP packets in ARP requests sent on interface:
0 - (default) Use any local address, configured on any interface
1 - Try to avoid local addresses that are not in the targets subnet
for this interface. Useful when target hosts reachable via this
interface require the source IP address in ARP requests to be part
of their logical network configured on the receiving interface.
When we generate the request we will check all our subnets that
include the target IP and will preserve the source address if it
is from such subnet. If there is no such subnet we select source
address according to the rules for level 2.
2 - Always use the best local address for this target. In this mode we
ignore the source address in the IP packet and try to select local
address that we prefer for talks with the target host. Such local
address is selected by looking for primary IP addresses on all our
subnets on the outgoing interface that include the target address.
If no suitable local address is found we select the first local
address we have on the outgoing interface or on all other
interfaces, with the hope we will receive reply for our request
and even sometimes no matter the source IP address we announce.
arp_notify, define mode for notification of address and device changes.
0 - (default): do nothing
1 - generate gratuitous arp requests when device is brought up or
hardware address changes.
arp_ignore, define different modes for sending replies in response to
received ARP requests that resolve local target addresses:
0 - (default): reply for any local target IP address, configured on
any interface
1 - reply only if the target IP address is a local address configured
on the incoming interface
2 - reply only if the target IP address is local address configured on
the incoming interface and both with the senders IP address are part
from same subnet on this interface
3 - do not reply for local addresses configured with scope host, only
resolutions for global and link addresses are replied
4-7 - reserved
8 - do not reply for all local addresses
arp_accept, define behavior for accepting gratuitous ARP (garp) frames
from devices that are not already present in the ARP table:
0 - dont create new entries in the ARP table
1 - create new entries in the ARP table
2 - create new entries only if the source IP address is in the same
subnet as an address configured on the interface that received
the garp message.
Both replies and requests type gratuitous arp will trigger the ARP
table to be updated, if this setting is on. If the ARP table already
contains the IP address of the gratuitous arp frame, the arp table
will be updated regardless if this setting is on or off.
icmp_errors_use_inbound_ifaddr
0 - (default): icmp error messages are sent with the primary address
of the exiting interface.
1 - the message will be sent with the primary address of the interface
that received the packet that caused the icmp error. This is the
behaviour many network administrators will expect from a router.
And it can make debugging complicated network layouts much easier.
Note, if no primary address exists for the interface selected, then
the primary address of the first non-loopback interface that has one
will be used regardless of this setting.
rp_filter, reverse path source filtering:
0 - (default): no source validation.
1 - Strict mode as defined in RFC3704, 'Strict Reverse Path'. Each
incoming packet is tested against the FIB and if the interface is
not the best reverse path the packet check will fail. By default
failed packets are discarded.
2 - Loose mode as defined in RFC3704, 'Loose Reverse Path'. Each
incoming packets source address is also tested against the FIB
and if the source address is not reachable via any interface the
packet check will fail.
Current recommended practice in RFC3704 is to enable strict mode to
prevent IP spoofing from DDos attacks. If using asymmetric routing or
other complicated routing, then loose mode is recommended.
The max value from conf/{all,interface}/rp_filter is used when doing
source validation on the {interface}.
[1]: https://github.com/FRRouting/frr/blob/master/doc/user/Useful_Sysctl_Settings.md
+7 -1
View File
@@ -8,10 +8,15 @@ All notable changes to the project are documented in this file.
### Changes
- NTP client status is now availible in YANG.
- NTP client status is now availible in YANG
- Add support for more mDNS settings: allow/deny interfaces, acting
as "reflector" and filtering of reflected services. Issue #678
- Review of default `sysctl` settings, issue #829
### Fixes
- Minor cleanup of Networking Guide
[v24.11.1][] - 2024-11-29
-------------------------
@@ -1418,6 +1423,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.11.0...HEAD
[v24.12.0]: https://github.com/kernelkit/infix/compare/v24.11.0...v24.12.0
[v24.11.1]: https://github.com/kernelkit/infix/compare/v24.11.0...v24.11.1
[v24.11.0]: https://github.com/kernelkit/infix/compare/v24.10.0...v24.11.0
[v24.10.2]: https://github.com/kernelkit/infix/compare/v24.10.1...v24.10.2
+92 -58
View File
@@ -1,21 +1,21 @@
Discover Infix Units
====================
# Discover Devices
Infix advertises itself via the [mDNS-SD](#mdns-sd) and [LLDP](#lldp)
discovery protocols. mDNS-SD has good client support in Windows, macOS
and on Linux systems. More on these protocols later.
An even simpler method is available when directly attached to an Infix
device:
```
.----. Ethernet .-------.
| PC +---------------------+ Infix |
'----' if1 eth0 '-------'
.----. Ethernet .-------.
| PC +---------------------+ Infix |
'----' if1 e1 '-------'
```
Figure 1: PC directly connected over Ethernet to Infix unit (here eth0).
When you wish to discover the IP address of an Infix switch, the simplest
way is probably to *ping the IPv6 all-hosts* address (ff02::1) over a
directly connected Ethernet cable. The unit's link-local IPv6 address is
seen in the response.
In the example below, the PC is connected to Infix via interface *tap0*
(*tap0* is *if1* in Figure 1) and Infix responds with address
With IPv6 you can *ping the all-hosts* address (ff02::1), the device's
link-local IPv6 address is then seen in the response. In the following
example, the PC here uses *tap0* as *if1*, Infix responds with address
*fe80::ff:fec0:ffed*.
```
@@ -31,7 +31,11 @@ rtt min/avg/max/mdev = 0.389/0.455/0.558/0.073 ms
linux-pc:#
```
The PC could connect then connect to Infix, e.g., using SSH.
> [!TIP]
> The `-L` option ignores local responses from the PC.
This address can then be used to connect to the device, e.g., using SSH.
Notice the syntax `username@address%interface`:
```
linux-pc:# ssh admin@fe80::ff:fec0:ffed%tap0
@@ -39,17 +43,13 @@ admin@fe80::ff:fec0:ffed%tap0's password: admin
admin@infix-c0-ff-ee:~$
```
## Discovery mechanisms available in Infix
Infix advertises its presence via the [mDNS](#mdns) and [LLDP](#lldp)
discovery protocols.
## LLDP
Infix supports LLDP (IEEE 802.1AB). For a device with factory default
settings, the link-local IPv6 address can be read from the Management
Address TLV using *tcpdump* or other sniffing tools[^1]:
### LLDP
Infix supports LLDP (IEEE 802.1AB). For a unit with factory default
settings, the PC can readout the link-local IPv6 address from the
Management Address TLV using *tcpdump* or other sniffing tools[^1].
```
linux-pc:# tcpdump -i tap0 -Qin -v ether proto 0x88cc
tcpdump: listening on tap0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
@@ -83,11 +83,12 @@ tcpdump: listening on tap0, link-type EN10MB (Ethernet), snapshot length 262144
linux-pc:#
```
If the unit has an IPv4 address assigned, it is shown in an additional
If the device has an IPv4 address assigned, it is shown in an additional
Management Address TLV.
> **Note** The Management Addresses shown by LLDP are not
> necessarily associated with the port transmitting the LLDP message.
> [!NOTE]
> The Management Addresses shown by LLDP are not necessarily associated
> with the port transmitting the LLDP message.
In the example below, the IPv4 address (10.0.1.1) happens to be
assigned to *eth0*, while the IPv6 address (2001:db8::1) is not.
@@ -130,10 +131,6 @@ tcpdump: listening on tap0, link-type EN10MB (Ethernet), snapshot length 262144
linux-pc:#
```
[^1]: [lldpd: implementation of IEEE 802.1ab
(LLDP)](https://github.com/lldp/lldpd) includes *lldpcli*, which
is handy to sniff and display LLDP packets.
The LLDP service can be disabled using the following commands.
```
@@ -143,13 +140,38 @@ admin@infix-c0-ff-ee:/config/> leave
admin@infix-c0-ff-ee:/>
```
### mDNS
DNS-SD/mDNS can be used to discover Infix units and services. Infix
units present their IP addresses, services and hostname within the
.local domain. This method has good client support in Apple and Linux
systems. On Linux, tools such as *avahi-browse* or *mdns-scan*[^2] can
be used to search for devices advertising their services via mDNS.
## mDNS-SD
DNS-SD/mDNS-SD can be used to discover Infix devices and services. By
default, Infix use the `.local` domain for advertising services. Some
networks use `.lan` instead, so this configurable:
```
admin@infix-c0-ff-ee:/> configure
admin@infix-c0-ff-ee:/config/> edit mdns
admin@infix-c0-ff-ee:/config/mdns/> set domain lan
```
Other available settings include limiting the interfaces mDNS responder
acts on:
```
admin@infix-c0-ff-ee:/config/> set interfaces allow e1
```
or
```
admin@infix-c0-ff-ee:/config/> set interfaces deny wan
```
The `allow` and `deny` settings are complementary, `deny` always wins.
----
In Linux, tools such as *avahi-browse* or *mdns-scan*[^2] can be used to
search for devices advertising their services via mDNS.
```
linux-pc:# avahi-browse -ar
@@ -181,9 +203,15 @@ linux-pc:# avahi-browse -ar
linux-pc:#
```
> [!TIP]
> The `-t` option is also very useful, it stops browsing automatically
> when a "more or less complete list" has been printed. However, some
> devices on the LAN may be in deep sleep so run the command again if
> you cannot find the device you are looking for.
Additionally, *avahi-resolve-host-name* can be used to verify domain
name mappings for IP addresses. By default, it translates from IPv4
addresses. This function allows users to confirm that addresses are
name mappings for IP addresses. By default, it translates from IPv4
addresses. This function allows users to confirm that addresses are
mapped correctly.
```
@@ -219,20 +247,21 @@ linux-pc:#
```
To disable mDNS/mDNS-SD, type the commands:
```
admin@infix-c0-ff-ee:/> configure
admin@infix-c0-ff-ee:/config/> no mdns
admin@infix-c0-ff-ee:/config/> leave
```
#### Human-Friendly Hostname Alias
### Human-Friendly Hostname Alias
Each Infix unit will advertise itself as *infix.local*, in addition to
its full hostname (e.g., *infix-c0-ff-ee.local* or *foo.local*). This
alias works seamlessly on a network with a single Infix device, and
makes it easy to connect when the exact hostname is not known in
advance. The examples below show how the alias can be used for
actions such as pinging or establishing an SSH connection:
Each Infix deviuce advertise itself as *infix.local*, in addition to its
full hostname (e.g., *infix-c0-ff-ee.local* or *foo.local*). This alias
works seamlessly on a network with a single Infix device, and makes it
easy to connect when the exact hostname is not known in advance. The
examples below show how the alias can be used for actions such as
pinging or establishing an SSH connection:
```
linux-pc:# ping infix.local -c 3
@@ -254,30 +283,33 @@ linux-pc:# ssh admin@infix.local
Run the command 'cli' for interactive OAM
linux-pc:#
admin@infix-c0-ff-ee:~$
```
When multiple Infix devices are present on the LAN the alias will not
uniquely identify a device; *infix.local* will refer to any of the
Infix devices, likely the one that first appeared.
> When multiple Infix units are present, use the full hostname (e.g.,
> *infix-c0-ff-ee.local* or *foo.local*) rather than the alias
> infix.local to deterministically connect to a unit.
> [!NOTE]
> When multiple Infix devices are present on the LAN, use the full name,
> e.g., *infix-c0-ff-ee.local* or *foo.local* rather than the alias
> *infix.local* to deterministically connect to the device.
#### Netbrowse service to find all your devices
### Browse Network Using *network.local*
Another mDNS alias that all Infix devices can advertise is
*network.local*. This is a web service which basically runs avahi-browse
and displays a table of other Infix devices and their services.
Another mDNS alias that all Infix devices advertise is *network.local*.
This is a web service which basically runs `avahi-browse` and displays a
table of other Infix devices and their services.
![Netbrowse Service - network.local](img/network-local.png)
With multiple Infix devices on the LAN, one will be your portal to
access all others, if it goes down another will take its place.
With multiple Infix devices on the LAN, one will take the role of your
portal to access all others, if it goes down another takes its place.
To disable the netbrowse service, and the *network.local* alias, the
following commands can be used:
To disable the netbrowse service, the following commands can be used:
```
admin@infix-c0-ff-ee:/> configure
admin@infix-c0-ff-ee:/config/> edit web
@@ -285,6 +317,8 @@ admin@infix-c0-ff-ee:/config/web/> no netbrowse
admin@infix-c0-ff-ee:/config/web/> leave
```
[^1]: E.g., [lldpd](https://github.com/lldp/lldpd) which includes the
*lldpcli* too, handy to sniff and display LLDP packets.
[^2]: [mdns-scan](http://0pointer.de/lennart/projects/mdns-scan/): a
tool for scanning for mDNS/DNS-SD published services on the local
network
tool for scanning for mDNS/DNS-SD services on the local network.
+56 -54
View File
@@ -43,18 +43,18 @@ it. In this example, traffic assigned to the VLAN in question would be
diverted to the VLAN interface before entering the bridge, while all
other traffic would be bridged as usual.
| **Type** | **Yang Model** | **Description** |
|----------|----------------------------|---------------------------------------------------------------|
| bridge | infix-if-bridge | SW implementation of an IEEE 802.1Q bridge |
| ip | ietf-ip, infix-ip | IP address to the subordinate interface |
| vlan | infix-if-vlan | Capture all traffic belonging to a specific 802.1Q VID |
| lag[^1] | infix-if-lag | Bonds multiple interfaces into one, creating a link aggregate |
| lo | ietf-interfaces | Software loopback interface |
| eth | ieee802-ethernet-interface | Physical Ethernet device/port. |
| | infix-ethernet-interface | |
| veth | infix-if-veth | Virtual Ethernet pair, typically one end is in a container |
| *common* | ietf-interfaces, | Properties common to all interface types |
| | infix-interfaces | |
| **Type** | **Yang Model** | **Description** |
|----------|----------------------------|--------------------------------------------------------------|
| bridge | infix-if-bridge | SW implementation of an IEEE 802.1Q bridge |
| ip | ietf-ip, infix-ip | IP address to the subordinate interface |
| vlan | infix-if-vlan | Capture all traffic belonging to a specific 802.1Q VID |
| lag[^1] | infix-if-lag | Bond multiple interfaces into one, creating a link aggregate |
| lo | ietf-interfaces | Software loopback interface |
| eth | ieee802-ethernet-interface | Physical Ethernet device/port. |
| | infix-ethernet-interface | |
| veth | infix-if-veth | Virtual Ethernet pair, typically one end is in a container |
| *common* | ietf-interfaces, | Properties common to all interface types |
| | infix-interfaces | |
## Data Plane
@@ -154,9 +154,9 @@ fabric!
#### MAC Bridge
In Infix ports are by default not switch ports, unless the customer
specific factory config sets it up this way. To enable switching
between ports you create a bridge and then add ports to that
bridge. That's it.
specific factory config sets it up this way. To enable switching, with
offloading if you have a switch chipset, between ports you create a
bridge and then add ports to that bridge. Like this:
```
admin@example:/> configure
@@ -170,13 +170,15 @@ admin@example:/config/> leave
Here we add two ports to bridge `br0`: `eth0` and `eth1`.
> [!TIP]
> Infix has many built-in helpers controlled by convention. Example,
> naming your bridge `brN`, where `N` is a number, hints Infix to set
> interface type automatically and unlocks all bridge features. Other
> "magic" names are `vethNA`, where `N` is a number and `A` is a letter
> ('a' for access port and 'b' for bridge side is common), and `ethN.M`
> for VLAN M on top of `ethN`, or `dockerN` to create an IP masquerading
> container bridge.
> The CLI has several built-in helpers governed by convention. E.g.,
> naming bridges `brN`, where `N` is a number, the type is *inferred*
> automatically and unlocks all bridge features. Other conventions are
> `vethNA`, where `N` is a number and `A` is a letter ('a' for access
> port and 'b' for bridge side is common), and `ethN.M` for VLAN M on
> top of `ethN`, or `dockerN` for a IP masquerading container bridge.
>
> Note, this inference only works with the CLI, configuring networking
> over NETCONF or RESTCONF requires setting the type explicitly.
![A MAC bridge with two ports](img/mac-bridge.svg)
@@ -187,11 +189,11 @@ bridge should be used instead.
#### VLAN Filtering Bridge
By default bridges in Linux do not filter based on VLAN tags. It can be
enabled in Infix when creating a bridge by adding a port to a VLAN as a
tagged or untagged member. Use the port default VID (PVID) setting to
control VLAN association for traffic ingressing a port untagged (default
PVID: 1).
By default bridges in Linux do not filter based on VLAN tags. This can
be enabled when creating a bridge by adding a port to a VLAN as a tagged
or untagged member. Use the port default VID (PVID) setting to control
VLAN association for traffic ingressing a port untagged (default PVID:
1).
```
admin@example:/config/> edit interface br0
@@ -283,15 +285,15 @@ br0 224.1.1.1 e3, e2
br0 ff02::6a br0
```
It is a small LAN, so our bridge has already become the elected IGMP
querier. We see it is ours because the timeout is `None`, and we
recognize our IP address. We can also see two ports that have joined
the same IPv4 multicast group, 224.1.1.1, and one join from Infix itself
for the IPv6 group ff02::6a.
This is a rather small LAN, so our bridge has already become the elected
IGMP querier. We see it is ours because the timeout is `None`, and we
recognize the IP address the system has detected, as ours. We can also
see two ports that have joined the same IPv4 multicast group, 224.1.1.1,
and one join from the system itself for the IPv6 group ff02::6a.
Now, let's see what happens when we add another bridge, with VLAN
filtering enabled. We skip the boring parts about how to move ports
e4-e7 to `br1` and assign them to VLANs, and again, focus on the
Now, let us see what happens when we add another bridge, this time with
VLAN filtering enabled. We skip the boring parts about how to move
ports e4-e7 to `br1` and assign them to VLANs, and again, focus on the
multicast bits only:
```
@@ -303,7 +305,7 @@ admin@example:/config/interface/br1/> leave
admin@example:/> copy running-config startup-config
```
Let's see what we get:
Let us see what we get:
```
admin@example:/> show ip multicast
@@ -404,15 +406,15 @@ an IGMP/MLD fast-leave port.
#### Forwarding of IEEE Reserved Group Addresses
Addresses in range `01:80:C2:00:00:0X` are used by various bridge
signaling protocols, and are not forwarded by default. Still, it is
sometimes useful to let the bridge forward such packets, and Infix
supports this by specifying protocol names or the last address
*nibble* as decimal value `0..15`.
Addresses in the range `01:80:C2:00:00:0X` are used by various bridge
signaling protocols, and are not forwarded by default. Still, it is
sometimes useful to let the bridge forward such packets, this can be
done by specifying protocol names or the last address *nibble* as
decimal value `0..15`:
```
admin@example:/config/> edit interface br0 bridge
admin@example:/config/interface/br0/bridge/> set ieee-group-forward <?>
admin@example:/config/interface/br0/bridge/> set ieee-group-forward # Tap the ? ley for alternatives
[0..15] List of IEEE link-local protocols to forward, e.g., STP, LLDP
dot1x 802.1X Port-Based Network Access Control.
lacp 802.3 Slow Protocols, e.g., LACP.
@@ -430,7 +432,7 @@ admin@example:/config/interface/br0/bridge/>
### VLAN Interfaces
Creating a VLAN can be done in many ways. This section assumes VLAN
Creating a VLAN can be done in many ways. This section assumes VLAN
interfaces created atop another Linux interface. E.g., the VLAN
interfaces created on top of the Ethernet interface or bridge in the
picture below.
@@ -472,7 +474,7 @@ top of a bridge interface *br0* is named *vlan10*.
> [!NOTE]
> If you name your VLAN interface `foo0.N` or `vlanN`, where `N` is a
> number, Infix will set the interface type automatically for you.
> number, the CLI infers the interface type automatically.
### Physical Ethernet Interfaces
@@ -638,7 +640,8 @@ admin@example:/config/>
This section details IP Addresses And Other Per-Interface IP settings.
Infix support several network interface types, each can be assigned one
or more IP addresses, both IPv4 and IPv6 are supported.
or more IP addresses, both IPv4 and IPv6 are supported. (There is no
concept of a "primary" address.)
![IP on top of network interface examples](img/ip-iface-examples.svg)
@@ -1226,7 +1229,7 @@ currently supported, namely `ipv4` and `ipv6`.
[4]: https://www.rfc-editor.org/rfc/rfc3442
[0]: https://frrouting.org/
[^1]: Please note, link aggregates are not yet supported in Infix.
[^1]: Please note, link aggregates are not yet supported.
[^2]: Link-local IPv6 addresses are implicitly enabled when enabling
IPv6. IPv6 can be enabled/disabled per interface in the
[ietf-ip][2] YANG model.
@@ -1237,11 +1240,10 @@ currently supported, namely `ipv4` and `ipv6`.
[^4]: A YANG deviation was previously used to make it possible to set
`phys-address`, but this has been replaced with the more flexible
`custom-phys-address`.
[^5]: Infix MAC bridges on Marvell Linkstreet devices are currently
limited to use a single MAC database, causing issues if the same
MAC address appears on different MAC bridges.
[^6]: Ethernet counters are described in
*ieee802-ethernet-interface.yang* and
*infix-ethernet-interface.yang*. [Ethernet
Counters](eth-counters.md) page provides additional details on
statistics support.
[^5]: MAC bridges on Marvell Linkstreet devices are currently limited to
a single MAC database, this may be a problem if the same MAC address
appears in different MAC bridges.
[^6]: Ethernet counters are described in *ieee802-ethernet-interface.yang*
and *infix-ethernet-interface.yang*. There is a dedicated document on
[Ethernet Counters](eth-counters.md) that provide additional details
on the statistics support.
@@ -0,0 +1 @@
AVAHI_ARGS="-l notice"
@@ -1 +1,2 @@
service [2345] name:mdns avahi-daemon -s -l notice -- Avahi mDNS-SD daemon
service <!> name:mdns env:-/etc/default/avahi \
[2345] avahi-daemon -s $AVAHI_ARGS -- Avahi mDNS-SD daemon
@@ -1,4 +1,5 @@
# <!>: dropbear does not honor SIGHUP.
# -R: Create hostkeys if needed.
# -F: Run in foreground.
service [2345] <!> env:-/etc/default/dropbear dropbear -F -R $DROPBEAR_ARGS -- Dropbear SSH daemon
service <!> env:-/etc/default/dropbear \
[2345] dropbear -F -R $DROPBEAR_ARGS -- Dropbear SSH daemon
@@ -1 +1,2 @@
service [2345] <usr/mkcert> env:-/etc/default/nginx nginx -g 'daemon off;' $NGINX_ARGS -- Web server
service <usr/mkcert> env:-/etc/default/nginx \
[2345] nginx -g 'daemon off;' $NGINX_ARGS -- Web server
+66 -3
View File
@@ -176,21 +176,81 @@ static int hostname_change(sr_session_ctx_t *session, uint32_t sub_id, const cha
return mdns_records("update", all);
}
static void fput_list(FILE *fp, struct lyd_node *cfg, const char *list, const char *heading)
{
const char *prefix = heading;
struct lyd_node *node;
LYX_LIST_FOR_EACH(lyd_child(cfg), node, list) {
fprintf(fp, "%s%s", prefix, lyd_get_value(node));
prefix = ",";
}
if (prefix != heading)
fprintf(fp, "\n");
}
#define AVAHI_CONF "/etc/avahi/avahi-daemon.conf"
static void mdns_conf(struct lyd_node *cfg)
{
struct lyd_node *ctx;
FILE *fp;
fp = fopen(AVAHI_CONF, "w");
if (!fp) {
ERRNO("failed creating %s", AVAHI_CONF);
return;
}
fprintf(fp, "# Generated by Infix confd\n"
"[server]\n"
"domain-name=%s\n"
"use-ipv4=yes\n"
"use-ipv6=yes\n", lydx_get_cattr(cfg, "domain"));
ctx = lydx_get_descendant(lyd_child(cfg), "interfaces", NULL);
if (ctx) {
fput_list(fp, ctx, "allow", "allow-interfaces=");
fput_list(fp, ctx, "deny", "deny-interfaces=");
}
fprintf(fp,
"ratelimit-interval-usec=1000000\n"
"ratelimit-burst=1000\n");
fprintf(fp, "\n[wide-area]\n");
/* nop */
fprintf(fp, "\n[publish]\n");
/* nop */
fprintf(fp, "\n[reflector]\n");
ctx = lydx_get_descendant(lyd_child(cfg), "reflector", NULL);
if (ctx) {
fprintf(fp, "enable-reflector=%s\n", lydx_is_enabled(ctx, "enabled") ? "on" : "off");
fput_list(fp, ctx, "service-filter", "reflect-filters=");
}
fprintf(fp, "\n[rlimits]\n");
/* nop */
fclose(fp);
}
static void mdns_cname(sr_session_ctx_t *session)
{
int ena = srx_enabled(session, "/infix-services:mdns/enabled");
if (ena) {
int www = srx_enabled(session, "/infix-services:web/netbrowse/enabled");
char *name = fgetkey("/etc/os-release", "DEFAULT_HOSTNAME");
const char *hostname = fgetkey("/etc/os-release", "DEFAULT_HOSTNAME");
if (name || www) {
if (hostname || www) {
FILE *fp;
fp = fopen("/etc/default/mdns-alias", "w");
if (fp) {
fprintf(fp, "MDNS_ALIAS_ARGS=\"%s%s %s\"\n",
name ?: "", name ? ".local" : "",
hostname ?: "", hostname ? ".local" : "",
www ? "network.local" : "");
fclose(fp);
} else {
@@ -217,6 +277,9 @@ static int mdns_change(sr_session_ctx_t *session, uint32_t sub_id, const char *m
ena = lydx_is_enabled(srv, "enabled");
if (ena) {
/* Generate/update avahi-daemon.conf */
mdns_conf(srv);
/* Generate/update basic mDNS service records */
mdns_records("update", all);
}
+1 -1
View File
@@ -33,7 +33,7 @@ MODULES=(
"infix-dhcp-client@2024-09-20.yang"
"infix-meta@2024-10-18.yang"
"infix-system@2024-11-27.yang"
"infix-services@2024-05-30.yang"
"infix-services@2024-12-02.yang"
"ieee802-ethernet-interface@2019-06-21.yang"
"infix-ethernet-interface@2024-02-27.yang"
"infix-factory-default@2023-06-28.yang"
+68
View File
@@ -3,10 +3,21 @@ module infix-services {
namespace "urn:ietf:params:xml:ns:yang:infix-services";
prefix infix-svc;
import ietf-interfaces {
prefix if;
}
import ietf-inet-types {
prefix inet;
}
organization "KernelKit";
contact "kernelkit@googlegroups.com";
description "Infix services, generic.";
revision 2024-12-02 {
description "Expand mdns options: domain, allow/deny interfaces, reflector.";
reference "internal";
}
revision 2024-05-30 {
description "Add support for RESTCONF enable/disable as a web service.";
reference "internal";
@@ -35,6 +46,63 @@ module infix-services {
description "Globally enable or disable mDNS/SD on all interfaces.";
type boolean;
}
leaf domain {
description "LAN domain name to register host name and services in.
Most common is .local, but some also use .lan, or .office,
usually this setting can be left as-is.";
default "local";
type inet:domain-name;
}
container interfaces {
description "Filter interfaces to act on.";
leaf-list allow {
description "Enabled on interfaces, can be combined with deny-interfaces.
By default all, except loopback and point-to-point links.";
type if:interface-ref;
}
leaf-list deny {
description "Disabled on interfaces (always wins).
Other not specified interfaces will be used, except loopback
and point-to-point, unless combined with allow-interfaces.
This option takes precedence over allow-interfaces.";
type if:interface-ref;
}
}
container reflector {
description "Reflect incoming mDNS requests to local interfaces.";
leaf enabled {
description "Enable mDNS reflector on local interfaces.";
type boolean;
}
leaf-list service-filter {
description "Filter mDNS service names to reflect.
Example, for AirPlay and AirTunes, use:
- _airplay._tcp.local
- _raop._tcp.local
For AirPrint use:
- _printer._tcp.local
- _ipp._tcp.local
- _pdl-datastream._tcp.local
By default all services are reflected.";
type string;
}
}
}
container web {
+3
View File
@@ -3,4 +3,7 @@
<<<
include::mdns_allow_deny/Readme.adoc[]
include::services_basic/Readme.adoc[]
+3 -1
View File
@@ -6,4 +6,6 @@
# are transparent with respect to IEEE link-local multicast.
#- name: services_basic
# case: services_basic/test.py
[]
- name: mdns_allow_deny
case: mdns_allow_deny/test.py
@@ -0,0 +1,32 @@
=== mDNS allow/deny interfaces
==== Description
Verify the mDNS responder interface allow/deny configuration. Both
settings can be used independently and in concert. We verify operation
with three scenarios:
1. Allow p2, no mDNS traffic should be received on p1 and p3
2. Deny p2, mDNS traffic should only be received on p1 and p3
3. Allow p1 and p3, deny p2 and p3, traffic only on p1
==== Topology
ifdef::topdoc[]
image::../../test/case/infix_services/mdns_allow_deny/topology.svg[mDNS allow/deny interfaces topology]
endif::topdoc[]
ifndef::topdoc[]
ifdef::testgroup[]
image::mdns_allow_deny/topology.svg[mDNS allow/deny interfaces topology]
endif::testgroup[]
ifndef::testgroup[]
image::topology.svg[mDNS allow/deny interfaces topology]
endif::testgroup[]
endif::topdoc[]
==== Test sequence
. Set up topology and attach to target DUT
. Configure device
. Allow mDNS on a single interface: p2
. Deny mDNS on a single interface: p2
. Allow mDNS on p1, p3 deny on p2, p3
<<<
+160
View File
@@ -0,0 +1,160 @@
#!/usr/bin/env python3
"""mDNS allow/deny interfaces
Verify the mDNS responder interface allow/deny configuration. Both
settings can be used independently and in concert. We verify operation
with three scenarios:
1. Allow p2, no mDNS traffic should be received on p1 and p3
2. Deny p2, mDNS traffic should only be received on p1 and p3
3. Allow p1 and p3, deny p2 and p3, traffic only on p1
"""
import time
import infamy
from infamy.util import parallel
def mdns_scan(tgt):
"""Trigger Avahi to send traffic on allowed interfaces"""
time.sleep(2)
tgt.runsh("logger -t scan 'calling avahi-browse ...'")
tgt.runsh("avahi-browse -lat")
def check(ns, expr, must):
"""Wrap netns.must_receive() with common defaults"""
return ns.must_receive(expr, timeout=3, must=must)
with infamy.Test() as test:
with test.step("Set up topology and attach to target DUT"):
env = infamy.Env()
dut = env.attach("dut", "mgmt")
ssh = env.attach("dut", "mgmt", "ssh")
_, p1 = env.ltop.xlate("dut", "p1")
_, p2 = env.ltop.xlate("dut", "p2")
_, p3 = env.ltop.xlate("dut", "p3")
_, eth1 = env.ltop.xlate("host", "eth1")
_, eth2 = env.ltop.xlate("host", "eth2")
_, eth3 = env.ltop.xlate("host", "eth3")
with test.step("Configure device"):
dut.put_config_dicts(
{
"ietf-interfaces": {
"interfaces": {
"interface": [
{
"name": p1,
"enabled": True,
"ipv4": {
"address": [
{
"ip": "10.0.1.1",
"prefix-length": 24
}
]
}
},
{
"name": p2,
"enabled": True,
"ipv4": {
"address": [
{
"ip": "10.0.2.1",
"prefix-length": 24
}
]
}
},
{
"name": p3,
"enabled": True,
"ipv4": {
"address": [
{
"ip": "10.0.3.1",
"prefix-length": 24
}
]
}
},
]
}
},
"ietf-system": {
"system": {
"hostname": "dut"
}
},
"infix-services": {
"mdns": {
"enabled": True
}
}
}
)
with infamy.IsolatedMacVlan(eth1) as ns1, \
infamy.IsolatedMacVlan(eth2) as ns2, \
infamy.IsolatedMacVlan(eth3) as ns3:
ns1.addip("10.0.1.2")
ns2.addip("10.0.2.2")
ns3.addip("10.0.3.2")
EXPR1 = "host 10.0.1.1 and port 5353"
EXPR2 = "host 10.0.2.1 and port 5353"
EXPR3 = "host 10.0.3.1 and port 5353"
with test.step("Allow mDNS on a single interface: p2"):
dut.put_config_dict("infix-services", {
"mdns": {
"interfaces": {
"allow": [p2],
}
}
})
parallel(lambda: mdns_scan(ssh),
lambda: check(ns1, EXPR1, False),
lambda: check(ns2, EXPR2, True),
lambda: check(ns3, EXPR3, False))
with test.step("Deny mDNS on a single interface: p2"):
dut.delete_xpath("/infix-services:mdns/interfaces")
dut.put_config_dict("infix-services", {
"mdns": {
"interfaces": {
"deny": [p2],
}
}
})
parallel(lambda: mdns_scan(ssh),
lambda: check(ns1, EXPR1, True),
lambda: check(ns2, EXPR2, False),
lambda: check(ns3, EXPR3, True))
with test.step("Allow mDNS on p1, p3 deny on p2, p3"):
dut.delete_xpath("/infix-services:mdns/interfaces")
dut.put_config_dict("infix-services", {
"mdns": {
"interfaces": {
"allow": [p1, p3],
"deny": [p2, p3],
}
}
})
parallel(lambda: mdns_scan(ssh),
lambda: check(ns1, EXPR1, True),
lambda: check(ns2, EXPR2, False),
lambda: check(ns3, EXPR3, False))
test.succeed()
@@ -0,0 +1,26 @@
graph "1x4" {
layout="neato";
overlap="false";
esep="+80";
node [shape=record, fontname="DejaVu Sans Mono, Book"];
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
host [
label="host | { <mgmt> mgmt | <eth1> eth1 | <eth2> eth2 | <eth3> eth3 }",
pos="0,12!",
kind="controller",
];
dut [
label="{ <mgmt> mgmt | <p1> p1 | <p2> p2 | <p3> p3 } | dut \n\n(br0)\n10.0.p.1/24",
pos="10,12!",
kind="infix",
];
host:mgmt -- dut:mgmt [kind=mgmt, color="lightgray"]
host:eth1 -- dut:p1 [color=black, fontcolor=black, taillabel="10.0.1.2/24"]
host:eth2 -- dut:p2 [color=black, fontcolor=black, taillabel="10.0.2.2/24"]
host:eth3 -- dut:p3 [color=black, fontcolor=black, taillabel="10.0.3.2/24"]
}
@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Title: 1x4 Pages: 1 -->
<svg width="465pt" height="101pt"
viewBox="0.00 0.00 465.03 101.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 97)">
<title>1x4</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-97 461.03,-97 461.03,4 -4,4"/>
<!-- host -->
<g id="node1" class="node">
<title>host</title>
<polygon fill="none" stroke="black" points="0,-0.5 0,-92.5 100,-92.5 100,-0.5 0,-0.5"/>
<text text-anchor="middle" x="25" y="-42.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">host</text>
<polyline fill="none" stroke="black" points="50,-0.5 50,-92.5 "/>
<text text-anchor="middle" x="75" y="-77.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="50,-69.5 100,-69.5 "/>
<text text-anchor="middle" x="75" y="-54.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">eth1</text>
<polyline fill="none" stroke="black" points="50,-46.5 100,-46.5 "/>
<text text-anchor="middle" x="75" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">eth2</text>
<polyline fill="none" stroke="black" points="50,-23.5 100,-23.5 "/>
<text text-anchor="middle" x="75" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">eth3</text>
</g>
<!-- dut -->
<g id="node2" class="node">
<title>dut</title>
<polygon fill="none" stroke="black" points="300.03,-0.5 300.03,-92.5 457.03,-92.5 457.03,-0.5 300.03,-0.5"/>
<text text-anchor="middle" x="325.03" y="-77.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="300.03,-69.5 350.03,-69.5 "/>
<text text-anchor="middle" x="325.03" y="-54.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">p1</text>
<polyline fill="none" stroke="black" points="300.03,-46.5 350.03,-46.5 "/>
<text text-anchor="middle" x="325.03" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">p2</text>
<polyline fill="none" stroke="black" points="300.03,-23.5 350.03,-23.5 "/>
<text text-anchor="middle" x="325.03" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">p3</text>
<polyline fill="none" stroke="black" points="350.03,-0.5 350.03,-92.5 "/>
<text text-anchor="middle" x="403.53" y="-65.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">dut </text>
<text text-anchor="middle" x="403.53" y="-34.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">(br0)</text>
<text text-anchor="middle" x="403.53" y="-19.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">10.0.p.1/24</text>
</g>
<!-- host&#45;&#45;dut -->
<g id="edge1" class="edge">
<title>host:mgmt&#45;&#45;dut:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M100,-81.5C100,-81.5 299.53,-81.5 299.53,-81.5"/>
</g>
<!-- host&#45;&#45;dut -->
<g id="edge2" class="edge">
<title>host:eth1&#45;&#45;dut:p1</title>
<path fill="none" stroke="black" stroke-width="2" d="M100,-58.5C100,-58.5 299.53,-58.5 299.53,-58.5"/>
<text text-anchor="middle" x="141" y="-62.3" font-family="DejaVu Serif, Book" font-size="14.00">10.0.1.2/24</text>
</g>
<!-- host&#45;&#45;dut -->
<g id="edge3" class="edge">
<title>host:eth2&#45;&#45;dut:p2</title>
<path fill="none" stroke="black" stroke-width="2" d="M100,-34.5C100,-34.5 299.53,-34.5 299.53,-34.5"/>
<text text-anchor="middle" x="141" y="-38.3" font-family="DejaVu Serif, Book" font-size="14.00">10.0.2.2/24</text>
</g>
<!-- host&#45;&#45;dut -->
<g id="edge4" class="edge">
<title>host:eth3&#45;&#45;dut:p3</title>
<path fill="none" stroke="black" stroke-width="2" d="M100,-11.5C100,-11.5 299.53,-11.5 299.53,-11.5"/>
<text text-anchor="middle" x="141" y="-15.3" font-family="DejaVu Serif, Book" font-size="14.00">10.0.3.2/24</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

@@ -17,7 +17,7 @@ endif::testgroup[]
endif::topdoc[]
==== Test sequence
. Set up topology and attach to target DUT
. Set IPv4 address 10.0.0.10/24 on target:data and disable MDNS and LLDP
. Set IPv4 address 10.0.0.10/24 on target:data and disable mDNS and LLDP
. Enable mDNS and LLDP and toggle target:data DOWN and UP to trigger service
. Verify on host:data there are packets from 10.0.0.10:5354 (mDNS)
. Verify on host:data there are LLDP packets sent from 10.0.0.10
@@ -71,7 +71,7 @@ with infamy.Test() as test:
env = infamy.Env()
target = env.attach("target", "mgmt")
with test.step("Set IPv4 address 10.0.0.10/24 on target:data and disable MDNS and LLDP"):
with test.step("Set IPv4 address 10.0.0.10/24 on target:data and disable mDNS and LLDP"):
_, tport = env.ltop.xlate("target", "data")
target.put_config_dict("ietf-interfaces", {
+10 -4
View File
@@ -389,21 +389,27 @@ class Device(Transport):
raise ValueError(f"Failed parsing xpath:{xpath}")
module = match.group('module')
modpath = f"/{match.group('module')}:{match.group('path')}"
path = match.group('path')
modpath = f"/{module}:{path}"
# Fetch current config
old = self.get_config_dict(modpath)
new = copy.deepcopy(old)
libyang.xpath_del(new, xpath)
# Perform deletion
if not libyang.xpath_del(new, xpath):
raise ValueError(f"Failed to delete specified xpath: {xpath}")
# Parse old and new data to generate the diff
mod = self.ly.get_module(module)
oldd = mod.parse_data_dict(old, no_state=True)
newd = mod.parse_data_dict(new, no_state=True)
oldd = mod.parse_data_dict(old, no_state=True, validate=False)
newd = mod.parse_data_dict(new, no_state=True, validate=False)
lyd = oldd.diff(newd)
if lyd is None:
raise ValueError(f"Failed generating diff for xpath:{xpath}")
# Apply the configuration change
return self.put_config(lyd.print_mem("xml", with_siblings=True,
pretty=False))
+9 -9
View File
@@ -88,7 +88,7 @@ class IsolatedMacVlans:
self.sleeper.kill()
self.sleeper.wait()
for n in range(100):
for _ in range(100):
promisc = False
for parent in self.ifmap.keys():
iplink = subprocess.run(f"ip -d -j link show dev {parent}".split(),
@@ -190,12 +190,13 @@ class IsolatedMacVlans:
def runsh(self, script, *args, **kwargs):
return self.run("/bin/sh", text=True, input=script,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, *args, **kwargs)
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
*args, **kwargs)
def addroute(self, subnet, nexthop, proto="ipv4", prefix_length=""):
p=proto[3]
p = proto[3]
if prefix_length:
prefix_length=f"/{prefix_length}"
prefix_length = f"/{prefix_length}"
self.runsh(f"""
set -ex
@@ -203,7 +204,7 @@ class IsolatedMacVlans:
""", check=True)
def addip(self, ifname, addr, prefix_length=24, proto="ipv4"):
p=proto[3]
p = proto[3]
self.runsh(f"""
set -ex
@@ -211,15 +212,14 @@ class IsolatedMacVlans:
ip -{p} addr add {addr}/{prefix_length} dev {ifname}
""", check=True)
def traceroute(self, addr):
res=self.runsh(f"""
res = self.runsh(f"""
set -ex
traceroute -n {addr}
""", check=True)
result=[]
result = []
for line in res.stdout.splitlines()[2:]:
l=line.split()
l = line.split()
result.append(l)
return result