Skip to content

Commit

Permalink
Merge pull request #695 from kernelkit/custom-phys-address
Browse files Browse the repository at this point in the history
  • Loading branch information
troglobit authored Oct 11, 2024
2 parents 6e8f91e + 2618085 commit 3358687
Show file tree
Hide file tree
Showing 20 changed files with 646 additions and 142 deletions.
2 changes: 1 addition & 1 deletion board/aarch64/r2s/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ what provides a default, the same default MAC addresses to Linux:

This is important in case you want to run multiple R2S devices on the
same LAN. Meaning you either have to change the MAC address in the
U-Boot environment (below), or modify your `phys-address` setting in
U-Boot environment (below), or use the `custom-phys-address` setting in
Infix for the interface(s).

Break into U-Boot using Ctrl-C at power-on, preferably when the text
Expand Down
100 changes: 7 additions & 93 deletions board/common/rootfs/usr/libexec/infix/init.d/30-cfg-migrate
Original file line number Diff line number Diff line change
@@ -1,102 +1,16 @@
#!/bin/sh
# Run .cfg migration jq scripts to backup and transform older .cfg files
ident=$(basename "$0")

MIGRATIONS_DIR="/usr/share/confd/migrate"
# Check if /cfg/startup-config.cfg needs to be migrated to new syntax.
# Backup of the original is created in /cfg/backup/ for old versions,
# the migrate tool inserts old version in name before .cfg extension.
CONFIG_FILE="/cfg/startup-config.cfg"
BACKUP_DIR="/cfg/backup"

note()
{
logger -I $$ -k -p user.notice -t "$ident" "$1"
}

err()
{
logger -I $$ -k -p user.err -t "$ident" "$1"
}

file_version()
{
jq -r '
if .["infix-meta:meta"] | has("version") then
.["infix-meta:meta"]["version"]
else
"0.0"
end
' "$1"
}

atoi()
{
echo "$1" | awk -F. '{print $1 * 1000 + $2}'
}
BACKUP_FILE="/cfg/backup/startup-config.cfg"
mkdir -p "$(dirname "$BACKUP_FILE")"

if [ ! -f "$CONFIG_FILE" ]; then
# Nothing to migrate
note "No $(basename "$CONFIG_FILE" .cfg) yet, likely factory reset."
exit 0
fi

cfg_version=$(file_version "$CONFIG_FILE")
current_version=$(atoi "$cfg_version")

# Find the latest version by examining the highest numbered directory
sys_version=$(find "$MIGRATIONS_DIR" -mindepth 1 -maxdepth 1 -type d | sort -V | tail -n1 | xargs -n1 basename)
latest_version=$(atoi "$sys_version")

# Check for downgrade
if [ "$current_version" -gt "$latest_version" ]; then
err "Configuration file $CONFIG_FILE version ($cfg_version) is newer than the latest supported version ($sys_version). Exiting."
exit 1
fi

# If the current version is already the latest, exit the script
if [ "$current_version" -eq "$latest_version" ]; then
note "Configuration is already at the latest version ($sys_version). No migration needed."
elif migrate -cq "$CONFIG_FILE"; then
exit 0
fi

note "Configuration file $CONFIG_FILE is of version $cfg_version, migrating ..."

# Create the backup directory if it doesn't exist
mkdir -p "$BACKUP_DIR"

# Create a backup of the current configuration file
nm=$(basename "$CONFIG_FILE" .cfg)
BACKUP_FILE="$BACKUP_DIR/${nm}-${cfg_version}.cfg"
if cp "$CONFIG_FILE" "$BACKUP_FILE"; then
note "Backup created: $BACKUP_FILE"
else
err "Failed creating backup: $BACKUP_FILE"
exit 1
fi

# Apply the scripts for each version directory in sequence
for version_dir in $(find "$MIGRATIONS_DIR" -mindepth 1 -maxdepth 1 -type d | sort -V); do
dir=$(basename "$version_dir")
version=$(atoi "$dir")

# Step by step upgrade file to latest version
if [ "$current_version" -lt "$version" ]; then
note "Applying migrations for version $dir ..."

# Apply all scripts in the version directory in order
for script in $(find "$version_dir" -type f -name '*.sh' | sort -V); do
note "Calling $script for $CONFIG_FILE ..."
sh "$script" "$CONFIG_FILE"
done

# File now at $version ...
current_version="$version"
fi
done

