mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-26 18:53:01 +02:00
Compare commits
+4
-64
@@ -1,67 +1,7 @@
|
||||
aarch64
|
||||
=======
|
||||
|
||||
Microchip SparX-5i PCB135 (eMMC)
|
||||
--------------------------------
|
||||
|
||||
At present, only FIT images are supported via the SparX-5i eval
|
||||
board's U-Boot, which must contain a valid load address. If you are
|
||||
using this board, after selecting your base configuration, run the
|
||||
following command to supplement the existing config with the required
|
||||
FIT options:
|
||||
|
||||
make board-enable-sparx-fit
|
||||
|
||||
### Unbricking
|
||||
|
||||
1. Don't load a corrupt bootloader to begin with
|
||||
2. Done
|
||||
|
||||
If, for some reason, you didn't manage to follow step 1, you can use
|
||||
the `FLASH_PROG`(`J4`) connector on the board to access the SPI flash
|
||||
directly.
|
||||
|
||||
Using a [Bus Blaster][BB3] in combination with [dangerspi][dangerspi],
|
||||
you can load a new bootloader. The schematic below details how to
|
||||
connect the Bus Blaster to the board, but the concept should be
|
||||
portable to any debugger/device based around an FTDI2232 chip.
|
||||
|
||||
```
|
||||
Bus Blaster: FLASH_PROG (J4):
|
||||
.---. .---.
|
||||
VTG >o o| VTG SCK >o o| GND
|
||||
TRST |o o| GND MISO |o o| #RESET_FLASH
|
||||
TDI |o o| GND o o| 3V3
|
||||
TMS |o o| GND #CS |o o| #SYSRESET
|
||||
TCK o o| GND MOSI |o o| GND
|
||||
RTCK o o| GND '---'
|
||||
TDO |o o| GND
|
||||
TSRST |o o| GND
|
||||
DBGRQ |o o| GND
|
||||
DBGACK |o o| GND
|
||||
'---'
|
||||
```
|
||||
|
||||
| Bus Blaster | FLASH_PROG |
|
||||
|-------------|----------------|
|
||||
| `VTG` | `3V3` |
|
||||
| `GND` | `GND` |
|
||||
| | |
|
||||
| `VTG` | `#RESET_FLASH` |
|
||||
| `GND` | `#SYSRESET` |
|
||||
| | |
|
||||
| `TDI` | `MOSI` |
|
||||
| `TMS` | `#CS` |
|
||||
| `TCK` | `SCK` |
|
||||
| `TDO` | `MISO` |
|
||||
|
||||
With the cable in place, build U-Boot with the correct configuration:
|
||||
|
||||
make fireant_boot_defconfig && make
|
||||
|
||||
To flash it to the board, connect the cable to `J4` and run:
|
||||
|
||||
make board-sparx-flash-uboot
|
||||
|
||||
[BB3]: http://dangerousprototypes.com/docs/Bus_Blaster#Bus_Blaster_v3
|
||||
[dangerspi]: https://github.com/wkz/dangerspi
|
||||
Board Specific Documentation
|
||||
----------------------------
|
||||
- [Marvell CN9130-CRB](cn9130-crb/)
|
||||
- [Microchip SparX-5i PCB135 (eMMC)](sparx5-pcb135/)
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
Marvell CN9130-CRB
|
||||
==================
|
||||
|
||||
## Build instructions
|
||||
|
||||
### Bootloader
|
||||
|
||||
Build the bootloader from the supplied `defconfig`. It might be useful
|
||||
to build in a separate output directory if you want to build Infix
|
||||
from the same working tree later:
|
||||
|
||||
make O=$(pwd)/x-cn9130-boot cn9130_crb_boot_defconfig
|
||||
cd x-cn9130-boot
|
||||
make
|
||||
|
||||
The artifact of interest is called `flash-image.bin`, which will be
|
||||
located in the `images/` directory once the build completes.
|
||||
|
||||
|
||||
### Infix
|
||||
|
||||
> If you do not want to build Infix from source, feel free to use a pre-built [release]
|
||||
|
||||
The standard `aarch64_defconfig` is compatible with this board:
|
||||
|
||||
make O=$(pwd)/x-aarch64 aarch64_defconfig
|
||||
cd x-aarch64
|
||||
make
|
||||
|
||||
Two artifacts from the `images/` directory of this build are required
|
||||
to provision a new board:
|
||||
|
||||
- `rootfs.itb`: Netbootable image
|
||||
- `infix-aarch64.pkg`: Standard upgrade bundle
|
||||
|
||||
## Provisioning
|
||||
|
||||
The overall provisioning flow, in which each step in described in
|
||||
details in the following sections, is as follows:
|
||||
|
||||
- Strap board to boot from SPI FLASH
|
||||
- Load `flash-image.bin` over UART
|
||||
- Burn `flash-image.bin` to SPI FLASH
|
||||
- Netboot `rootfs.itb`
|
||||
- Run Infix's built-in provisioning script
|
||||
- Reboot, now booting from the primary partition of the SD-card
|
||||
|
||||
#### Strap Board to Boot from SPI FLASH
|
||||
|
||||
By default, the board is strapped to boot from eMMC. However, Infix
|
||||
assumes that the board will boot from SPI FLASH. Therefore we have to
|
||||
ensure that the DIP switches of `SW2` on the board selects a
|
||||
`BOOT_MODE` of `0x32`, meaning that the switches on positions 4, 3,
|
||||
and 1 should be enabled (i.e. these need to be tied to ground):
|
||||
|
||||
```
|
||||
.-----. .-----. .-----.
|
||||
| | | | | PWR |
|
||||
| SIM | | uSD | | |
|
||||
| | | | '-----'
|
||||
'-----' '-----'
|
||||
|
||||
|
|
||||
v
|
||||
.-----. .-----.
|
||||
| SW1 | | SW2 |
|
||||
'-----' '-----'
|
||||
.--------------------------.
|
||||
| |
|
||||
| |
|
||||
| CN9130 |
|
||||
```
|
||||
|
||||
#### UART Boot U-Boot
|
||||
|
||||
Make sure that:
|
||||
- [mvebu64boot] is installed and available in your shell's `$PATH`
|
||||
- No other program is attached to `ttyUSB0`
|
||||
- No power is applied to the board
|
||||
|
||||
1. Start `mvebu64boot`:
|
||||
|
||||
mvebu64boot -b /path/to/flash-image.bin /dev/ttyUSB0
|
||||
|
||||
2. Apply power
|
||||
|
||||
As soon as `mvebu64boot` completes, attach to the serial port,
|
||||
e.g. using `screen(1)`, or `console(1)` and stop the normal boot
|
||||
process by hitting any key.
|
||||
|
||||
#### Burn U-Boot to SPI FLASH
|
||||
|
||||
Make sure that:
|
||||
- `eth1` is connected to a machine which serves `flash-image.bin` over
|
||||
TFTP
|
||||
- U-Boot can reach the TFTP server. If the neighboring machine is also
|
||||
set up as a DHCP server, simply run the command `dhcp -`
|
||||
|
||||
To burn the bootloader to SPI FLASH, run the `bubt` command:
|
||||
|
||||
bubt flash-image.bin
|
||||
|
||||
Once the command completes, reset the board to verify that it can now
|
||||
boot unassisted:
|
||||
|
||||
reset
|
||||
|
||||
#### Boot up `rootfs.itb`
|
||||
|
||||
U-Boot will automatically fallback to netboot since the SD-card is
|
||||
still blank. Make sure that the PC provides the path to `rootfs.itb`
|
||||
in DHCP option 67 ("bootfile").
|
||||
|
||||
#### Install Firmware
|
||||
|
||||
Login as `admin`/`admin`, setup networking to the PC, ensure that the date on
|
||||
the device is reasonably correct, and run the provisioning script:
|
||||
|
||||
admin@infix:~$ sudo -i
|
||||
root@infix:~$ udhcpc -i e28
|
||||
root@infix:~$ date -us YYYY-MM-DD
|
||||
root@infix:~$ /libexec/infix/prod/provision tftp://<PC-IP>/infix-aarch64.pkg /dev/mmcblk0
|
||||
|
||||
After successful completion, the device is fully provisioned. On the
|
||||
next boot, the device will boot of its own accord from the primary
|
||||
SD-card partition.
|
||||
|
||||
> If possible, serve `infix-aarch64.pkg` over HTTP instead, as
|
||||
> libcurl's TFTP implementation is quite slow.
|
||||
|
||||
[release]: https://github.com/kernelkit/infix/releases
|
||||
[mvebu64boot]: https://github.com/addiva-elektronik/mvebu64boot
|
||||
@@ -16,36 +16,7 @@ run ixboot
|
||||
";
|
||||
boot_targets = "mmc1";
|
||||
ethprime = "eth1";
|
||||
|
||||
/* Uncomment this if you're debugging U-Boot
|
||||
*
|
||||
* This will allow you to break out of the
|
||||
* normal boot flow and into the interactive
|
||||
* console.
|
||||
*
|
||||
* To upgrade U-Boot itself, simply set the
|
||||
* `bootfile` variable to the path of
|
||||
* `flash-image.bin` on your TFTP server, then
|
||||
* issue `run ixupgradeboot`.
|
||||
*/
|
||||
/* bootdelay = "2"; */
|
||||
/* ixupgradeboot = " */
|
||||
/* dhcp */
|
||||
/* setexpr fileblks ${filesize} + 0x1ff */
|
||||
/* setexpr fileblks ${fileblks} / 0x200 */
|
||||
|
||||
/* mmc dev 1 */
|
||||
/* part start mmc 1 boot bootstart */
|
||||
/* part size mmc 1 boot bootsize */
|
||||
|
||||
|
||||
/* mmc erase ${bootstart} ${bootsize} */
|
||||
/* mmc write ${fileaddr} ${bootstart} ${fileblks} */
|
||||
/* "; */
|
||||
bootdelay = "1";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/* &cp0_eth0 { */
|
||||
/* phy-mode = "10gbase-r"; */
|
||||
/* }; */
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
CONFIG_DEVICE_TREE_INCLUDES="infix-env.dtsi infix-key.dtsi cn9130-crb-env.dtsi"
|
||||
CONFIG_SYS_PROMPT="(cn9130-crb) "
|
||||
|
||||
CONFIG_ENV_IS_NOWHERE=y
|
||||
# CONFIG_ENV_IS_IN_MMC is not set
|
||||
CONFIG_MVEBU_SPI_BOOT=y
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2020 Marvell International Ltd.
|
||||
*/
|
||||
|
||||
#include "cn9130-crb.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Marvell Armada CN9130-CRB-A";
|
||||
|
||||
chosen {
|
||||
infix {
|
||||
/* "admin" */
|
||||
factory-password-hash = "$5$mI/zpOAqZYKLC2WU$i7iPzZiIjOjrBF3NyftS9CCq8dfYwHwrmUK097Jca9A";
|
||||
usb-ports = <&cp0_usb3_1>;
|
||||
usb-port-names = "USB";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&cp0_pcie0 {
|
||||
status = "okay";
|
||||
num-lanes = <4>;
|
||||
num-viewport = <8>;
|
||||
/* Generic PHY, providing serdes lanes */
|
||||
phys = <&cp0_comphy0 0
|
||||
&cp0_comphy1 0
|
||||
&cp0_comphy2 0
|
||||
&cp0_comphy3 0>;
|
||||
iommu-map =
|
||||
<0x0 &smmu 0x480 0x20>,
|
||||
<0x100 &smmu 0x4a0 0x20>,
|
||||
<0x200 &smmu 0x4c0 0x20>;
|
||||
iommu-map-mask = <0x031f>;
|
||||
};
|
||||
|
||||
&cp0_usb3_0 {
|
||||
status = "okay";
|
||||
usb-phy = <&cp0_usb3_0_phy0>;
|
||||
phy-names = "usb";
|
||||
};
|
||||
|
||||
&cp0_usb3_1 {
|
||||
status = "okay";
|
||||
usb-phy = <&cp0_usb3_0_phy1>;
|
||||
phy-names = "usb";
|
||||
};
|
||||
|
||||
&cp0_eth2 {
|
||||
status = "okay";
|
||||
phy-mode = "2500base-x";
|
||||
};
|
||||
|
||||
&cp0_pinctrl {
|
||||
cp0_expander0_pins: cp0-expander-pins-0 {
|
||||
marvell,pins = "mpp39";
|
||||
marvell,function = "gpio";
|
||||
};
|
||||
};
|
||||
|
||||
&expander0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&cp0_expander0_pins>;
|
||||
interrupt-parent = <&cp0_gpio2>;
|
||||
interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells=<2>;
|
||||
microchip,irq-mirror;
|
||||
};
|
||||
@@ -178,6 +178,7 @@ CONFIG_NET_SCHED=y
|
||||
CONFIG_NET_CLS_BASIC=y
|
||||
CONFIG_NET_CLS_BPF=y
|
||||
CONFIG_NET_CLS_FLOWER=y
|
||||
CONFIG_DCB=y
|
||||
CONFIG_NETLINK_DIAG=y
|
||||
CONFIG_MPLS=y
|
||||
CONFIG_NET_MPLS_GSO=y
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
Microchip SparX-5i PCB135 (eMMC)
|
||||
================================
|
||||
|
||||
At present, only FIT images are supported via the SparX-5i eval
|
||||
board's U-Boot, which must contain a valid load address. If you are
|
||||
using this board, after selecting your base configuration, run the
|
||||
following command to supplement the existing config with the required
|
||||
FIT options:
|
||||
|
||||
make board-enable-sparx-fit
|
||||
|
||||
## Unbricking
|
||||
|
||||
1. Don't load a corrupt bootloader to begin with
|
||||
2. Done
|
||||
|
||||
If, for some reason, you didn't manage to follow step 1, you can use
|
||||
the `FLASH_PROG`(`J4`) connector on the board to access the SPI flash
|
||||
directly.
|
||||
|
||||
Using a [Bus Blaster][BB3] in combination with [dangerspi][dangerspi],
|
||||
you can load a new bootloader. The schematic below details how to
|
||||
connect the Bus Blaster to the board, but the concept should be
|
||||
portable to any debugger/device based around an FTDI2232 chip.
|
||||
|
||||
```
|
||||
Bus Blaster: FLASH_PROG (J4):
|
||||
.---. .---.
|
||||
VTG >o o| VTG SCK >o o| GND
|
||||
TRST |o o| GND MISO |o o| #RESET_FLASH
|
||||
TDI |o o| GND o o| 3V3
|
||||
TMS |o o| GND #CS |o o| #SYSRESET
|
||||
TCK o o| GND MOSI |o o| GND
|
||||
RTCK o o| GND '---'
|
||||
TDO |o o| GND
|
||||
TSRST |o o| GND
|
||||
DBGRQ |o o| GND
|
||||
DBGACK |o o| GND
|
||||
'---'
|
||||
```
|
||||
|
||||
| Bus Blaster | FLASH_PROG |
|
||||
|-------------|----------------|
|
||||
| `VTG` | `3V3` |
|
||||
| `GND` | `GND` |
|
||||
| | |
|
||||
| `VTG` | `#RESET_FLASH` |
|
||||
| `GND` | `#SYSRESET` |
|
||||
| | |
|
||||
| `TDI` | `MOSI` |
|
||||
| `TMS` | `#CS` |
|
||||
| `TCK` | `SCK` |
|
||||
| `TDO` | `MISO` |
|
||||
|
||||
With the cable in place, build U-Boot with the correct configuration:
|
||||
|
||||
make fireant_boot_defconfig && make
|
||||
|
||||
To flash it to the board, connect the cable to `J4` and run:
|
||||
|
||||
make board-sparx-flash-uboot
|
||||
|
||||
[BB3]: http://dangerousprototypes.com/docs/Bus_Blaster#Bus_Blaster_v3
|
||||
[dangerspi]: https://github.com/wkz/dangerspi
|
||||
@@ -1,67 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# Generate a self signed certificate with unlimited expire time
|
||||
|
||||
import argparse
|
||||
|
||||
from cryptography import x509
|
||||
from cryptography.x509.oid import NameOID
|
||||
from cryptography.hazmat.primitives import hashes, serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
# Generate private key
|
||||
private_key = rsa.generate_private_key(
|
||||
public_exponent=65537,
|
||||
key_size=2048,
|
||||
backend=default_backend()
|
||||
|
||||
)
|
||||
|
||||
parser = argparse.ArgumentParser(description="Generate a self signed certificate")
|
||||
parser.add_argument('--country', required=True, help="Set country")
|
||||
parser.add_argument('--state', required=True, help="Set state or province name")
|
||||
parser.add_argument('--city', required=True, help="Set city name")
|
||||
parser.add_argument('--organisation', required=True, help="Set organisation name")
|
||||
parser.add_argument('--organisation-unit', required=True, help="Set organisation unit name")
|
||||
parser.add_argument('--common-name', required=True, help="Set common name")
|
||||
parser.add_argument('--out-certificate', required=True, help="Output certificate")
|
||||
parser.add_argument('--out-key', required=True, help="Output key")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Builder for certificate
|
||||
subject = issuer = x509.Name([
|
||||
x509.NameAttribute(NameOID.COUNTRY_NAME, args.country),
|
||||
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, args.state),
|
||||
x509.NameAttribute(NameOID.LOCALITY_NAME, args.city),
|
||||
x509.NameAttribute(NameOID.ORGANIZATION_NAME, args.organisation),
|
||||
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, args.organisation_unit),
|
||||
x509.NameAttribute(NameOID.COMMON_NAME, args.common_name),
|
||||
])
|
||||
certificate = x509.CertificateBuilder().subject_name(
|
||||
subject
|
||||
).issuer_name(
|
||||
issuer
|
||||
).public_key(
|
||||
private_key.public_key()
|
||||
).serial_number(
|
||||
x509.random_serial_number()
|
||||
).not_valid_before(
|
||||
datetime(2000, 1, 1)
|
||||
).not_valid_after(
|
||||
datetime(9999, 1, 1)
|
||||
).add_extension(
|
||||
x509.SubjectAlternativeName([x509.DNSName(args.common_name)]),
|
||||
critical=False,
|
||||
).sign(private_key, hashes.SHA256(), default_backend())
|
||||
|
||||
# Serialize certificate and private key
|
||||
with open(args.out_certificate, "wb") as f:
|
||||
f.write(certificate.public_bytes(serialization.Encoding.PEM))
|
||||
with open(args.out_key, "wb") as f:
|
||||
f.write(private_key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.PKCS8,
|
||||
encryption_algorithm=serialization.NoEncryption()
|
||||
))
|
||||
@@ -0,0 +1,10 @@
|
||||
Reserved facilities for logging in various subsystems:
|
||||
|
||||
- local0: rauc
|
||||
- local1: containers
|
||||
- local2:
|
||||
- local3:
|
||||
- local5:
|
||||
- local6: reserved
|
||||
- local7: nginx
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
. /etc/partion-uuid
|
||||
. /etc/partition-uuid
|
||||
|
||||
disk=$1
|
||||
bootoffs=$2
|
||||
@@ -28,11 +28,11 @@ else
|
||||
fi
|
||||
|
||||
sgdisk \
|
||||
-o \
|
||||
-Z \
|
||||
-n1:${bootoffs}:+${bootsize} -t1:8301 -c1:boot \
|
||||
-n2::+${auxsize} -t2:8301 -c2:aux -u2:${AUX_UUID}\
|
||||
-n2::+${auxsize} -t2:8301 -c2:aux -u2:${AUX_UUID} \
|
||||
-n3::+${imgsize} -t3:8300 -c3:primary -u3:${PRIMARY_UUID} \
|
||||
-n4::+${imgsize} -t4:8300 -c4:secondary -u4:${SECONDARY_UUID}\
|
||||
-n4::+${imgsize} -t4:8300 -c4:secondary -u4:${SECONDARY_UUID} \
|
||||
-n5::+${cfgsize} -t5:8302 -c5:cfg \
|
||||
-n6:: -t6:8310 -c6:var \
|
||||
-p \
|
||||
|
||||
@@ -54,7 +54,6 @@ BR2_PACKAGE_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
|
||||
BR2_PACKAGE_UBOOT_TOOLS_FIT_CHECK_SIGN=y
|
||||
BR2_PACKAGE_UBOOT_TOOLS_MKENVIMAGE=y
|
||||
BR2_PACKAGE_PYTHON3=y
|
||||
BR2_PACKAGE_PYTHON_CRYPTOGRAPHY=y
|
||||
BR2_PACKAGE_PYTHON_GUNICORN=y
|
||||
BR2_PACKAGE_LIBSSH_OPENSSL=y
|
||||
BR2_PACKAGE_LIBSSH2=y
|
||||
@@ -130,6 +129,7 @@ INFIX_SUPPORT="mailto:kernelkit@googlegroups.com"
|
||||
BR2_PACKAGE_CONFD=y
|
||||
BR2_PACKAGE_CURIOS_HTTPD=y
|
||||
BR2_PACKAGE_EXECD=y
|
||||
BR2_PACKAGE_GENCERT=y
|
||||
BR2_PACKAGE_STATD=y
|
||||
BR2_PACKAGE_FACTORY=y
|
||||
BR2_PACKAGE_FINIT_PLUGIN_HOTPLUG=y
|
||||
@@ -142,6 +142,7 @@ BR2_PACKAGE_IITO=y
|
||||
BR2_PACKAGE_K8S_LOGGER=y
|
||||
BR2_PACKAGE_KEYACK=y
|
||||
BR2_PACKAGE_KLISH_PLUGIN_INFIX=y
|
||||
BR2_PACKAGE_LANDING=y
|
||||
BR2_PACKAGE_LOWDOWN=y
|
||||
BR2_PACKAGE_MCD=y
|
||||
BR2_PACKAGE_MDNS_ALIAS=y
|
||||
|
||||
@@ -16,6 +16,8 @@ BR2_ROOTFS_POST_IMAGE_SCRIPT="${BR2_EXTERNAL_INFIX_PATH}/board/common/post-image
|
||||
# BR2_PACKAGE_IFUPDOWN_SCRIPTS is not set
|
||||
# BR2_TARGET_ROOTFS_TAR is not set
|
||||
BR2_TARGET_ARM_TRUSTED_FIRMWARE=y
|
||||
BR2_TARGET_ARM_TRUSTED_FIRMWARE_CUSTOM_VERSION=y
|
||||
BR2_TARGET_ARM_TRUSTED_FIRMWARE_CUSTOM_VERSION_VALUE="v2.9"
|
||||
BR2_TARGET_ARM_TRUSTED_FIRMWARE_PLATFORM="t9130"
|
||||
BR2_TARGET_ARM_TRUSTED_FIRMWARE_FIP=y
|
||||
BR2_TARGET_ARM_TRUSTED_FIRMWARE_UBOOT_AS_BL33=y
|
||||
@@ -36,8 +38,8 @@ BR2_PACKAGE_HOST_RAUC=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SUPPORT=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
|
||||
# SIGN_ENABLED is not set
|
||||
DISK_IMAGE=y
|
||||
TRUSTED_KEYS=y
|
||||
TRUSTED_KEYS_DEVELOPMENT=y
|
||||
DISK_IMAGE_BOOT_BIN=y
|
||||
DISK_IMAGE_BOOT_DATA="${BINARIES_DIR}/flash-image.bin"
|
||||
DISK_IMAGE_BOOT_OFFSET=0x00200000
|
||||
|
||||
@@ -13,7 +13,7 @@ BR2_TARGET_GENERIC_HOSTNAME="infix"
|
||||
BR2_TARGET_GENERIC_ISSUE="Infix by KernelKit"
|
||||
BR2_INIT_FINIT=y
|
||||
BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y
|
||||
BR2_ROOTFS_DEVICE_TABLE="system/device_table.txt ${BR2_EXTERNAL_INFIX_PATH}/board/common/xattrs ${BR2_EXTERNAL_INFIX_PATH}/board/netconf/xattrs"
|
||||
BR2_ROOTFS_DEVICE_TABLE="system/device_table.txt ${BR2_EXTERNAL_INFIX_PATH}/board/common/xattrs"
|
||||
# BR2_TARGET_ENABLE_ROOT_LOGIN is not set
|
||||
BR2_SYSTEM_BIN_SH_BASH=y
|
||||
BR2_TARGET_GENERIC_GETTY_PORT="@console"
|
||||
@@ -22,8 +22,8 @@ BR2_SYSTEM_DHCP="eth0"
|
||||
BR2_ENABLE_LOCALE_WHITELIST="C en_US en_CA"
|
||||
BR2_GENERATE_LOCALE="en_US en_CA"
|
||||
BR2_TARGET_TZ_INFO=y
|
||||
BR2_ROOTFS_OVERLAY="${BR2_EXTERNAL_INFIX_PATH}/board/common/rootfs ${BR2_EXTERNAL_INFIX_PATH}/board/aarch64/rootfs ${BR2_EXTERNAL_INFIX_PATH}/board/netconf/rootfs"
|
||||
BR2_ROOTFS_POST_BUILD_SCRIPT="$(BR2_EXTERNAL_INFIX_PATH)/board/aarch64/r2s/post-build.sh ${BR2_EXTERNAL_INFIX_PATH}/board/common/post-build.sh ${BR2_EXTERNAL_INFIX_PATH}/board/netconf/post-build.sh"
|
||||
BR2_ROOTFS_OVERLAY="${BR2_EXTERNAL_INFIX_PATH}/board/common/rootfs ${BR2_EXTERNAL_INFIX_PATH}/board/aarch64/rootfs"
|
||||
BR2_ROOTFS_POST_BUILD_SCRIPT="$(BR2_EXTERNAL_INFIX_PATH)/board/aarch64/r2s/post-build.sh ${BR2_EXTERNAL_INFIX_PATH}/board/common/post-build.sh"
|
||||
BR2_ROOTFS_POST_IMAGE_SCRIPT="${BR2_EXTERNAL_INFIX_PATH}/board/common/post-image.sh support/scripts/genimage.sh"
|
||||
BR2_ROOTFS_POST_SCRIPT_ARGS="-c $(BR2_EXTERNAL_INFIX_PATH)/board/aarch64/r2s/genimage.cfg"
|
||||
BR2_LINUX_KERNEL=y
|
||||
@@ -64,7 +64,6 @@ BR2_PACKAGE_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
|
||||
BR2_PACKAGE_UBOOT_TOOLS_FIT_CHECK_SIGN=y
|
||||
BR2_PACKAGE_UBOOT_TOOLS_MKENVIMAGE=y
|
||||
BR2_PACKAGE_PYTHON3=y
|
||||
BR2_PACKAGE_PYTHON_CRYPTOGRAPHY=y
|
||||
BR2_PACKAGE_PYTHON_GUNICORN=y
|
||||
BR2_PACKAGE_CA_CERTIFICATES=y
|
||||
BR2_PACKAGE_LIBSSH_OPENSSL=y
|
||||
@@ -159,6 +158,7 @@ INFIX_DOC="https://github.com/kernelkit/infix/tree/main/doc"
|
||||
INFIX_SUPPORT="mailto:kernelkit@googlegroups.com"
|
||||
BR2_PACKAGE_CONFD=y
|
||||
BR2_PACKAGE_EXECD=y
|
||||
BR2_PACKAGE_GENCERT=y
|
||||
BR2_PACKAGE_STATD=y
|
||||
BR2_PACKAGE_FACTORY=y
|
||||
BR2_PACKAGE_FINIT_PLUGIN_HOTPLUG=y
|
||||
@@ -171,6 +171,7 @@ BR2_PACKAGE_IITO=y
|
||||
BR2_PACKAGE_K8S_LOGGER=y
|
||||
BR2_PACKAGE_KEYACK=y
|
||||
BR2_PACKAGE_KLISH_PLUGIN_INFIX=y
|
||||
BR2_PACKAGE_LANDING=y
|
||||
BR2_PACKAGE_LOWDOWN=y
|
||||
BR2_PACKAGE_MCD=y
|
||||
BR2_PACKAGE_MDNS_ALIAS=y
|
||||
|
||||
@@ -49,7 +49,6 @@ BR2_PACKAGE_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
|
||||
BR2_PACKAGE_UBOOT_TOOLS_FIT_CHECK_SIGN=y
|
||||
BR2_PACKAGE_UBOOT_TOOLS_MKENVIMAGE=y
|
||||
BR2_PACKAGE_PYTHON3=y
|
||||
BR2_PACKAGE_PYTHON_CRYPTOGRAPHY=y
|
||||
BR2_PACKAGE_PYTHON_GUNICORN=y
|
||||
BR2_PACKAGE_LIBSSH_OPENSSL=y
|
||||
BR2_PACKAGE_LIBSSH2=y
|
||||
@@ -133,6 +132,7 @@ INFIX_SUPPORT="mailto:kernelkit@googlegroups.com"
|
||||
BR2_PACKAGE_CONFD=y
|
||||
BR2_PACKAGE_CURIOS_HTTPD=y
|
||||
BR2_PACKAGE_EXECD=y
|
||||
BR2_PACKAGE_GENCERT=y
|
||||
BR2_PACKAGE_STATD=y
|
||||
BR2_PACKAGE_FACTORY=y
|
||||
BR2_PACKAGE_FINIT_PLUGIN_HOTPLUG=y
|
||||
@@ -145,6 +145,7 @@ BR2_PACKAGE_IITO=y
|
||||
BR2_PACKAGE_K8S_LOGGER=y
|
||||
BR2_PACKAGE_KEYACK=y
|
||||
BR2_PACKAGE_KLISH_PLUGIN_INFIX=y
|
||||
BR2_PACKAGE_LANDING=y
|
||||
BR2_PACKAGE_LOWDOWN=y
|
||||
BR2_PACKAGE_MCD=y
|
||||
BR2_PACKAGE_MDNS_ALIAS=y
|
||||
|
||||
@@ -4,6 +4,43 @@ Change Log
|
||||
All notable changes to the project are documented in this file.
|
||||
|
||||
|
||||
[v24.04.2][] - 2024-05-15
|
||||
-------------------------
|
||||
|
||||
### Changes
|
||||
- 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
|
||||
|
||||
### Fixes
|
||||
- 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
|
||||
|
||||
|
||||
[v24.04.1][] - 2024-05-03
|
||||
-------------------------
|
||||
|
||||
### 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
|
||||
|
||||
### Fixes
|
||||
- Add missing LICENSE hash for factory reset tool
|
||||
- Fix #424: regression, root user can log in without password
|
||||
|
||||
|
||||
[v24.04.0][] - 2024-04-30
|
||||
-------------------------
|
||||
|
||||
@@ -844,6 +881,8 @@ 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.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
|
||||
[v24.02.0]: https://github.com/kernelkit/infix/compare/v23.11.0...v24.02.0
|
||||
[v23.11.0]: https://github.com/kernelkit/infix/compare/v23.10.0...v23.11.0
|
||||
|
||||
@@ -67,7 +67,7 @@ $ sudo apt install bc binutils build-essential bzip2 cpio \
|
||||
diffutils file findutils git gzip \
|
||||
libncurses-dev libssl-dev perl patch \
|
||||
python3 rsync sed tar unzip wget \
|
||||
autopoint
|
||||
autopoint bison flex
|
||||
```
|
||||
|
||||
For testing, a few more tools and services are required on your system:
|
||||
|
||||
+454
-7
@@ -331,6 +331,58 @@ admin@infix.local%eth0's password: *****
|
||||
~$
|
||||
```
|
||||
|
||||
### Controlling LEDs for Production Tests
|
||||
|
||||
As part of production testing, LED verification is often expected to
|
||||
be performed. Infix uses standard [Linux support for LED
|
||||
management][6], where LEDs appear in the file system under
|
||||
/sys/class/leds and can be controlled using *echo* command. `sudo`
|
||||
privileges are required.
|
||||
|
||||
When interacting with LEDs this way, first disable the Infix *iitod*
|
||||
daemon to avoid conflicting LED control.
|
||||
|
||||
```
|
||||
~$ ssh admin@example.local 'initctl stop iitod'
|
||||
```
|
||||
|
||||
Then run the test, e.g., visually control that a red LED labeled
|
||||
'LAN' is working.
|
||||
|
||||
```
|
||||
~$ ssh admin@example.local 'echo none | sudo tee /sys/class/leds/red\:lan/trigger'
|
||||
~$ ssh admin@example.local 'echo 1 | sudo tee /sys/class/leds/red\:lan/brightness'
|
||||
```
|
||||
|
||||
To turn off the same LED, run the following commands.
|
||||
|
||||
```
|
||||
~$ ssh admin@example.local 'echo none | sudo tee /sys/class/leds/red\:lan/trigger'
|
||||
~$ ssh admin@example.local 'echo 0 | sudo tee /sys/class/leds/red\:lan/brightness'
|
||||
```
|
||||
When done with LED testing, enable Infix *iitod* daemon again.
|
||||
|
||||
```
|
||||
~$ ssh admin@example.local 'initctl start iitod'
|
||||
```
|
||||
|
||||
### Reading Power Feed Status for Production Tests
|
||||
|
||||
As part of production tests, verification of Power Feed sensors is
|
||||
often expected to be performed. Infix uses standard [Linux support for
|
||||
Power management][7], where power sources appear in the file system
|
||||
under /sys/class/power_supply. The following example reads status of
|
||||
two power supplies named *pwr1* and *pwr2*.
|
||||
|
||||
```
|
||||
~$ ssh admin@example 'cat /sys/class/power_supply/pwr1/online'
|
||||
1
|
||||
~$ ssh admin@example 'cat /sys/class/power_supply/pwr2/online'
|
||||
0
|
||||
~$
|
||||
```
|
||||
Here, only *pwr1* happened to have power.
|
||||
|
||||
|
||||
## Examples using SSH and sysrepocfg
|
||||
|
||||
@@ -346,10 +398,10 @@ See [sysrepocfg][4] for information. Examples below will utilize
|
||||
|
||||
|
||||
- `sysrepocfg -I FILE -fjson -d DATABASE` to import/write a JSON
|
||||
formatted configuration file to the specificed database.
|
||||
formatted configuration file to the specified database.
|
||||
- `sysrepocfg -E FILE -fjson -d DATABASE` to edit/merge JSON formatted
|
||||
configuration in FILE with the specificed database.
|
||||
- `sysrepocfg -R FILE -fjson` to execute remote procdure call (RPC) defined in
|
||||
configuration in FILE with the specified database.
|
||||
- `sysrepocfg -R FILE -fjson` to execute remote procedure call (RPC) defined in
|
||||
FILE (JSON formatted).
|
||||
- `sysrepocfg -X -fjson -d DATABASE -x xpath` to read configuration or
|
||||
status from specified database.
|
||||
@@ -360,7 +412,7 @@ configuration. Exporting (-X) could operate on configuration (e.g.,
|
||||
`-d running`) or status (`-d operational`).
|
||||
|
||||
Some commands require a file as input. In examples below we assume
|
||||
it been transfered to Infix in advance, e.g. using `scp` as shown below.
|
||||
it been transferred to Infix in advance, e.g. using `scp` as shown below.
|
||||
|
||||
```
|
||||
~$ cat file.json
|
||||
@@ -388,7 +440,7 @@ it been transfered to Infix in advance, e.g. using `scp` as shown below.
|
||||
```
|
||||
See [Factory Reset](#factory-reset) for another (simpler) alternative.
|
||||
|
||||
If you only wish to copy factory config to running config the
|
||||
If it is only wished to copy factory config to running config the
|
||||
following RPC is available
|
||||
|
||||
```
|
||||
@@ -601,7 +653,7 @@ on interface *e0*.
|
||||
~$
|
||||
```
|
||||
|
||||
### Backup Configuration Using sysrepocfg And scp
|
||||
### <a id="backup"></a> Backup Configuration Using sysrepocfg And scp
|
||||
|
||||
Displaying running or startup configuration is possible with
|
||||
`sysrepocfg -X`, as shown below.
|
||||
@@ -637,7 +689,7 @@ startup configuration (not running).
|
||||
~$
|
||||
```
|
||||
|
||||
### Restore Configuration Using sysrepocfg and ssh/scp
|
||||
### <a id="restore"></a> Restore Configuration Using sysrepocfg and ssh/scp
|
||||
|
||||
|
||||
To restore a backup configuration to startup, the simplest way is to
|
||||
@@ -688,7 +740,402 @@ An alternative is to write it to a temporary file, and use `sysrepocfg
|
||||
~$
|
||||
```
|
||||
|
||||
### Read Out Hardware Information Using sysrepocfg
|
||||
|
||||
Infix supports IETF Hardware YANG with augments for ONIE formatted
|
||||
production data stored in EEPROMs, if available. See Infix [VPD
|
||||
documentation][5], as well as *ietf-hardware* and *infix-hardware* YANG
|
||||
models for details.
|
||||
|
||||
|
||||
```
|
||||
~$ ssh admin@example.local 'sysrepocfg -X -fjson -d operational -x /ietf-hardware:hardware'
|
||||
{
|
||||
"ietf-hardware:hardware": {
|
||||
"component": [
|
||||
{
|
||||
"name": "product",
|
||||
"class": "infix-hardware:vpd",
|
||||
"serial-num": "12345",
|
||||
"model-name": "Switch2010",
|
||||
"mfg-date": "2024-01-30T16:42:37+00:00",
|
||||
"infix-hardware:vpd-data": {
|
||||
"product-name": "Switch2010",
|
||||
"part-number": "ABC123-001",
|
||||
"serial-number": "007",
|
||||
"mac-address": "00:53:00:01:23:45",
|
||||
"manufacture-date": "01/30/2024 16:42:37",
|
||||
"num-macs": 11,
|
||||
"manufacturer": "ACME Production",
|
||||
"vendor": "SanFran Networks"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "USB",
|
||||
"class": "infix-hardware:usb",
|
||||
"state": {
|
||||
"admin-state": "unlocked",
|
||||
"oper-state": "enabled"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
~$
|
||||
```
|
||||
|
||||
## Miscellaneous
|
||||
|
||||
### <a id="port-test-intro"></a> Port Configuration Example for Production Tests
|
||||
|
||||
As part of production tests, verification Ethernet ports are expected
|
||||
to be performed. A common way is to connect a test PC to two ports and
|
||||
send a *ping* traversing all ports. This can be achieved by using
|
||||
VLANs on the switch as described in this section. The resulting
|
||||
configuration file can be applied to the running configuration of the
|
||||
produced unit, e.g, use config file restore as described
|
||||
[above](#restore).
|
||||
|
||||
In this example we assume a 10 port switch, with ports e1-e10.
|
||||
|
||||
The following VLAN configuration and cable connections will be used:
|
||||
|
||||
| VLAN & Ports | Connect |
|
||||
|:------------------|:----------|
|
||||
| VLAN 10: e1 & e2 | e2 <=> e3 |
|
||||
| VLAN 20: e3 & e4 | e4 <=> e5 |
|
||||
| VLAN 30: e5 & e6 | e6 <=> e7 |
|
||||
| VLAN 40: e7 & e8 | e8 <=> e9 |
|
||||
| VLAN 50: e9 & e10 | |
|
||||
|
||||
The test PC is connected to e1 and e10 via different interfaces
|
||||
(alternatively, two different PCs are used).
|
||||
|
||||
> Configuration here is done via console. When configuring remotely
|
||||
> over SSH, remember to keep one IP address (the one used for the SSH
|
||||
> connection)! I.e., set a static IP address first, then perform the
|
||||
> VLAN configuration step."
|
||||
|
||||
#### Configuration at Start
|
||||
|
||||
Starting out, we assume a configuration where all ports are network
|
||||
interfaces (possibly with IPv6 enabled).
|
||||
|
||||
``` shell
|
||||
admin@example:/> show interfaces
|
||||
lo ethernet UP 00:00:00:00:00:00
|
||||
ipv4 127.0.0.1/8 (static)
|
||||
ipv6 ::1/128 (static)
|
||||
e1 ethernet LOWER-DOWN 00:53:00:06:11:01
|
||||
e2 ethernet LOWER-DOWN 00:53:00:06:11:02
|
||||
e3 ethernet LOWER-DOWN 00:53:00:06:11:03
|
||||
e4 ethernet LOWER-DOWN 00:53:00:06:11:04
|
||||
e5 ethernet LOWER-DOWN 00:53:00:06:11:05
|
||||
e6 ethernet LOWER-DOWN 00:53:00:06:11:06
|
||||
e7 ethernet LOWER-DOWN 00:53:00:06:11:07
|
||||
e8 ethernet LOWER-DOWN 00:53:00:06:11:08
|
||||
e9 ethernet LOWER-DOWN 00:53:00:06:11:09
|
||||
e10 ethernet UP 00:53:00:06:11:0a
|
||||
ipv6 fe80::0053:00ff:fe06:110a/64 (link-layer)
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
#### Creating Bridge and Adding Ports
|
||||
|
||||
The example below uses Infix documentation on [creating bridges][8].
|
||||
|
||||
``` shell
|
||||
admin@example:/> configure
|
||||
admin@example:/config/> edit interface br0
|
||||
admin@example:/config/interface/br0/> end
|
||||
admin@example:/config/> set interface e1 bridge-port bridge br0
|
||||
admin@example:/config/> set interface e2 bridge-port bridge br0
|
||||
admin@example:/config/> set interface e3 bridge-port bridge br0
|
||||
admin@example:/config/> set interface e4 bridge-port bridge br0
|
||||
admin@example:/config/> set interface e5 bridge-port bridge br0
|
||||
admin@example:/config/> set interface e6 bridge-port bridge br0
|
||||
admin@example:/config/> set interface e7 bridge-port bridge br0
|
||||
admin@example:/config/> set interface e8 bridge-port bridge br0
|
||||
admin@example:/config/> set interface e9 bridge-port bridge br0
|
||||
admin@example:/config/> set interface e10 bridge-port bridge br0
|
||||
admin@example:/config/>
|
||||
```
|
||||
|
||||
The interface status can be viewed using "show interface" after
|
||||
leaving configuration context. If configuration via SSH, first assign
|
||||
an IP address to br0 before *leaving* configuration context, e.g.,
|
||||
`set interface br0 ipv6 enabled` to get auto-configured IPv6
|
||||
address. Or skip 'leave' and stay in configuration context until done
|
||||
with all sections, including the one on [Add IP on
|
||||
Switch](#ip-on-switch).
|
||||
|
||||
``` shell
|
||||
admin@example:/config/> leave
|
||||
admin@example:/>
|
||||
admin@example:/> show interfaces
|
||||
INTERFACE PROTOCOL STATE DATA
|
||||
br0 bridge
|
||||
│ ethernet UP 00:53:00:06:11:01
|
||||
├ e1 bridge LOWER-DOWN
|
||||
├ e2 bridge LOWER-DOWN
|
||||
├ e3 bridge LOWER-DOWN
|
||||
├ e4 bridge LOWER-DOWN
|
||||
├ e5 bridge LOWER-DOWN
|
||||
├ e6 bridge LOWER-DOWN
|
||||
├ e7 bridge LOWER-DOWN
|
||||
├ e8 bridge LOWER-DOWN
|
||||
├ e9 bridge LOWER-DOWN
|
||||
└ e10 bridge FORWARDING
|
||||
lo ethernet UP 00:00:00:00:00:00
|
||||
ipv4 127.0.0.1/8 (static)
|
||||
ipv6 ::1/128 (static)
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
#### Assign VLANs to Ports
|
||||
|
||||
Then configure VLANs as outlined [above](#port-test-intro):
|
||||
default VID for ingress (PVID), which is done per port, and egress
|
||||
mode (untagged), which is done at the bridge level. See Infix
|
||||
[documentation for VLAN bridges][9] for more information.
|
||||
|
||||
|
||||
``` shell
|
||||
admin@example:/>
|
||||
admin@example:/> configure
|
||||
admin@example:/config/> set interface e1 bridge-port pvid 10
|
||||
admin@example:/config/> set interface e2 bridge-port pvid 10
|
||||
admin@example:/config/> set interface e3 bridge-port pvid 20
|
||||
admin@example:/config/> set interface e4 bridge-port pvid 20
|
||||
admin@example:/config/> set interface e5 bridge-port pvid 30
|
||||
admin@example:/config/> set interface e6 bridge-port pvid 30
|
||||
admin@example:/config/> set interface e7 bridge-port pvid 40
|
||||
admin@example:/config/> set interface e8 bridge-port pvid 40
|
||||
admin@example:/config/> set interface e9 bridge-port pvid 50
|
||||
admin@example:/config/> set interface e10 bridge-port pvid 50
|
||||
admin@example:/config/> edit interface br0
|
||||
admin@example:/config/interface/br0/> edit bridge vlans
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 10 untagged e1
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 10 untagged e2
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 20 untagged e3
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 20 untagged e4
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 30 untagged e5
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 30 untagged e6
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 40 untagged e7
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 40 untagged e8
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 50 untagged e9
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 50 untagged e10
|
||||
admin@example:/config/interface/br0/bridge/vlans/> leave
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
Interface status would now should something like the following
|
||||
|
||||
``` shell
|
||||
admin@example:/> show interfaces
|
||||
INTERFACE PROTOCOL STATE DATA
|
||||
br0 bridge
|
||||
│ ethernet UP 00:53:00:06:11:01
|
||||
├ e1 bridge LOWER-DOWN vlan:10u pvid:10
|
||||
├ e2 bridge LOWER-DOWN vlan:10u pvid:10
|
||||
├ e3 bridge LOWER-DOWN vlan:20u pvid:20
|
||||
├ e4 bridge LOWER-DOWN vlan:20u pvid:20
|
||||
├ e5 bridge LOWER-DOWN vlan:30u pvid:30
|
||||
├ e6 bridge LOWER-DOWN vlan:30u pvid:30
|
||||
├ e7 bridge LOWER-DOWN vlan:40u pvid:40
|
||||
├ e8 bridge LOWER-DOWN vlan:40u pvid:40
|
||||
├ e9 bridge LOWER-DOWN vlan:50u pvid:50
|
||||
└ e10 bridge FORWARDING vlan:50u pvid:50
|
||||
lo ethernet UP 00:00:00:00:00:00
|
||||
ipv4 127.0.0.1/8 (static)
|
||||
ipv6 ::1/128 (static)
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
#### Connect Cables and Test
|
||||
|
||||
We can now connect the PC to e1 and e10, and the other ports are
|
||||
patched according to plan [above](#port-test-intro). We should get
|
||||
link up on all ports.
|
||||
|
||||
``` shell
|
||||
admin@example:/> show interfaces
|
||||
INTERFACE PROTOCOL STATE DATA
|
||||
br0 bridge
|
||||
│ ethernet UP 00:53:00:06:11:01
|
||||
├ e1 bridge FORWARDING vlan:10u pvid:10
|
||||
├ e2 bridge FORWARDING vlan:10u pvid:10
|
||||
├ e3 bridge FORWARDING vlan:20u pvid:20
|
||||
├ e4 bridge FORWARDING vlan:20u pvid:20
|
||||
├ e5 bridge FORWARDING vlan:30u pvid:30
|
||||
├ e6 bridge FORWARDING vlan:30u pvid:30
|
||||
├ e7 bridge FORWARDING vlan:40u pvid:40
|
||||
├ e8 bridge FORWARDING vlan:40u pvid:40
|
||||
├ e9 bridge FORWARDING vlan:50u pvid:50
|
||||
└ e10 bridge FORWARDING vlan:50u pvid:50
|
||||
lo ethernet UP 00:00:00:00:00:00
|
||||
ipv4 127.0.0.1/8 (static)
|
||||
ipv6 ::1/128 (static)
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
Here we use IPv6 ping all hosts (ff02::1) on PC interface eth1 to
|
||||
check reachability to the other interface of the PC.
|
||||
|
||||
> A recommendation is to use network name spaces on PC to ensure
|
||||
> traffic really goes out to switch, instead of being looped
|
||||
> internally. Or use two PCs.
|
||||
|
||||
|
||||
``` shell
|
||||
~ $ ping -L ff02::1%eth1
|
||||
PING ff02::1%eth1(ff02::1%eth1) 56 data bytes
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=1 ttl=64 time=0.496 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=2 ttl=64 time=0.514 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=3 ttl=64 time=0.473 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=4 ttl=64 time=0.736 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=5 ttl=64 time=0.563 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=6 ttl=64 time=0.507 ms
|
||||
^C
|
||||
--- ff02::1%eth1 ping statistics ---
|
||||
6 packets transmitted, 6 received, 0% packet loss, time 5108ms
|
||||
rtt min/avg/max/mdev = 0.473/0.548/0.736/0.088 ms
|
||||
~ $
|
||||
```
|
||||
|
||||
We can verify that traffic goes through the switch by disconnecting
|
||||
one of the patch cables, e.g., between e4 and e5
|
||||
|
||||
``` shell
|
||||
~ $ ping -L ff02::1%eth1
|
||||
PING ff02::1%eth1(ff02::1%eth1) 56 data bytes
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=1 ttl=64 time=0.510 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=2 ttl=64 time=0.448 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=3 ttl=64 time=0.583 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=4 ttl=64 time=0.515 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=5 ttl=64 time=0.521 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=6 ttl=64 time=0.495 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=7 ttl=64 time=0.743 ms
|
||||
... Disconnecting patch cable, thus losing packets
|
||||
... and reconnecting again. Connectivity resumes.
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=16 ttl=64 time=0.961 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=17 ttl=64 time=0.513 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=18 ttl=64 time=0.794 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=19 ttl=64 time=0.755 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=20 ttl=64 time=0.779 ms
|
||||
^C
|
||||
--- ff02::1%eth1 ping statistics ---
|
||||
20 packets transmitted, 12 received, 40% packet loss, time 19432ms
|
||||
rtt min/avg/max/mdev = 0.448/0.634/0.961/0.156 ms
|
||||
~ $
|
||||
```
|
||||
|
||||
#### <a id="ip-on-switch"></a> Add IP Address on Switch
|
||||
|
||||
The configuration so far does not provide a means to connect to the
|
||||
switch management via SSH or NETCONF, as the switch has no IP
|
||||
address. The example below shows how to add the switch to VLAN 10 (as
|
||||
used for ports e1 and e2) and enables IPv6.
|
||||
|
||||
|
||||
``` shell
|
||||
admin@example:/config/> edit interface vlan10
|
||||
admin@example:/config/interface/vlan10/> set vlan lower-layer-if br0
|
||||
admin@example:/config/interface/vlan10/> set ipv6 enabled
|
||||
admin@example:/config/interface/vlan10/> show
|
||||
type vlan;
|
||||
ipv6 {
|
||||
enabled true;
|
||||
}
|
||||
vlan {
|
||||
tag-type c-vlan;
|
||||
id 10;
|
||||
lower-layer-if br0;
|
||||
}
|
||||
admin@example:/config/interface/vlan10/>
|
||||
admin@example:/config/interface/vlan10/> end
|
||||
admin@example:/config/> edit interface br0 bridge vlans
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 10 tagged br0
|
||||
admin@example:/config/interface/br0/bridge/vlans/> leave
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
Interface *vlan10* with an auto-configured IPv6 address should appear.
|
||||
|
||||
``` shell
|
||||
admin@example:/> show interfaces
|
||||
INTERFACE PROTOCOL STATE DATA
|
||||
br0 bridge vlan:10t
|
||||
│ ethernet UP 00:53:00:06:11:01
|
||||
├ e1 bridge FORWARDING vlan:10u pvid:10
|
||||
├ e2 bridge FORWARDING vlan:10u pvid:10
|
||||
├ e3 bridge FORWARDING vlan:20u pvid:20
|
||||
├ e4 bridge FORWARDING vlan:20u pvid:20
|
||||
├ e5 bridge FORWARDING vlan:30u pvid:30
|
||||
├ e6 bridge FORWARDING vlan:30u pvid:30
|
||||
├ e7 bridge FORWARDING vlan:40u pvid:40
|
||||
├ e8 bridge FORWARDING vlan:40u pvid:40
|
||||
├ e9 bridge FORWARDING vlan:50u pvid:50
|
||||
└ e10 bridge FORWARDING vlan:50u pvid:50
|
||||
lo ethernet UP 00:00:00:00:00:00
|
||||
ipv4 127.0.0.1/8 (static)
|
||||
ipv6 ::1/128 (static)
|
||||
vlan10 ethernet UP 00:53:00:06:11:01
|
||||
│ ipv6 fe80::0053:00ff:fe06:1101/64 (link-layer)
|
||||
└ br0 ethernet UP 00:53:00:06:11:01
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
When pinging "IPv6 all hosts" from the PC, there should be two
|
||||
responses for every ping, one from the switch and one from the PC
|
||||
attached to e10.
|
||||
|
||||
``` shell
|
||||
~ $ ping -L ff02::1%eth1
|
||||
PING ff02::1%eth1(ff02::1%eth1) 56 data bytes
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=1 ttl=64 time=0.508 ms
|
||||
64 bytes from fe80::0053:00ff:fe06:1101%eth1: icmp_seq=1 ttl=64 time=0.968 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=2 ttl=64 time=0.866 ms
|
||||
64 bytes from fe80::0053:00ff:fe06:1101%eth1: icmp_seq=2 ttl=64 time=0.867 ms
|
||||
64 bytes from fe80::0053:00ff:fe06:1101%eth1: icmp_seq=3 ttl=64 time=0.467 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=3 ttl=64 time=0.469 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=4 ttl=64 time=0.452 ms
|
||||
64 bytes from fe80::0053:00ff:fe06:1101%eth1: icmp_seq=4 ttl=64 time=0.453 ms
|
||||
^C
|
||||
--- ff02::1%eth1 ping statistics ---
|
||||
4 packets transmitted, 4 received, +4 duplicates, 0% packet loss, time 3031ms
|
||||
rtt min/avg/max/mdev = 0.452/0.631/0.968/0.211 ms
|
||||
~ $
|
||||
```
|
||||
|
||||
It should now be possible to access the switch from the PC via SSH (or NETCONF).
|
||||
|
||||
``` shell
|
||||
~ $ ssh admin@fe80::0053:00ff:fe06:1101%eth1
|
||||
admin@fe80::0053:00ff:fe06:1101%eth1's password:
|
||||
.-------.
|
||||
| . . | Infix -- a Network Operating System
|
||||
|-. v .-| https://kernelkit.github.io
|
||||
'-'---'-'
|
||||
|
||||
Run the command 'cli' for interactive OAM
|
||||
|
||||
admin@example:~$ exit
|
||||
~ $
|
||||
```
|
||||
|
||||
See previous sections on [backup](#backup) and [restore](#restore) of
|
||||
the created configuration.
|
||||
|
||||
|
||||
|
||||
|
||||
[1]: discovery.md
|
||||
[2]: https://rauc.io/
|
||||
[3]: boot.md#system-upgrade
|
||||
[4]: https://netopeer.liberouter.org/doc/sysrepo/libyang1/html/sysrepocfg.html
|
||||
[5]: vpd.md
|
||||
[6]: https://docs.kernel.org/leds/leds-class.html
|
||||
[7]: https://docs.kernel.org/power/power_supply_class.html
|
||||
[8]: networking.md#bridging
|
||||
[9]: networking.md#vlan-filtering-bridge
|
||||
|
||||
@@ -410,6 +410,45 @@ You can now freely debug the network activity of your test and the
|
||||
responses from the DUT.
|
||||
|
||||
|
||||
### Deterministic Topology Mappings
|
||||
|
||||
By default, mappings from logical to physical topologies are not
|
||||
stable across test case executions. This can be very frustrating when
|
||||
debugging a failing test, since logical nodes are suffled around
|
||||
between phyical nodes. In such cases, supplying a `PYTHONHASHSEED`
|
||||
variable (set to any 32-bit unsigned integer) when launching the test
|
||||
environment will make sure that topology mappings are deterministic:
|
||||
|
||||
$ make PYTHONHASHSEED=0 test-sh
|
||||
|
||||
If a seed is not supplied, a random value is chosen. This seed is
|
||||
logged by the `meta/reproducible.py` test case when running a test
|
||||
suite:
|
||||
|
||||
$ make test
|
||||
Info: Generating topology
|
||||
Info: Generating node YAML
|
||||
Info: Generating executables
|
||||
Info: Launching dut1
|
||||
Info: Launching dut2
|
||||
Info: Launching dut3
|
||||
Info: Launching dut4
|
||||
9PM - Simplicity is the ultimate sophistication
|
||||
|
||||
Starting test 0002-reproducible.py
|
||||
2024-05-03 10:40:30 # Starting (2024-05-03 10:40:30)
|
||||
2024-05-03 10:40:30 # Specify PYTHONHASHSEED=3773822171 to reproduce this test environment
|
||||
2024-05-03 10:40:30 ok 1 - $PYTHONHASHSEED is set
|
||||
2024-05-03 10:40:30 # Exiting (2024-05-03 10:40:30)
|
||||
2024-05-03 10:40:30 1..1
|
||||
...
|
||||
|
||||
This is useful because this value can then be used to rerun a test (or
|
||||
the whole suite) with identical topology mappings:
|
||||
|
||||
$ make PYTHONHASHSEED=3773822171 INFIX_TESTS=case/ietf_system/hostname.py test
|
||||
|
||||
|
||||
[9PM]: https://github.com/rical/9pm
|
||||
[Qeneth]: https://github.com/wkz/qeneth
|
||||
[TAP]: https://testanything.org/
|
||||
|
||||
+4
-2
@@ -1,10 +1,12 @@
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/confd/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/curios-httpd/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/execd/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/gencert/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/statd/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/factory/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/faux/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/finit/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/greenpak-programmer/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/ifupdown-ng/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/iito/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/k8s-logger/Config.in"
|
||||
@@ -12,13 +14,13 @@ source "$BR2_EXTERNAL_INFIX_PATH/package/keyack/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/klish-plugin-infix/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/klish/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/klish-plugin-sysrepo/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/python-statd/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/landing/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/libsrx/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/lowdown/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/mcd/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/mdns-alias/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/netbrowse/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/podman/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/python-statd/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/skeleton-init-finit/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/tetris/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/greenpak-programmer/Config.in"
|
||||
|
||||
@@ -7,14 +7,13 @@ run name:bootstrap log:prio:user.notice norestart <pid/syslogd> \
|
||||
run name:error :1 log:console norestart if:<run/bootstrap/failure> \
|
||||
[S] /usr/libexec/confd/error --
|
||||
|
||||
service name:confd log <run/bootstrap/success> \
|
||||
service name:confd log:prio:daemon.err <run/bootstrap/success> \
|
||||
[S12345] sysrepo-plugind -f -p /run/confd.pid -n -- Configuration daemon
|
||||
|
||||
# Bootstrap system with startup-config, ensure correct ownership
|
||||
# Bootstrap system with startup-config
|
||||
run name:startup log:prio:user.notice norestart <pid/confd> \
|
||||
[S] /usr/libexec/confd/load startup-config \
|
||||
-- Loading startup-config
|
||||
task [S] <pid/confd> chown admin:wheel /cfg/startup-config.cfg --
|
||||
|
||||
# Run if loading startup-config fails for some reason
|
||||
run name:failure log:prio:user.critical norestart <pid/confd> if:<run/startup/failure> \
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
sha256 518cd23d81f17b912f1c4ccc6acb67ff614ddcc2caf14def5044126d1a60f364 LICENSE
|
||||
@@ -0,0 +1,8 @@
|
||||
config BR2_PACKAGE_GENCERT
|
||||
bool "gencert"
|
||||
select BR2_PACKAGE_OPENSSL
|
||||
help
|
||||
Generate a self-signed certificate for embedded systems confused
|
||||
about the current time. Start date: 2000-01-01, end: 9999-01-01
|
||||
|
||||
https://github.com/kernelkit/infix
|
||||
@@ -0,0 +1 @@
|
||||
sha256 fff5e150e496c6bb6035ae388ee13cf2d0df842335e5145a4b902ef6b7892088 LICENSE
|
||||
@@ -0,0 +1,22 @@
|
||||
################################################################################
|
||||
#
|
||||
# gencert
|
||||
#
|
||||
################################################################################
|
||||
|
||||
GENCERT_VERSION = 1.0
|
||||
GENCERT_SITE_METHOD = local
|
||||
GENCERT_SITE = $(BR2_EXTERNAL_INFIX_PATH)/src/gencert
|
||||
GENCERT_LICENSE = ISC
|
||||
GENCERT_LICENSE_FILES = LICENSE
|
||||
GENCERT_REDISTRIBUTE = NO
|
||||
GENCERT_DEPENDENCIES = openssl
|
||||
GENCERT_AUTORECONF = YES
|
||||
|
||||
define GENCERT_CONF_ENV
|
||||
CFLAGS="$(INFIX_CFLAGS)"
|
||||
endef
|
||||
|
||||
GENCERT_CONF_OPTS = --prefix= --disable-silent-rules
|
||||
|
||||
$(eval $(autotools-package))
|
||||
@@ -0,0 +1,6 @@
|
||||
config BR2_PACKAGE_LANDING
|
||||
bool "Landing page (temporary)"
|
||||
help
|
||||
Temporary landing page before upcoming webui.
|
||||
|
||||
https://github.com/kernelkit/infix
|
||||
@@ -0,0 +1 @@
|
||||
sha256 fff5e150e496c6bb6035ae388ee13cf2d0df842335e5145a4b902ef6b7892088 LICENSE
|
||||
@@ -0,0 +1,18 @@
|
||||
################################################################################
|
||||
#
|
||||
# landing
|
||||
#
|
||||
################################################################################
|
||||
|
||||
LANDING_VERSION = 1.0
|
||||
LANDING_SITE_METHOD = local
|
||||
LANDING_SITE = $(BR2_EXTERNAL_INFIX_PATH)/src/landing
|
||||
LANDING_LICENSE = ISC
|
||||
LANDING_LICENSE_FILES = LICENSE
|
||||
|
||||
define LANDING_INSTALL_TARGET_CMDS
|
||||
cp $(@D)/*.html $(TARGET_DIR)/usr/html/
|
||||
cp $(@D)/*.png $(TARGET_DIR)/usr/html/
|
||||
endef
|
||||
|
||||
$(eval $(generic-package))
|
||||
@@ -1 +1,3 @@
|
||||
#set DEBUG=1
|
||||
|
||||
service name:statd log [S12345] <pid/confd> statd -f -p /run/statd.pid -n -- Status daemon
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
From 49c660c412342e1af76735e75cd3f286ae655b82 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Tue, 28 Mar 2023 10:37:53 +0200
|
||||
Subject: [PATCH 1/4] sysrepo-plugind: add support for running in foreground
|
||||
Subject: [PATCH 1/5] sysrepo-plugind: add support for running in foreground
|
||||
with syslog
|
||||
Organization: Addiva Elektronik
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
From 87224ddce4ed1a118b3efb24e0dc7167f8754c22 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Wed, 7 Jun 2023 15:19:13 +0200
|
||||
Subject: [PATCH 2/4] Refactor _sr_install_modules() to use
|
||||
Subject: [PATCH 2/5] Refactor _sr_install_modules() to use
|
||||
ly_ctx_load_module()
|
||||
Organization: Addiva Elektronik
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
From 2d3b9524b1448dd6a8ef83e4e101e509d2552dcc Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Fri, 16 Jun 2023 12:13:16 +0200
|
||||
Subject: [PATCH 3/4] Add support for loading /etc/sysrepo/factory-default
|
||||
Subject: [PATCH 3/5] Add support for loading /etc/sysrepo/factory-default
|
||||
Organization: Addiva Elektronik
|
||||
|
||||
Follow-up to 3d07270 which adds support for compile-time defaults, this
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 7726017815243818f07936e4ff3bb979e9291f04 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Fri, 16 Jun 2023 16:17:27 +0200
|
||||
Subject: [PATCH 4/4] sysrepoctl: add support for -Cfactory -d running
|
||||
Subject: [PATCH 4/5] sysrepoctl: add support for -Cfactory -d running
|
||||
Organization: Addiva Elektronik
|
||||
|
||||
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
|
||||
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
From 49b558067ab7c6f59ec84f14cc89201b4a7bde57 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Tue, 7 May 2024 15:41:53 +0200
|
||||
Subject: [PATCH 5/5] Allow SR_EV_DONE to return any error to sysrepocfg
|
||||
Organization: Addiva Elektronik
|
||||
|
||||
Importing a system configuration with sysrepocfg the model callbacks do
|
||||
their best to validate the configuration and prepare the transaction in
|
||||
SR_EV_CHANGE. In cases when dealing with a complex system, like adding
|
||||
a route, address, or other change using iproute2, we may still get into
|
||||
trouble when actually sending off the transaction in SR_EV_DONE.
|
||||
|
||||
If something does go wrong we'd like to know this by the exit code of
|
||||
sysrepocfg, not by inspecting logs or expected system state.
|
||||
|
||||
This patch is a clumsy way of forcing the (first) error to bubble up to
|
||||
the surface and cause a non-zero exit code from sysrepocfg.
|
||||
|
||||
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
|
||||
---
|
||||
src/shm_sub.c | 36 ++++++++++++++++++++++++++++--------
|
||||
src/shm_sub.h | 2 +-
|
||||
src/sysrepo.c | 2 +-
|
||||
3 files changed, 30 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/src/shm_sub.c b/src/shm_sub.c
|
||||
index 99b2be37..298d7dd3 100644
|
||||
--- a/src/shm_sub.c
|
||||
+++ b/src/shm_sub.c
|
||||
@@ -1586,7 +1586,7 @@ cleanup:
|
||||
|
||||
sr_error_info_t *
|
||||
sr_shmsub_change_notify_change_done(struct sr_mod_info_s *mod_info, const char *orig_name, const void *orig_data,
|
||||
- uint32_t timeout_ms)
|
||||
+ uint32_t timeout_ms, sr_error_info_t **cb_err_info)
|
||||
{
|
||||
sr_error_info_t *err_info = NULL;
|
||||
struct sr_mod_info_mod_s *mod = NULL;
|
||||
@@ -1717,12 +1717,19 @@ sr_shmsub_change_notify_change_done(struct sr_mod_info_s *mod_info, const char *
|
||||
sr_rwunlock(&nsub->sub_shm->lock, 0, SR_LOCK_WRITE, cid, __func__);
|
||||
nsub->lock = SR_LOCK_NONE;
|
||||
|
||||
- /* we do not care about an error */
|
||||
- sr_errinfo_free(&nsub->cb_err_info);
|
||||
-
|
||||
SR_LOG_INF("EV ORIGIN: \"%s\" \"%s\" ID %" PRIu32 " priority %" PRIu32 " succeeded.",
|
||||
nsub->mod->ly_mod->name, sr_ev2str(SR_SUB_EV_DONE), nsub->mod->request_id, nsub->cur_priority);
|
||||
|
||||
+ /*
|
||||
+ * unexpected critical error, merge and cleanup, let
|
||||
+ * sysrepocfg caller handle undefined system state.
|
||||
+ */
|
||||
+ if (nsub->cb_err_info) {
|
||||
+ sr_errinfo_merge(cb_err_info, nsub->cb_err_info);
|
||||
+ nsub->cb_err_info = NULL;
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+
|
||||
nsub->pending_event = 0;
|
||||
}
|
||||
} while (1);
|
||||
@@ -3167,7 +3174,7 @@ sr_shmsub_change_listen_check_update_edit(sr_session_ctx_t *ev_sess, const char
|
||||
sr_error_info_t *
|
||||
sr_shmsub_change_listen_process_module_events(struct modsub_change_s *change_subs, sr_conn_ctx_t *conn)
|
||||
{
|
||||
- sr_error_info_t *err_info = NULL;
|
||||
+ sr_error_info_t *err_info = NULL, *err_tmp;
|
||||
uint32_t i, data_len = 0, valid_subscr_count;
|
||||
char *data = NULL, *shm_data_ptr;
|
||||
int ret = SR_ERR_OK;
|
||||
@@ -3289,6 +3296,11 @@ process_event:
|
||||
}
|
||||
break;
|
||||
}
|
||||
+ } else if (sub_info.event == SR_SUB_EV_DONE) {
|
||||
+ if (ret && err_code == SR_ERR_OK) {
|
||||
+ /* unexpected, callback actually failed, save for later. */
|
||||
+ err_code = ret;
|
||||
+ }
|
||||
}
|
||||
|
||||
/* subscription processed this event */
|
||||
@@ -3330,6 +3342,11 @@ process_event:
|
||||
}
|
||||
break;
|
||||
case SR_SUB_EV_DONE:
|
||||
+ if (err_code) {
|
||||
+ /* prepare unexepected error from session to be written to SHM */
|
||||
+ sr_errinfo_new(&err_info, err_code, "Oups, error detected in SR_EV_DONE");
|
||||
+ }
|
||||
+ break;
|
||||
case SR_SUB_EV_ABORT:
|
||||
/* nothing to do */
|
||||
break;
|
||||
@@ -3349,14 +3366,17 @@ process_event:
|
||||
|
||||
/* SUB WRITE URGE LOCK */
|
||||
if (sr_shmsub_change_listen_relock(multi_sub_shm, SR_LOCK_WRITE_URGE, &sub_info, change_sub, change_subs->module_name,
|
||||
- ret, ev_sess, &err_info)) {
|
||||
+ ret, ev_sess, &err_tmp)) {
|
||||
+ if (err_tmp)
|
||||
+ err_info = err_tmp;
|
||||
goto cleanup;
|
||||
}
|
||||
sub_lock = SR_LOCK_WRITE_URGE;
|
||||
|
||||
/* finish event */
|
||||
- if ((err_info = sr_shmsub_multi_listen_write_event(multi_sub_shm, valid_subscr_count, err_code, &shm_data_sub, data,
|
||||
+ if ((err_tmp = sr_shmsub_multi_listen_write_event(multi_sub_shm, valid_subscr_count, err_code, &shm_data_sub, data,
|
||||
data_len, change_subs->module_name, err_code ? "fail" : "success"))) {
|
||||
+ err_info = err_tmp;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
@@ -3886,7 +3906,7 @@ finish_iter:
|
||||
sr_errinfo_free(&cb_err_info);
|
||||
|
||||
/* publish "done" event */
|
||||
- if ((err_info = sr_shmsub_change_notify_change_done(&mod_info, NULL, NULL, SR_CHANGE_CB_TIMEOUT))) {
|
||||
+ if ((err_info = sr_shmsub_change_notify_change_done(&mod_info, NULL, NULL, SR_CHANGE_CB_TIMEOUT, &cb_err_info))) {
|
||||
goto cleanup_unlock;
|
||||
}
|
||||
|
||||
diff --git a/src/shm_sub.h b/src/shm_sub.h
|
||||
index 3056e4c5..460ad927 100644
|
||||
--- a/src/shm_sub.h
|
||||
+++ b/src/shm_sub.h
|
||||
@@ -144,7 +144,7 @@ sr_error_info_t *sr_shmsub_change_notify_change(struct sr_mod_info_s *mod_info,
|
||||
* @return err_info, NULL on success.
|
||||
*/
|
||||
sr_error_info_t *sr_shmsub_change_notify_change_done(struct sr_mod_info_s *mod_info, const char *orig_name,
|
||||
- const void *orig_data, uint32_t timeout_ms);
|
||||
+ const void *orig_data, uint32_t timeout_ms, sr_error_info_t **cb_err_info);
|
||||
|
||||
/**
|
||||
* @brief Notify about (generate) a change "abort" event.
|
||||
diff --git a/src/sysrepo.c b/src/sysrepo.c
|
||||
index ca2a20b7..e7c1fd0e 100644
|
||||
--- a/src/sysrepo.c
|
||||
+++ b/src/sysrepo.c
|
||||
@@ -3801,7 +3801,7 @@ store:
|
||||
}
|
||||
|
||||
/* publish "done" event, all changes were applied */
|
||||
- if ((err_info = sr_shmsub_change_notify_change_done(mod_info, orig_name, orig_data, timeout_ms))) {
|
||||
+ if ((err_info = sr_shmsub_change_notify_change_done(mod_info, orig_name, orig_data, timeout_ms, cb_err_info))) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
--
|
||||
2.34.1
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
pkglibexec_SCRIPTS = bootstrap error load gen-service \
|
||||
gen-hostkeys gen-admin-auth gen-hostname gen-interfaces gen-motd gen-hardware
|
||||
pkglibexec_SCRIPTS = bootstrap error load gen-service gen-hostkeys gen-admin-auth \
|
||||
gen-hostname gen-interfaces gen-motd gen-hardware
|
||||
sbin_SCRIPTS = dagger
|
||||
|
||||
@@ -72,7 +72,7 @@ collate()
|
||||
# Report error on console, syslog, and set login banners for getty + ssh
|
||||
console_error()
|
||||
{
|
||||
logger -p user.crit -t "BOOTSTRAP" "$1"
|
||||
logger -p user.crit -t bootstrap "$1"
|
||||
|
||||
# shellcheck disable=SC3037
|
||||
/bin/echo -e "\n\n\e[31mCRITICAL BOOTSTRAP ERROR\n$1\e[0m\n" > /dev/console
|
||||
|
||||
@@ -50,8 +50,8 @@ action_exec()
|
||||
|
||||
for node in $order; do
|
||||
for work in $(find "$actdir/$node" -type f -executable 2>/dev/null | sort); do
|
||||
$work >>"$work.log" 2>&1 || code=$?
|
||||
echo "[exit:$code]" >>"$work.log"
|
||||
$work | ts >>"$work.log" 2>&1 || code=$?
|
||||
echo "[exit:$code]" | ts >>"$work.log"
|
||||
|
||||
[ $code -eq 0 ] || abort "$work failed with exitcode $code"
|
||||
done
|
||||
+34
-4
@@ -19,6 +19,16 @@ banner_append()
|
||||
return 0
|
||||
}
|
||||
|
||||
note()
|
||||
{
|
||||
logger -s -I $$ -p user.notice -t load "$*"
|
||||
}
|
||||
|
||||
err()
|
||||
{
|
||||
logger -s -I $$ -p user.error -t load "$*"
|
||||
}
|
||||
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. /etc/confdrc
|
||||
@@ -35,17 +45,31 @@ else
|
||||
fi
|
||||
|
||||
if [ ! -f "$fn" ]; then
|
||||
logger -sik -p user.error "No such file, $fn, aborting!"
|
||||
err "No such file, $fn, aborting!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
note "Loading $config ..."
|
||||
if ! sysrepocfg -v3 -I"$fn" -f json; then
|
||||
case "$config" in
|
||||
startup-config)
|
||||
logger -sik -p user.error "Failed loading $fn, reverting to Fail Secure mode!"
|
||||
err "Failed loading $fn, reverting to Fail Secure mode!"
|
||||
# On failure to load startup-config the system is in an undefined state
|
||||
cat <<-EOF >/tmp/factory.json
|
||||
{
|
||||
"infix-factory-default:factory-default": {}
|
||||
}
|
||||
EOF
|
||||
# Default timeout is 2 seconds, allow time to clean up shm
|
||||
if ! sysrepocfg -f json -t 10 -R /tmp/factory.json; then
|
||||
rm -f /etc/sysrepo/data/*startup*
|
||||
rm -f /etc/sysrepo/data/*running*
|
||||
rm -f /dev/shm/sr_*
|
||||
killall sysrepo-plugind
|
||||
fi
|
||||
;;
|
||||
failure-config)
|
||||
logger -sik -p user.error "Failed loading $fn, aborting!"
|
||||
err "Failed loading $fn, aborting!"
|
||||
banner_append "CRITICAL ERROR: Logins are disabled, no credentials available"
|
||||
initctl -nbq runlevel 9
|
||||
;;
|
||||
@@ -53,7 +77,13 @@ if ! sysrepocfg -v3 -I"$fn" -f json; then
|
||||
|
||||
exit 1
|
||||
fi
|
||||
logger -sik -p user.notice "Loaded $fn successfully."
|
||||
|
||||
note "Loaded $fn successfully."
|
||||
if [ "$config" = "failure-config" ]; then
|
||||
banner_append "ERROR: Corrupt startup-config, system has reverted to default login credentials"
|
||||
else
|
||||
# Ensure correct ownership and permissions, in particular after factory reset
|
||||
# Created by the system, writable by any user in the admin group.
|
||||
chown root:wheel "$fn"
|
||||
chmod 0660 "$fn"
|
||||
fi
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
factorydir = $(pkgdatadir)/factory.d
|
||||
dist_factory_DATA = 10-nacm.json 10-netconf-server.json \
|
||||
10-infix-system.json 10-system.json
|
||||
10-infix-services.json 10-system.json
|
||||
|
||||
+1
-1
@@ -434,7 +434,7 @@ int cni_ifchange_cand_infer_type(sr_session_ctx_t *session, const char *path)
|
||||
return SR_ERR_SYS;
|
||||
|
||||
err = sr_get_data(session, path, 0, 0, 0, &cfg);
|
||||
if (err)
|
||||
if (err || !cfg)
|
||||
goto err;
|
||||
|
||||
node = lydx_get_descendant(cfg->tree, "interfaces", "interface", NULL);
|
||||
|
||||
@@ -196,7 +196,7 @@ static int change_hardware(sr_session_ctx_t *session, uint32_t sub_id, const cha
|
||||
return SR_ERR_OK;
|
||||
|
||||
rc = sr_get_data(session, XPATH_BASE_ "//.", 0, 0, 0, &cfg);
|
||||
if (rc)
|
||||
if (rc || !cfg)
|
||||
goto err;
|
||||
|
||||
rc = srx_get_diff(session, &diff);
|
||||
|
||||
@@ -1699,7 +1699,7 @@ static int ifchange(sr_session_ctx_t *session, uint32_t sub_id, const char *modu
|
||||
return err;
|
||||
|
||||
err = sr_get_data(session, "/interfaces/interface", 0, 0, 0, &cfg);
|
||||
if (err)
|
||||
if (err || !cfg)
|
||||
goto err_abandon;
|
||||
|
||||
err = srx_get_diff(session, (struct lyd_node **)&diff);
|
||||
|
||||
@@ -348,6 +348,11 @@ static int change_control_plane_protocols(sr_session_ctx_t *session, uint32_t su
|
||||
}
|
||||
|
||||
rc = sr_get_data(session, "/ietf-routing:routing/control-plane-protocols//.", 0, 0, 0, &cfg);
|
||||
if (rc || !cfg) {
|
||||
NOTE("No control-plane-protocols available.");
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
LY_LIST_FOR(lyd_child(cfg->tree), tmp) {
|
||||
LY_LIST_FOR(lyd_child(tmp), cplane) {
|
||||
const char *type;
|
||||
@@ -360,11 +365,13 @@ static int change_control_plane_protocols(sr_session_ctx_t *session, uint32_t su
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
sr_release_data(cfg);
|
||||
|
||||
err_close:
|
||||
fclose(fp);
|
||||
if (!staticd_enabled)
|
||||
(void)remove(STATICD_CONF_NEXT);
|
||||
sr_release_data(cfg);
|
||||
|
||||
err_abandon:
|
||||
return rc;
|
||||
}
|
||||
|
||||
+122
-80
@@ -4,6 +4,8 @@
|
||||
#include <ctype.h>
|
||||
#include <paths.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/types.h>
|
||||
@@ -442,7 +444,7 @@ static int change_ntp(sr_session_ctx_t *session, uint32_t sub_id, const char *mo
|
||||
continue;
|
||||
|
||||
name = sr_val_to_str(old);
|
||||
DEBUG("Removing NTP server %s\n", name);
|
||||
DEBUG("Removing NTP server %s", name);
|
||||
erasef("/etc/chrony/sources.d/%s.sources", name);
|
||||
free(name);
|
||||
changes++;
|
||||
@@ -542,12 +544,11 @@ static int change_dns(sr_session_ctx_t *session, uint32_t sub_id, const char *mo
|
||||
|
||||
case SR_EV_DONE:
|
||||
/* Check if passed validation in previous event */
|
||||
if (access(RESOLV_NEXT, F_OK))
|
||||
return SR_ERR_OK;
|
||||
|
||||
(void)remove(RESOLV_PREV);
|
||||
(void)rename(RESOLV_CONF, RESOLV_PREV);
|
||||
(void)rename(RESOLV_NEXT, RESOLV_CONF);
|
||||
if (!access(RESOLV_NEXT, F_OK)) {
|
||||
(void)remove(RESOLV_PREV);
|
||||
(void)rename(RESOLV_CONF, RESOLV_PREV);
|
||||
(void)rename(RESOLV_NEXT, RESOLV_CONF);
|
||||
}
|
||||
|
||||
/* in bootstrap, another resolvconf will soon take your call */
|
||||
if (systemf("initctl -bq cond get hook/sys/up"))
|
||||
@@ -594,11 +595,10 @@ static int change_dns(sr_session_ctx_t *session, uint32_t sub_id, const char *mo
|
||||
goto fail;
|
||||
|
||||
for (size_t i = 0; i < cnt; i++) {
|
||||
const char *xpath = val[i].xpath;
|
||||
char *ptr;
|
||||
|
||||
/* Get /ietf-system:system/dns-resolver/server[name='foo'] */
|
||||
ptr = srx_get_str(session, "%s/udp-and-tcp/address", xpath);
|
||||
ptr = srx_get_str(session, "%s/udp-and-tcp/address", val[i].xpath);
|
||||
if (ptr)
|
||||
/* XXX: add support also for udp-and-tcp/port */
|
||||
fprintf(fp, "nameserver %s\n", ptr);
|
||||
@@ -637,7 +637,7 @@ static char *sys_find_usable_shell(sr_session_ctx_t *sess, char *name)
|
||||
sr_data_t *cfg;
|
||||
|
||||
snprintf(xpath, sizeof(xpath), XPATH_AUTH_"/user[name='%s']/infix-system:shell", name);
|
||||
if (!sr_get_data(sess, xpath, 0, 0, 0, &cfg)) {
|
||||
if (!sr_get_data(sess, xpath, 0, 0, 0, &cfg) && cfg) {
|
||||
struct lyd_node *node;
|
||||
|
||||
if (!lyd_find_path(cfg->tree, xpath, 0, &node))
|
||||
@@ -666,41 +666,18 @@ static char *sys_find_usable_shell(sr_session_ctx_t *sess, char *name)
|
||||
shell = LOGIN_SHELL;
|
||||
|
||||
shell = strdup(shell);
|
||||
sr_release_data(cfg);
|
||||
if (cfg)
|
||||
sr_release_data(cfg);
|
||||
|
||||
return shell;
|
||||
}
|
||||
|
||||
static uid_t sys_home_exists(char *user)
|
||||
{
|
||||
char path[strlen(user) + 10];
|
||||
struct stat st;
|
||||
|
||||
snprintf(path, sizeof(path), "/home/%s", user);
|
||||
if (stat(path, &st))
|
||||
return 0;
|
||||
|
||||
return st.st_uid;
|
||||
}
|
||||
|
||||
static int sys_uid_busy(char *user, uid_t uid)
|
||||
{
|
||||
struct passwd *pw;
|
||||
int rc = 0;
|
||||
|
||||
setpwent();
|
||||
while ((pw = getpwent())) {
|
||||
if (pw->pw_uid != uid)
|
||||
continue;
|
||||
if (strcmp(pw->pw_name, user))
|
||||
rc = -1;
|
||||
}
|
||||
endpwent();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sys_del_user(char *user)
|
||||
/*
|
||||
* Used both when deleting a user from the system configuration, and
|
||||
* when cleaning up stale users while adding a new user to the system.
|
||||
* In the latter case we don't need to log failure/success.
|
||||
*/
|
||||
static int sys_del_user(char *user, bool silent)
|
||||
{
|
||||
char *args[] = {
|
||||
"deluser", "--remove-home", user, NULL
|
||||
@@ -710,49 +687,71 @@ static int sys_del_user(char *user)
|
||||
erasef("/var/run/sshd/%s.keys", user);
|
||||
err = systemv_silent(args);
|
||||
if (err) {
|
||||
ERROR("Error deleting user \"%s\"\n", user);
|
||||
if (!silent)
|
||||
ERROR("Error deleting user \"%s\"", user);
|
||||
|
||||
/* Ensure $HOME is removed at least. */
|
||||
systemf("rm -rf /home/%s", user);
|
||||
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
NOTE("User \"%s\" deleted\n", user);
|
||||
|
||||
if (!silent)
|
||||
NOTE("User \"%s\" deleted", user);
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
static int sys_add_new_user(sr_session_ctx_t *sess, char *name)
|
||||
/*
|
||||
* Helper for sys_add_user(), notice how uid:gid must never be 0:0, it
|
||||
* is reserved for the system root user which is never created/deleted.
|
||||
*
|
||||
* This function balances both /etc/passwd and /etc/group, so to make
|
||||
* things a little bit easier, we simplify the world and reserve the
|
||||
* same group name as the username. This means we can always remove
|
||||
* any stale group entries first.
|
||||
*
|
||||
* When uid:gid is non-zero we create the user and re-attach them with
|
||||
* their $HOME. When uid:gid *is* zero, however, we must make sure to
|
||||
* clean up any stale user + $HOME before calling adduser to allocate
|
||||
* a new uid:gid pair for the new user.
|
||||
*
|
||||
* If this function fails, it is up to the callee to retry the operation
|
||||
* with a zero uid:gid pair, which is the most iron clad form.
|
||||
*/
|
||||
static int sys_call_adduser(sr_session_ctx_t *sess, char *name, uid_t uid, gid_t gid)
|
||||
{
|
||||
char *shell = sys_find_usable_shell(sess, name);
|
||||
char uid_str[10];
|
||||
char *eargs[] = {
|
||||
"adduser", "-d", "-s", shell, "-u", NULL, "-H", name, NULL
|
||||
"adduser", "-d", "-s", shell, "-u", NULL, "-G", NULL, "-H", name, NULL
|
||||
};
|
||||
char *nargs[] = {
|
||||
"adduser", "-d", "-s", shell, name, NULL
|
||||
};
|
||||
bool do_chown = false;
|
||||
char uid_str[10], grp_str[strlen(name) + 1];
|
||||
char **args;
|
||||
uid_t uid;
|
||||
int err;
|
||||
|
||||
uid = sys_home_exists(name);
|
||||
if (uid) {
|
||||
if (sys_uid_busy(name, uid)) {
|
||||
/* Exists but owned by someone else. */
|
||||
ERROR("Creating user %s, /home/%s existed but was owned by another uid (%d)",
|
||||
name, name, uid);
|
||||
ERROR("Cleaning up stale /home/%s", name);
|
||||
systemf("rm -rf /home/%s", name);
|
||||
args = nargs;
|
||||
} else {
|
||||
/* Not in passwd or owned by us already */
|
||||
do_chown = true;
|
||||
DEBUG("Adding new user %s, cleaning up any stale group.", name);
|
||||
systemf("delgroup %s 2>/dev/null", name);
|
||||
|
||||
/* reusing existing uid:gid from $HOME */
|
||||
if (uid && gid) {
|
||||
/* recreate group first */
|
||||
systemf("addgroup -g %d %s", gid, name);
|
||||
|
||||
snprintf(uid_str, sizeof(uid_str), "%d", uid);
|
||||
snprintf(grp_str, sizeof(grp_str), "%s", name);
|
||||
eargs[5] = uid_str;
|
||||
eargs[7] = grp_str;
|
||||
|
||||
args = eargs;
|
||||
} else {
|
||||
DEBUG("Cleaning up any stale user %s and /home/%s", name, name);
|
||||
sys_del_user(name, true);
|
||||
|
||||
NOTE("Reusing uid %d and /home/%s for new user %s", uid, name, name);
|
||||
snprintf(uid_str, sizeof(uid_str), "%d", uid);
|
||||
eargs[5] = uid_str;
|
||||
args = eargs;
|
||||
}
|
||||
} else
|
||||
args = nargs;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Busybox implementation of 'adduser -d' sets the password
|
||||
@@ -761,14 +760,51 @@ static int sys_add_new_user(sr_session_ctx_t *sess, char *name)
|
||||
*/
|
||||
err = systemv_silent(args);
|
||||
free(shell);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new (locked) user, if they have a matching $HOME we use the
|
||||
* uid:gid from that, if they are free. In all cases of conflict with
|
||||
* any (type of) user we remove the existing $HOME and start with a new
|
||||
* uid:gid pair.
|
||||
*/
|
||||
static int sys_add_user(sr_session_ctx_t *sess, char *name)
|
||||
{
|
||||
char home[strlen(name) + 10];
|
||||
bool reused = false;
|
||||
struct stat st;
|
||||
int err;
|
||||
|
||||
/* Map users to their existing $HOME */
|
||||
snprintf(home, sizeof(home), "/home/%s", name);
|
||||
if (!stat(home, &st)) {
|
||||
/* Verify IDs aren't already used, like BusyBox adduser */
|
||||
if (getpwuid(st.st_uid) || getgrgid(st.st_uid) || getgrgid(st.st_gid)) {
|
||||
/* Exists but owned by someone else. */
|
||||
ERROR("Failed mapping user %s to /home/%s, uid:gid (%d:%d) already exists.",
|
||||
name, name, st.st_uid, st.st_gid);
|
||||
err = sys_call_adduser(sess, name, 0, 0);
|
||||
} else {
|
||||
DEBUG("Reusing uid:gid %d:%d and /home/%s for new user %s",
|
||||
st.st_uid, st.st_gid, name, name);
|
||||
err = sys_call_adduser(sess, name, st.st_uid, st.st_gid);
|
||||
if (err) {
|
||||
ERROR("Failed reusing uid:gid from /home/%s, retrying create user ...", name);
|
||||
err = sys_call_adduser(sess, name, 0, 0);
|
||||
} else
|
||||
reused = true;
|
||||
}
|
||||
} else
|
||||
err = sys_call_adduser(sess, name, 0, 0);
|
||||
|
||||
if (err) {
|
||||
ERROR("Failed creating new user \"%s\"\n", name);
|
||||
ERROR("Failed creating new user \"%s\"", name);
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
|
||||
NOTE("New user \"%s\" created\n", name);
|
||||
if (do_chown)
|
||||
systemf("chown -R %s:%s /home/%s", name, name, name);
|
||||
NOTE("User \"%s\" created%s.", name, reused ? ", mapped to existing home directory" : "");
|
||||
|
||||
/*
|
||||
* OpenSSH in Infix has been set up to use /var/run/sshd/%s.keys
|
||||
@@ -776,6 +812,7 @@ static int sys_add_new_user(sr_session_ctx_t *sess, char *name)
|
||||
* /home/%s/.ssh/authorized_keys file. This creates a both the
|
||||
* directory and the symlink owned by root to prevent tampering.
|
||||
*/
|
||||
DEBUG("Adding secure /home/%s/.ssh directory.", name);
|
||||
fmkpath(0750, "/home/%s/.ssh", name);
|
||||
systemf("ln -sf /var/run/sshd/%s.keys /home/%s/.ssh/authorized_keys", name, name);
|
||||
|
||||
@@ -803,7 +840,7 @@ static char *change_get_user(struct sr_change *change)
|
||||
if (user) {
|
||||
pw = getpwnam(user);
|
||||
if (!pw) {
|
||||
ERROR("Skipping attribute for missing user (%s)", user);
|
||||
/* Skipping, user probably deleted. */
|
||||
free(user);
|
||||
user = NULL;
|
||||
}
|
||||
@@ -828,7 +865,7 @@ static sr_error_t handle_sr_passwd_update(augeas *aug, sr_session_ctx_t *, struc
|
||||
assert(change->new);
|
||||
|
||||
if (change->new->type != SR_STRING_T) {
|
||||
ERROR("Internal error, expected pass to be string type\n");
|
||||
ERROR("Internal error, expected pass to be string type.");
|
||||
err = SR_ERR_INTERNAL;
|
||||
break;
|
||||
}
|
||||
@@ -841,7 +878,7 @@ static sr_error_t handle_sr_passwd_update(augeas *aug, sr_session_ctx_t *, struc
|
||||
* it as "*", meaning the user can log in with SSH keys.
|
||||
*/
|
||||
if (!hash || !strlen(hash)) {
|
||||
ERROR("Empty passwords are not allowed, disabling password login.\n");
|
||||
ERROR("Empty passwords are not allowed, disabling password login.");
|
||||
hash = "*";
|
||||
}
|
||||
if (aug_set_dynpath(aug, hash, "etc/shadow/%s/password", user))
|
||||
@@ -882,6 +919,9 @@ static sr_error_t handle_sr_shell_update(augeas *aug, sr_session_ctx_t *sess, st
|
||||
char *user;
|
||||
int err;
|
||||
|
||||
if (change->op == SR_OP_DELETED)
|
||||
return SR_ERR_OK;
|
||||
|
||||
user = change_get_user(change);
|
||||
if (!user)
|
||||
return SR_ERR_OK;
|
||||
@@ -930,7 +970,7 @@ static sr_error_t handle_sr_user_update(augeas *aug, sr_session_ctx_t *sess, str
|
||||
assert(change->new);
|
||||
|
||||
name = sr_xpath_key_value(change->new->xpath, "user", "name", &state);
|
||||
err = sys_add_new_user(sess, name);
|
||||
err = sys_add_user(sess, name);
|
||||
if (err) {
|
||||
sr_xpath_recover(&state);
|
||||
return err;
|
||||
@@ -941,7 +981,7 @@ static sr_error_t handle_sr_user_update(augeas *aug, sr_session_ctx_t *sess, str
|
||||
assert(change->old);
|
||||
|
||||
name = sr_xpath_key_value(change->old->xpath, "user", "name", &state);
|
||||
err = sys_del_user(name);
|
||||
err = sys_del_user(name, false);
|
||||
if (err) {
|
||||
sr_xpath_recover(&state);
|
||||
return err;
|
||||
@@ -973,8 +1013,9 @@ static sr_error_t generate_auth_keys(sr_session_ctx_t *session, const char *xpat
|
||||
sr_error_t err = 0;
|
||||
sr_data_t *cfg;
|
||||
|
||||
/* err may be OK and cfg NULL if 'no system' */
|
||||
err = sr_get_data(session, xpath, 0, 0, 0, &cfg);
|
||||
if (err)
|
||||
if (err || !cfg)
|
||||
return err;
|
||||
|
||||
auth = lydx_get_descendant(cfg->tree, "system", "authentication", NULL);
|
||||
@@ -1043,7 +1084,7 @@ static sr_error_t change_auth_done(augeas *aug, sr_session_ctx_t *session)
|
||||
*/
|
||||
err = aug_load(aug);
|
||||
if (err) {
|
||||
ERROR("Error loading files into aug tree\n");
|
||||
ERROR("Error loading files into aug tree.");
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
@@ -1079,7 +1120,7 @@ static sr_error_t change_auth_done(augeas *aug, sr_session_ctx_t *session)
|
||||
return err;
|
||||
}
|
||||
|
||||
DEBUG("Changes to authentication saved\n");
|
||||
DEBUG("Changes to authentication saved.");
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
@@ -1326,8 +1367,9 @@ static int change_hostname(sr_session_ctx_t *session, uint32_t sub_id, const cha
|
||||
|
||||
nm = srx_get_str(session, "%s", xpath);
|
||||
if (!nm) {
|
||||
/* XXX: derive from global "options.h" or /usr/share/factory/ */
|
||||
nm = strdup("infix");
|
||||
nm = fgetkey("/etc/os-release", "DEFAULT_HOSTNAME");
|
||||
if (nm)
|
||||
nm = strdup(nm);
|
||||
if (!nm) {
|
||||
err = SR_ERR_NO_MEMORY;
|
||||
goto err;
|
||||
|
||||
@@ -226,7 +226,7 @@ static int change(sr_session_ctx_t *session, uint32_t sub_id, const char *module
|
||||
}
|
||||
|
||||
err = sr_get_data(session, CFG_XPATH "//.", 0, 0, 0, &cfg);
|
||||
if (err)
|
||||
if (err || !cfg)
|
||||
goto err_abandon;
|
||||
|
||||
err = srx_get_diff(session, &diff);
|
||||
|
||||
@@ -278,7 +278,7 @@ static int change(sr_session_ctx_t *session, uint32_t sub_id, const char *module
|
||||
}
|
||||
|
||||
err = sr_get_data(session, XPATH "//.", 0, 0, 0, &cfg);
|
||||
if (err)
|
||||
if (err || !cfg)
|
||||
goto err_abandon;
|
||||
|
||||
err = srx_get_diff(session, &diff);
|
||||
|
||||
@@ -104,7 +104,7 @@ static sr_data_t *get(sr_session_ctx_t *session, sr_event_t event, const char *x
|
||||
return NULL; /* Don't care about CHANGE, ABORT, etc. */
|
||||
|
||||
snprintf(path, sizeof(path), "%s//.", xpath);
|
||||
if (sr_get_data(session, path, 0, 0, 0, &cfg)) {
|
||||
if (sr_get_data(session, path, 0, 0, 0, &cfg) || !cfg) {
|
||||
ERROR("no data for %s", path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@ cat <<EOF
|
||||
Exec prefix...........: $eprefix
|
||||
Sysconfdir............: `eval echo $sysconfdir`
|
||||
Localstatedir.........: `eval echo $localstatedir`
|
||||
Default fstab.........: `eval echo $fstab`
|
||||
System environment....: ${sysconfig_path:-${sysconfig}}
|
||||
C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS
|
||||
Linker................: $LD $LLDP_LDFLAGS $LLDP_BIN_LDFLAGS $LDFLAGS $LIBS
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
*~
|
||||
*.o
|
||||
gencert
|
||||
|
||||
/aclocal.m4
|
||||
/autom4te.cache/
|
||||
/aux
|
||||
/clixon.xml
|
||||
/compile
|
||||
/config.h
|
||||
/config.h.in
|
||||
/config.guess
|
||||
/config.log
|
||||
/config.status
|
||||
/config.sub
|
||||
/configure
|
||||
/depcomp
|
||||
/install-sh
|
||||
/libtool
|
||||
/ltmain.sh
|
||||
/m4
|
||||
/missing
|
||||
|
||||
GPATH
|
||||
GRTAGS
|
||||
GTAGS
|
||||
Makefile
|
||||
Makefile.in
|
||||
@@ -0,0 +1,14 @@
|
||||
Copyright (c) 2024 The KernelKit Authors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
with or without fee is hereby granted, provided that the above copyright notice
|
||||
and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
THIS SOFTWARE.
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
DISTCLEANFILES = *~ *.d
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
bin_PROGRAMS = gencert
|
||||
gencert_SOURCES = gencert.c
|
||||
gencert_CFLAGS = -W -Wall -Wextra -Wno-unused
|
||||
gencert_CFLAGS = $(ssl_CFLAGS)
|
||||
gencert_LDADD = $(ssl_LIBS)
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
autoreconf -W portability -vifm
|
||||
@@ -0,0 +1,64 @@
|
||||
AC_PREREQ(2.61)
|
||||
AC_INIT([gencert], [3.0], [https://github.com/kernelkit/infix/issues])
|
||||
AM_INIT_AUTOMAKE(1.11 foreign)
|
||||
AM_SILENT_RULES(yes)
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
Makefile
|
||||
])
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
|
||||
# Check for pkg-config first, warn if it's not installed
|
||||
PKG_PROG_PKG_CONFIG
|
||||
|
||||
PKG_CHECK_MODULES([ssl], [openssl >= 3.0.0])
|
||||
|
||||
test "x$prefix" = xNONE && prefix=$ac_default_prefix
|
||||
test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
|
||||
|
||||
DATAROOTDIR=`eval echo $datarootdir`
|
||||
DATAROOTDIR=`eval echo $DATAROOTDIR`
|
||||
AC_SUBST(DATAROOTDIR)
|
||||
|
||||
LIBDIR=`eval echo $libdir`
|
||||
LIBDIR=`eval echo $LIBDIR`
|
||||
AC_SUBST(LIBDIR)
|
||||
|
||||
LOCALSTATEDIR=`eval echo $localstatedir`
|
||||
LOCALSTATEDIR=`eval echo $LOCALSTATEDIR`
|
||||
AC_SUBST(LOCALSTATEDIR)
|
||||
|
||||
RUNSTATEDIR=`eval echo $runstatedir`
|
||||
RUNSTATEDIR=`eval echo $RUNSTATEDIR`
|
||||
AC_SUBST(RUNSTATEDIR)
|
||||
|
||||
SYSCONFDIR=`eval echo $sysconfdir`
|
||||
SYSCONFDIR=`eval echo $SYSCONFDIR`
|
||||
AC_SUBST(SYSCONFDIR)
|
||||
|
||||
AC_OUTPUT
|
||||
|
||||
cat <<EOF
|
||||
|
||||
------------------ Summary ------------------
|
||||
$PACKAGE_NAME version $PACKAGE_VERSION
|
||||
Prefix................: $prefix
|
||||
Exec prefix...........: $eprefix
|
||||
Sysconfdir............: `eval echo $sysconfdir`
|
||||
Localstatedir.........: `eval echo $localstatedir`
|
||||
System environment....: ${sysconfig_path:-${sysconfig}}
|
||||
C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS
|
||||
Linker................: $LD $LLDP_LDFLAGS $LLDP_BIN_LDFLAGS $LDFLAGS $LIBS
|
||||
|
||||
------------- Compiler version --------------
|
||||
$($CC --version || true)
|
||||
-------------- Linker version ---------------
|
||||
$($LD --version || true)
|
||||
---------------------------------------------
|
||||
|
||||
Check the above options and compile with:
|
||||
${MAKE-make}
|
||||
|
||||
EOF
|
||||
@@ -0,0 +1,172 @@
|
||||
/* SPDX-License-Identifier: ISC */
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/bn.h>
|
||||
|
||||
extern char *__progname;
|
||||
|
||||
static int usage(int rc)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: %s\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" --country COUNTRY Set country, default: US\n"
|
||||
" --state STATE Set state or province, default: California\n"
|
||||
" --city CITY Set city name, default: Berkeley\n"
|
||||
" --organization ORG Set organization name, default: Acme, Inc.\n"
|
||||
" --organization-unit UNIT Set organization unit name, default: Second\n"
|
||||
" --common-name CN Set common name, default: none (required!)\n"
|
||||
" --out-certificate FILE Output certificate\n"
|
||||
" --out-key FILE Output private key\n"
|
||||
, __progname);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct option opts[] = {
|
||||
{"city", required_argument, 0, 'l'},
|
||||
{"common-name", required_argument, 0, 'n'},
|
||||
{"country", required_argument, 0, 'c'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"organization", required_argument, 0, 'o'},
|
||||
{"organisation", required_argument, 0, 'o'},
|
||||
{"organization-unit", required_argument, 0, 'u'},
|
||||
{"organisation-unit", required_argument, 0, 'u'},
|
||||
{"out-certificate", required_argument, 0, 'p'},
|
||||
{"out-key", required_argument, 0, 'k'},
|
||||
{"state", required_argument, 0, 's'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
char *common_name = NULL, *cert = "certificate.pem", *key = "private.key";
|
||||
char *country = "US", *state = "California", *locality = "Berkeley";
|
||||
char *organization = "Acme, Inc.", *unit = "Second";
|
||||
ASN1_INTEGER *asn1_serno;
|
||||
EVP_PKEY_CTX *pkey_ctx;
|
||||
BIGNUM *bn_serial;
|
||||
X509_NAME *name;
|
||||
EVP_PKEY *pkey;
|
||||
X509 *x509;
|
||||
FILE *fp;
|
||||
int c;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "c:hs:l:o:u:n:", opts, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'c':
|
||||
country = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
return usage(0);
|
||||
case 'k':
|
||||
key = optarg;
|
||||
break;
|
||||
case 'l':
|
||||
locality = optarg;
|
||||
break;
|
||||
case 'n':
|
||||
common_name = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
organization = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
cert = optarg;
|
||||
break;
|
||||
case 's':
|
||||
state = optarg;
|
||||
break;
|
||||
case 'u':
|
||||
unit = optarg;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "got here: '%c'\n", c);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (!common_name)
|
||||
return usage(1);
|
||||
|
||||
OpenSSL_add_all_algorithms();
|
||||
|
||||
/* Key generation using EVP */
|
||||
pkey = EVP_PKEY_new();
|
||||
pkey_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
|
||||
if (!EVP_PKEY_keygen_init(pkey_ctx) || !EVP_PKEY_CTX_set_rsa_keygen_bits(pkey_ctx, 2048) ||
|
||||
!EVP_PKEY_keygen(pkey_ctx, &pkey)) {
|
||||
fprintf(stderr, "Error generating RSA key\n");
|
||||
EVP_PKEY_CTX_free(pkey_ctx);
|
||||
return 1;
|
||||
}
|
||||
EVP_PKEY_CTX_free(pkey_ctx);
|
||||
|
||||
/* Certificate creation */
|
||||
x509 = X509_new();
|
||||
assert(x509);
|
||||
X509_set_version(x509, 2);
|
||||
|
||||
/* Generating a random serial number */
|
||||
bn_serial = BN_new();
|
||||
assert(bn_serial);
|
||||
BN_rand(bn_serial, 160, 0, 0); /* Generate a 160-bit serial number */
|
||||
asn1_serno = BN_to_ASN1_INTEGER(bn_serial, NULL);
|
||||
assert(asn1_serno);
|
||||
X509_set_serialNumber(x509, asn1_serno);
|
||||
|
||||
ASN1_TIME_set_string(X509_getm_notBefore(x509), "20000101000000Z");
|
||||
ASN1_TIME_set_string(X509_getm_notAfter(x509), "99990101000000Z");
|
||||
|
||||
name = X509_get_subject_name(x509);
|
||||
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char *)country, -1, -1, 0);
|
||||
X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_ASC, (unsigned char *)state, -1, -1, 0);
|
||||
X509_NAME_add_entry_by_txt(name, "L", MBSTRING_ASC, (unsigned char *)locality, -1, -1, 0);
|
||||
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char *)organization, -1, -1, 0);
|
||||
X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_ASC, (unsigned char *)unit, -1, -1, 0);
|
||||
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)common_name, -1, -1, 0);
|
||||
|
||||
X509_set_issuer_name(x509, name);
|
||||
X509_set_pubkey(x509, pkey);
|
||||
|
||||
if (!X509_sign(x509, pkey, EVP_sha256())) {
|
||||
fprintf(stderr, "Error signing certificate\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Saving the certificate */
|
||||
fp = fopen(cert, "w");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Failed opening %s for writing: %s\n", cert, strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
PEM_write_X509(fp, x509);
|
||||
fclose(fp);
|
||||
|
||||
/* Saving the private key */
|
||||
fp = fopen(key, "w");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Failed opening %s for writing: %s\n", key, strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, NULL, NULL);
|
||||
fclose(fp);
|
||||
|
||||
done: /* Cleanup */
|
||||
X509_free(x509);
|
||||
EVP_PKEY_free(pkey);
|
||||
BN_free(bn_serial);
|
||||
ASN1_INTEGER_free(asn1_serno);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
Copyright (c) 2024 The KernelKit Authors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
with or without fee is hereby granted, provided that the above copyright notice
|
||||
and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
THIS SOFTWARE.
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
Default Landing Page for Infix
|
||||
==============================
|
||||
|
||||
This is a temporary landing page for the Infix web interface pending the
|
||||
upcoming RESTCONF based WebUI application.
|
||||
|
||||
Use `gen.sh` to update `index.html` from the top-level project README.md
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
@@ -20,12 +20,13 @@ int asprintf(char **strp, const char *fmt, ...);
|
||||
|
||||
/*
|
||||
* Only NOTICE and above are logged by default, see setlogmask() for details.
|
||||
* Use `set DEBUG=1` at top of package/confd/confd.conf for logmask = debug
|
||||
*/
|
||||
#define DEBUG(fmt, ...) do { if (debug) syslog(LOG_DEBUG, fmt, ##__VA_ARGS__); } while (0)
|
||||
#define INFO(fmt, ...) syslog(LOG_INFO, fmt, ##__VA_ARGS__)
|
||||
#define NOTE(fmt, ...) syslog(LOG_NOTICE, fmt, ##__VA_ARGS__)
|
||||
#define WARN(fmt, ...) syslog(LOG_WARNING, "%s: " fmt, __func__, ##__VA_ARGS__)
|
||||
#define ERROR(fmt, ...) syslog(LOG_ERR, "%s: " fmt, __func__, ##__VA_ARGS__)
|
||||
#define ERRNO(fmt, ...) syslog(LOG_ERR, "%s: " fmt ": %s", __func__, ##__VA_ARGS__, strerror(errno))
|
||||
#define WARN(fmt, ...) syslog(LOG_WARNING, fmt, ##__VA_ARGS__)
|
||||
#define ERROR(fmt, ...) syslog(LOG_ERR, fmt, ##__VA_ARGS__)
|
||||
#define ERRNO(fmt, ...) syslog(LOG_ERR, fmt ": %s", ##__VA_ARGS__, strerror(errno))
|
||||
|
||||
#endif /* CONFD_COMMON_H_ */
|
||||
|
||||
+3
-1
@@ -526,9 +526,11 @@ int main(int argc, char *argv[])
|
||||
struct statd statd = {};
|
||||
int log_opts = LOG_USER;
|
||||
sr_conn_ctx_t *sr_conn;
|
||||
char *env;
|
||||
int err;
|
||||
|
||||
if (argc > 1 && !strcmp(argv[1], "-d")) {
|
||||
env = getenv("DEBUG");
|
||||
if (env || (argc > 1 && !strcmp(argv[1], "-d"))) {
|
||||
log_opts |= LOG_PERROR;
|
||||
debug = 1;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
- case: meta/reproducible.py
|
||||
- case: meta/wait.py
|
||||
|
||||
- name: Misc tests
|
||||
|
||||
@@ -12,7 +12,7 @@ env = infamy.Env()
|
||||
ctrl = env.ptop.get_ctrl()
|
||||
infixen = env.ptop.get_infixen()
|
||||
for ix in infixen:
|
||||
(_, cport), (_, ixport) = env.ptop.get_path(ctrl, ix)
|
||||
cport, ixport = env.ptop.get_mgmt_link(ctrl, ix)
|
||||
print(f"Attaching to {ix}:{ixport} via {ctrl}:{cport}")
|
||||
exec(f"{ix} = env.attach(\"{ix}\", \"{ixport}\")")
|
||||
|
||||
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
|
||||
import infamy
|
||||
|
||||
with infamy.Test() as test:
|
||||
with test.step("$PYTHONHASHSEED is set"):
|
||||
seed = os.environ.get("PYTHONHASHSEED")
|
||||
if seed == None:
|
||||
print("$PYTHONHASHSEED must be set in order to create a reproducible test environment")
|
||||
test.fail()
|
||||
else:
|
||||
print(f"Specify PYTHONHASHSEED={seed} to reproduce this test environment")
|
||||
|
||||
test.succeed()
|
||||
@@ -8,13 +8,13 @@ import infamy, infamy.neigh
|
||||
def ll6ping(node):
|
||||
neigh = None
|
||||
|
||||
for (_, cport), (_, nport) in env.ptop.get_paths(ctrl, node):
|
||||
neigh = infamy.neigh.ll6ping(cport, flags=["-w1", "-c1", "-L", "-n"])
|
||||
if neigh:
|
||||
print(f"Found {neigh} on {cport} (connected to {node}:{nport})")
|
||||
break
|
||||
cport, nport = env.ptop.get_mgmt_link(ctrl, node)
|
||||
neigh = infamy.neigh.ll6ping(cport, flags=["-w1", "-c1", "-L", "-n"])
|
||||
if neigh:
|
||||
print(f"Found {neigh} on {cport} (connected to {node}:{nport})")
|
||||
return neigh
|
||||
|
||||
return neigh
|
||||
return None
|
||||
|
||||
def netconf_syn(neigh):
|
||||
try:
|
||||
|
||||
@@ -157,6 +157,7 @@ if [ "$containerize" ]; then
|
||||
--cap-add=NET_RAW \
|
||||
--cap-add=NET_ADMIN \
|
||||
--device=/dev/net/tun \
|
||||
--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 # ' \
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ class Env(object):
|
||||
if not password:
|
||||
password = "admin"
|
||||
ctrl = self.ptop.get_ctrl()
|
||||
_, cport = self.ptop.get_path(ctrl, (node, port))[0]
|
||||
cport, _ = self.ptop.get_mgmt_link(ctrl, node)
|
||||
|
||||
print(f"Probing {node} on port {cport} for IPv6LL mgmt address ...")
|
||||
mgmtip = neigh.ll6ping(cport)
|
||||
|
||||
+64
-78
@@ -1,88 +1,79 @@
|
||||
import networkx as nx
|
||||
from networkx.algorithms import isomorphism
|
||||
|
||||
def qstrip(text):
|
||||
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)
|
||||
pes = sorted(list(pes.values()), key=lambda x: x.get("kind", ""), reverse=True)
|
||||
|
||||
def match_node(n1attrs, n2attrs):
|
||||
return n1attrs.get("kind") == n2attrs.get("kind")
|
||||
for i in range(len(les)):
|
||||
if pes[i].get("kind") != les[i].get("kind"):
|
||||
return None
|
||||
|
||||
def match_edge(e1attrs, e2attrs):
|
||||
if "kind" in e1attrs or "kind" in e2attrs:
|
||||
return e1attrs.get("kind") == e2attrs.get("kind")
|
||||
acc.append((les[i], pes[i]))
|
||||
|
||||
return True
|
||||
return acc
|
||||
|
||||
def match_node(pn, ln):
|
||||
return pn.get("kind") == ln.get("kind")
|
||||
|
||||
def match_edge(pes, les):
|
||||
return map_edges(les, pes) != None
|
||||
|
||||
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
|
||||
edges = {}
|
||||
self.g = nx.MultiGraph()
|
||||
|
||||
for n in self.dotg.get_nodes():
|
||||
name = n.get_name()
|
||||
if name in ("node", "edge"):
|
||||
continue
|
||||
|
||||
repr(n.get_attributes())
|
||||
attrs = { _qstrip(k): _qstrip(v) for k, v in n.get_attributes().items() if k != "label" }
|
||||
self.g.add_node(name, **attrs)
|
||||
|
||||
for e in self.dotg.get_edges():
|
||||
attrs = e.get_attributes()
|
||||
if "weight" not in attrs:
|
||||
attrs["weight"] = 1
|
||||
|
||||
edges[tuple(e.get_source().split(":"))] = { tuple(e.get_destination().split(":")): attrs }
|
||||
sn, sp = e.get_source().split(":")
|
||||
dn, dp = e.get_destination().split(":")
|
||||
|
||||
|
||||
self.g = nx.Graph(edges, weight=1)
|
||||
for e in list(self.g.edges):
|
||||
s, d = e
|
||||
self.g.nodes[s]["kind"] = "port"
|
||||
self.g.nodes[d]["kind"] = "port"
|
||||
|
||||
sn, sp = s
|
||||
dn, dp = d
|
||||
|
||||
try:
|
||||
sk = qstrip(self.dotg.get_node(sn)[0].get_attributes()["kind"])
|
||||
except:
|
||||
raise ValueError("\"{}\"'s kind is not known".format(sn))
|
||||
|
||||
try:
|
||||
dk = qstrip(self.dotg.get_node(dn)[0].get_attributes()["kind"])
|
||||
except:
|
||||
raise ValueError("\"{}\"'s kind is not known".format(dn))
|
||||
|
||||
self.g.add_node(sn, kind=sk)
|
||||
self.g.add_edge(sn, s, weight=0)
|
||||
self.g.add_node(dn, kind=dk)
|
||||
self.g.add_edge(dn, d, weight=0)
|
||||
attrs = { _qstrip(k): _qstrip(v) for k, v in e.get_attributes().items() }
|
||||
attrs[sn] = sp
|
||||
attrs[dn] = dp
|
||||
self.g.add_edge(sn, dn, **attrs)
|
||||
|
||||
def map_to(self, phy):
|
||||
def _map_node(lnode, pnode):
|
||||
if lnode in self.mapping:
|
||||
assert(self.mapping[lnode][None] == pnode)
|
||||
else:
|
||||
self.mapping[lnode] = { None: pnode }
|
||||
|
||||
def _map_port(log, phy):
|
||||
(lnode, lport) = log
|
||||
(pnode, pport) = phy
|
||||
|
||||
if lport in self.mapping[lnode]:
|
||||
assert(self.mapping[lnode][lport] == pport)
|
||||
else:
|
||||
self.mapping[lnode][lport] = pport
|
||||
|
||||
nxmap = isomorphism.GraphMatcher(phy.g, self.g, edge_match=match_edge, node_match=match_node)
|
||||
if not nxmap.subgraph_is_isomorphic():
|
||||
mapper = isomorphism.MultiGraphMatcher(phy.g, self.g,
|
||||
edge_match=match_edge,
|
||||
node_match=match_node)
|
||||
if not mapper.subgraph_is_monomorphic():
|
||||
return False
|
||||
|
||||
self.phy = phy
|
||||
self.mapping = {}
|
||||
for (phy, log) in nxmap.mapping.items():
|
||||
if isinstance(log, tuple):
|
||||
lnode, lport = log
|
||||
pnode, pport = phy
|
||||
|
||||
_map_node(lnode, pnode)
|
||||
_map_port((lnode, lport), (pnode, pport))
|
||||
else:
|
||||
_map_node(log, phy)
|
||||
for pn, ln in mapper.mapping.items():
|
||||
self.mapping.setdefault(ln, { None: pn })
|
||||
|
||||
for lsrc, ldst in set(self.g.edges()):
|
||||
psrc = self.mapping[lsrc][None]
|
||||
pdst = self.mapping[ldst][None]
|
||||
|
||||
les = self.g.get_edge_data(lsrc, ldst)
|
||||
pes = self.phy.g.get_edge_data(psrc, pdst)
|
||||
|
||||
for le, pe in map_edges(les, pes):
|
||||
self.mapping[lsrc][le[lsrc]] = pe[psrc]
|
||||
self.mapping[ldst][le[ldst]] = pe[pdst]
|
||||
|
||||
return True
|
||||
|
||||
@@ -116,20 +107,16 @@ class Topology:
|
||||
password=b.get("password")
|
||||
return qstrip(password) if password is not None else None
|
||||
|
||||
def get_ports(self, node):
|
||||
ports = self.get_nodes(lambda name, _: name.startswith(f"{node}:"))
|
||||
return { p.removeprefix(f"{node}:") for p in ports }
|
||||
def get_link(self, src, dst, flt=lambda _: True):
|
||||
es = self.g.get_edge_data(src, dst)
|
||||
for e in es.values():
|
||||
if flt(e):
|
||||
return e[src], e[dst]
|
||||
|
||||
def get_path(self, src, dst):
|
||||
path = nx.shortest_path(self.g, src, dst)
|
||||
return path[1:-1] if path else None
|
||||
return None
|
||||
|
||||
def get_paths(self, src, dst):
|
||||
paths = nx.all_shortest_paths(self.g, src, dst)
|
||||
if not paths:
|
||||
return None
|
||||
|
||||
return map(lambda path: path[1:-1], paths)
|
||||
def get_mgmt_link(self, src, dst):
|
||||
return self.get_link(src, dst, lambda e: e.get("kind") == "mgmt")
|
||||
|
||||
def get_ctrl(self):
|
||||
ns = self.get_nodes(lambda _, attrs: attrs.get("kind") == "controller")
|
||||
@@ -139,7 +126,6 @@ class Topology:
|
||||
def get_infixen(self):
|
||||
return self.get_nodes(lambda _, attrs: attrs.get("kind") == "infix")
|
||||
|
||||
|
||||
# Support calling this script like so...
|
||||
#
|
||||
# python3 topology.py <physical> <logical>
|
||||
|
||||
Reference in New Issue
Block a user