# Update the JSON file to the latest version
if jq --arg version "$sys_version" '.["infix-meta:meta"] = {"infix-meta:version": $version}' "$CONFIG_FILE" \
> "${CONFIG_FILE}.tmp" && mv "${CONFIG_FILE}.tmp" "$CONFIG_FILE"; then
note "Configuration updated to version $sys_version."
else
err "Failed updating configuration to version $sys_version!"
exit 1
fi
migrate -i -b "$BACKUP_FILE" "$CONFIG_FILE"
35 changes: 31 additions & 4 deletions doc/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,41 @@ All notable changes to the project are documented in this file.

[v24.10.0][] - UNRELEASED
-------------------------

**News:** this release contains *breaking YANG changes* in custom MAC
addresses for interfaces! For details, see below issue #680.

### Changes
- OSPF: Add limitation to only allow one interface per area.
- Update CONTRIBUTING.md for scaling core team and helping external
contributors understand the development process, issue #672
- OSPF: Add limitation to only allow one interface per area
- The default builds now include the curiOS nftables container image,
which can be used for advanced firewall setups. For an introduction
see <https://kernelkit.org/posts/firewall-container/>

### Fixes
- Fix #499 by adding a NACM rule to factory config, which by default
deny everyone to read the user password hash.
- Fix #499: add an NACM rule to factory config, which by default
deny everyone to read user password hash(es)
- Fix #663: internal Ethernet interfaces shown in CLI tab completion
- Fix #674: CLI `show interfaces` display internal Ethernet interfaces,
regression introduced late in v24.09 release cycle
- Fix #676: port dropped from bridge when changing its VLAN membership
from tagged to untagged
- Fix #680: replace deviation for `phys-address` in ietf-interfaces.yang
with `custom-phys-address` to allow for constructing more free-form
MAC addresses based on the chassis MAC (a.k.a., base MAC) address.
For more information, see the YANG model, a few examples are listed in
the updated documentation.
The syntax will be automatically updated in the `startup-config` and
`factory-config` -- make sure to verify the changes and update any
static `factory-config` used for your products
- Fix #690: CLI `show ip route` command stops working after 24 hours,
this includes all operational data in ietf-routing:/routing/ribs.
- Fix #697: password is not always set for new users, bug introduced
in v24.06.0 when replacing Augeas with native user handling
- Fix BFD in OSPF, previously you could not enable BFD on a single
interface without it was enabled on all interfaces.
interface without it was enabled on all interfaces


[v24.09.0][] - 2024-09-30
-------------------------
Expand Down
2 changes: 1 addition & 1 deletion doc/container.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ line where we declare the `ntpd` end as a container network interface:
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 custom-phys-address static 00:c0:ff:ee:00:01
admin@example:/config/interface/ntpd/> set container-network

> Notice how you can also set a custom MAC address at the same time.
Expand Down
4 changes: 4 additions & 0 deletions doc/img/lego-relations.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion doc/img/lego.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
89 changes: 89 additions & 0 deletions doc/networking.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,34 @@ Infix to exploit the unique features not available in IEEE models.

## Interface LEGO®

The network building blocks available in Linux are akin to the popular
LEGO® bricks.

![Linux Networking Blocks](img/lego.svg)

There are two types of relationships that can link two blocks together:

1. **Lower-to-upper**: Visually represented by an extruding square
connected upwards to a square socket. An interface _can only have
a single_ lower-to-upper relationship, i.e., it can be attached to
a single upper interface like a bridge or a LAG. In `iproute2`
parlance, this corresponds to the interface's `master` setting
2. **Upper-to-lower**: Visually represented by an extruding semicircle
connected downwards to a semicircle socket. The lower interface in
these relationships _accepts multiple_ upper-to-lower relationships
from different upper blocks. E.g., multiple VLANs and IP address
blocks can be connected to the same lower interface

![Stacking order dependencies](img/lego-relations.svg)

An interface may simultaneously have a _lower-to-upper_ relation to some
other interface, and be the target of one or more _upper-to-lower_
relationships. It is valid, for example, for a physical port to be
attached to a bridge, but also have a VLAN interface stacked on top of
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 |
Expand All @@ -27,6 +53,7 @@ Infix to exploit the unique features not available in IEEE models.
| | ieee802-ethernet-interface | |
| veth | infix-if-veth | Virtual Ethernet pair, typically one end is in a container |


## Data Plane

The blocks you choose, and how you connect them, defines your data plane.
Expand All @@ -50,6 +77,65 @@ possible to share with a container. Meaning, all the building blocks
used on the left hand side can also be used freely on the right hand
side as well.


### General

General interface settings include `type`, `enable`, custom MAC address,
and text `description`. Other settings have their own sections, below.

The `type` is important to set when configuring devices remotely because
unlike the CLI, a NETCONF or RESTCONF session cannot guess the interface
type for you. The operating system provides an override of the
available interface types.

An `enabled` interface can be inspected using the operational datastore,
nodes `admin-state` and `oper-state` show the status, . Possible values
are listed in the YANG model.

The `custom-phys-address` can be used to set an interface's MAC address.
This is an extension to the ietf-interfaces YANG model, which defines
`phys-address` as read-only[^4]. The following shows the different
configuration options.

> **Note:** there is no validation or safety checks performed by the
> system when using `custom-phys-address`. In particular the `offset`
> variant can be dangerous to use -- pay attention to the meaning of
> bits in the upper-most octet: local bit, multicast/group, etc.
#### Fixed custom MAC

```
admin@example:/config/> edit interface veth0a
admin@example:/config/interface/veth0a/> set custom-phys-address static 00:ab:00:11:22:33
=> 00:ab:00:11:22:33
```

#### Chassis MAC

Chassis MAC, sometimes also referred to as base MAC. In these two
examples it is `00:53:00:c0:ff:ee`.

```
admin@example:/config/> edit interface veth0a
admin@example:/config/interface/veth0a/> set custom-phys-address chassis
=> 00:53:00:c0:ff:ee
```

#### Chassis MAC, with offset

When constructing a derived address it is recommended to set the locally
administered bit. Same chassis MAC as before.

```
admin@example:/config/> edit interface veth0a
admin@example:/config/interface/veth0a/> set custom-phys-address chassis offset 02:00:00:00:00:02
=> 02:53:00:c0:ff:f0
```


### Bridging

This is the most central part of the system. A bridge is a switch, and
Expand Down Expand Up @@ -1031,3 +1117,6 @@ currently supported, namely `ipv4` and `ipv6`.
mapping the low-order 23-bits of the IP address in the low-order 23
bits of the Ethernet address 01:00:5E:00:00:00. Meaning, more than
one IP multicast group maps to the same MAC multicast group.
[^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`.
2 changes: 1 addition & 1 deletion package/confd/confd.mk
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#
################################################################################

CONFD_VERSION = 1.1
CONFD_VERSION = 1.2
CONFD_SITE_METHOD = local
CONFD_SITE = $(BR2_EXTERNAL_INFIX_PATH)/src/confd
CONFD_LICENSE = BSD-3-Clause
Expand Down
2 changes: 1 addition & 1 deletion src/confd/bin/Makefile.am
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pkglibexec_SCRIPTS = bootstrap error load gen-service gen-hostname \
gen-interfaces gen-motd gen-hardware gen-version
sbin_SCRIPTS = dagger
sbin_SCRIPTS = dagger migrate
27 changes: 23 additions & 4 deletions src/confd/bin/bootstrap
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@
########################################################################
STATUS=""

# Log functions
critical()
{
logger -i -p user.crit -t bootstrap "$1" 2>/dev/null || echo "$1"
}

err()
{
logger -i -p user.err -t bootstrap "$1" 2>/dev/null || echo "$1"
}

# When logging errors, generating /etc/issue* or /etc/banner (SSH)
. /etc/os-release

Expand All @@ -33,8 +44,7 @@ if [ "$1" = "-f" ] && [ -f "$2" ]; then
fi

if [ ! -f "$RC" ]; then
logger -sik -p user.error -t bootstrap "Missing rc file $RC" 2>/dev/null \
|| echo "Missing rc file $RC"
err "Missing rc file $RC"
exit 99
fi

Expand Down Expand Up @@ -72,7 +82,7 @@ collate()
# Report error on console, syslog, and set login banners for getty + ssh
console_error()
{
logger -p user.crit -t bootstrap "$1"
critical "$1"

# shellcheck disable=SC3037
/bin/echo -e "\n\n\e[31mCRITICAL BOOTSTRAP ERROR\n$1\e[0m\n" > /dev/console
Expand Down Expand Up @@ -157,11 +167,20 @@ else
fi
rc=$?

# Ensure 'admin' group users always have access
chgrp wheel "$CFG_PATH_"
chmod g+w "$CFG_PATH_"

# Ensure factory-config has correct syntax
if ! migrate -cq "$INIT_DATA"; then
if migrate -iq -b "${INIT_DATA%.*}.bak" "$INIT_DATA"; then
err "${INIT_DATA}: found and fixed old syntax!"
fi
fi

if ! sysrepoctl -z "$INIT_DATA"; then
rc=$?
logger -sik -p user.error "Failed loading factory-default datastore"
err "Failed loading factory-default datastore"
else
# Clear running-config so we can load/create startup in the next step
temp=$(mktemp)
Expand Down
Loading

0 comments on commit 3358687

Please sign in to comment.