Skip to content

Commit 13221d1

Browse files
authored
Merge pull request #1647 from kernelkit/tftp-server
Add TFTP server and DHCP bootfile support
2 parents aeb92b0 + 71142d9 commit 13221d1

57 files changed

Lines changed: 1761 additions & 230 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/trigger.yml‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,26 @@ on:
99
- ci-work
1010
workflow_dispatch:
1111

12+
# A label event that neither starts nor stops a run is skipped by
13+
# check-trigger, keep those in a group of their own so they cannot cancel
14+
# a run in progress. Adding 'ci:skip' does share the group, on purpose,
15+
# to stop a build that is no longer wanted.
1216
concurrency:
13-
group: ci-${{ github.event.pull_request.number || github.ref }}
17+
group: ci-${{ github.event.pull_request.number || github.ref }}${{ (github.event.action == 'labeled' && github.event.label.name != 'ci:main' && github.event.label.name != 'ci:skip') && '-label' || '' }}
1418
cancel-in-progress: true
1519

1620
jobs:
1721
# Gate all builds through this check to prevent wasted runs. Only run on
1822
# 'labeled' events when the label is actually 'ci:main'. Concurrency control
19-
# above handles canceling the 'opened' event when 'labeled' arrives quickly
23+
# above handles canceling the 'opened' event when 'ci:main' arrives quickly
2024
# after (e.g., when creating a PR with ci:main already attached). See #1154.
25+
#
26+
# A PR labeled 'ci:skip' builds nothing, for changes that cannot affect
27+
# the image, e.g. a ChangeLog fixup after someone else's branch landed.
2128
check-trigger:
2229
if: |
2330
startsWith(github.repository, 'kernelkit/') &&
31+
!contains(github.event.pull_request.labels.*.name, 'ci:skip') &&
2432
(github.event_name != 'pull_request' ||
2533
github.event.action != 'labeled' ||
2634
github.event.label.name == 'ci:main')

‎board/common/rootfs/usr/bin/dir‎

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,8 @@
22

33
dir()
44
{
5-
path=$1
6-
7-
if [ -z "$COLUMS" ]; then
8-
TTY=$(resize)
9-
eval "$TTY"
10-
fi
5+
path=${1%/}
6+
[ -n "$path" ] || path=/
117

128
printf "\033[7m%-*s\033[0m\n" "$COLUMNS" "$path directory"
139
if [ -d "$path" ]; then
@@ -18,14 +14,35 @@ dir()
1814
echo
1915
}
2016

21-
if [ -d "$1" ]; then
22-
dir "$1"
23-
else
17+
# Directories copy(1) can read and write, and the user is likely to
18+
# browse. The TFTP root follows the server when it is enabled.
19+
locations()
20+
{
2421
if [ "$USER" = "root" ]; then
25-
dir "$HOME"
22+
echo "$HOME"
2623
else
27-
dir "/home/$USER"
24+
echo "/home/$USER"
2825
fi
29-
dir "/cfg"
30-
dir "/log"
26+
echo "/cfg"
27+
sed -n 's/^tftp-root=//p' /etc/dnsmasq.d/tftp.conf 2>/dev/null || true
28+
echo "/var/lib/tftpboot"
29+
echo "/media"
30+
echo "/log"
31+
}
32+
33+
# resize(1) talks to the terminal, so it has to run before any pipeline
34+
# that would hand it something else on stdin
35+
if [ -z "$COLUMNS" ]; then
36+
TTY=$(resize)
37+
eval "$TTY"
38+
fi
39+
: "${COLUMNS:=80}"
40+
41+
if [ -n "$1" ]; then
42+
dir "$1"
43+
else
44+
# The configured TFTP root may well be the default one
45+
for path in $(locations | awk '!seen[$0]++'); do
46+
[ -d "$path" ] && dir "$path"
47+
done
3148
fi
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
d /var/lib/tftpboot 2775 root wheel

‎doc/ChangeLog.md‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,13 @@ All notable changes to the project are documented in this file.
2222
- WebUI: the support bundle is collected with the `infix-system:support-collect`
2323
RPC as the logged-in user, so NACM decides who may download it, rather
2424
than by running the tool as root
25+
- The CLI `dir` command lists directories as the logged-in user, so it
26+
shows only what that user may read
2527

2628
### Added
2729

30+
- The CLI accepts an unambiguous prefix of a command name, e.g. `sh int`
31+
for `show interface`
2832
- Add `/system/advanced` for low-level system customization, issue #463:
2933
- `rc.d`: user scripts stored in the configuration, run once at boot
3034
after the startup configuration has been applied, in the order listed
@@ -43,6 +47,14 @@ All notable changes to the project are documented in this file.
4347
statistics` replaces `dhcp-server clear-statistics`. `set datetime` now
4448
also accepts free-form input, e.g., `14:05`, and echoes the ISO-8601
4549
value it sets
50+
- The CLI `remove` command now offers the startup configuration, and
51+
warns that removing it leaves the system booting factory defaults
52+
- Add CLI `rename` command, for renaming or moving a file without
53+
copying it, e.g. `rename startup-config backup` to keep a
54+
configuration before starting over. Directories in the destination
55+
are created as needed
56+
- The CLI completes file system paths with Tab, for `copy`, `rename`,
57+
`remove`, and `dir`, limited to the directories those commands accept
4658
- The CLI `configure` command takes an optional path to start in a
4759
sub-context directly, e.g., `configure system authentication`
4860
- `/bin/sh` is now provided by Busybox ash instead of Bash, speeding up
@@ -67,6 +79,16 @@ All notable changes to the project are documented in this file.
6779
as `/interfaces/interface[name='eth0']/if-index`. SNMP SET, SNMPv3,
6880
notifications and view-based access control are not supported.
6981
Disabled by default, see [SNMP][snmp]
82+
- Add TFTP server for network boot and device provisioning, issue #1542.
83+
Read-only, serving `/var/lib/tftpboot` or a directory on USB media, with
84+
optional per-client subdirectories. `show tftp` lists the files served,
85+
see [TFTP Server](tftp.md)
86+
- Add network boot parameters to DHCP server: `boot file`, `server-address`,
87+
and `server-name` at global, subnet, or host scope, sent in the BOOTP header
88+
fields and as options 66/67
89+
- The CLI `copy` and `remove` commands now also accept files in `/var/lib`,
90+
`/var/tmp`, and `/tmp`. Files written there are world-readable.
91+
The `.cfg`extension is only added for files in `/cfg`
7092

7193
### Fixes
7294

‎doc/README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ regression test system solely relies on NETCONF and RESTCONF.
2323
- [Network Configuration](networking.md)
2424
- [Wi-Fi](wifi.md)
2525
- [DHCP Server](dhcp.md)
26+
- [TFTP Server](tftp.md)
2627
- [Syslog Support](syslog.md)
2728
- **Infix In-Depth**
2829
- [Boot Procedure](boot.md)

‎doc/developers-guide.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,14 @@ $ git submodule update --init
523523
> in the GUI for your fork for this purpose. A cronjob on your server
524524
> of choice can do this for you with the [GitHub CLI tool][7].
525525
526+
CI on a pull request is controlled with two labels. With neither, the
527+
minimal images are built and the default tests run.
528+
529+
- `ci:main` builds the full images and runs the complete test suite
530+
- `ci:skip` builds nothing, for changes that cannot affect the image,
531+
e.g. a ChangeLog fixup after another branch landed. Adding it to an
532+
open pull request also stops a build already running
533+
526534
[^1]: Organizations should make sure to lock the `main` (or `master`)
527535
branch of their clones to ensure members do not accidentally merge
528536
changes there. Keeping these branches in sync with upstream Infix

‎doc/dhcp.md‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,44 @@ admin@example:/config/dhcp-server/subnet/192.168.2.0/24/> <b>leave</b>
140140
</code></pre>
141141

142142

143+
## Network Boot
144+
145+
Devices that boot over the network, or fall back to it when their own
146+
firmware is damaged, learn the name of the boot file and the address of
147+
the TFTP server from the DHCP server. These are set with `boot`, which
148+
can be given globally, per subnet, or per static host. The most
149+
specific scope wins.
150+
151+
<pre class="cli"><code>admin@example:/config/dhcp-server/> <b>edit subnet 192.168.2.0/24</b>
152+
admin@example:/config/dhcp-server/subnet/192.168.2.0/24/> <b>set boot file fallback.itb</b>
153+
admin@example:/config/dhcp-server/subnet/192.168.2.0/24/> <b>leave</b>
154+
</code></pre>
155+
156+
By default the server address handed out is the system's own address
157+
on the interface facing the client, matching the built-in
158+
[TFTP server](tftp.md). Set `boot server-address` to point clients at
159+
another server instead.
160+
161+
The boot file and server address are sent both in the BOOTP header
162+
fields, which BOOTP clients, bootloaders like U-Boot, and PXE ROMs read,
163+
and as DHCP options 66 and 67 to clients that request them. The two
164+
options cannot be set in the `option` list when `boot` is used.
165+
166+
> [!CAUTION]
167+
> Boot files are handed out over TFTP, which has no authentication.
168+
> Anything below the server root can be read by any client, see
169+
> [TFTP Server](tftp.md).
170+
171+
To hand a single device a different image, e.g., during a staged
172+
rollout, set `boot` on its static host entry:
173+
174+
<pre class="cli"><code>admin@example:/config/dhcp-server/subnet/192.168.2.0/24/> <b>edit host 192.168.2.10</b>
175+
admin@example:/config/dhcp-server/…/192.168.2.10/> <b>set match mac-address 00:11:22:33:44:55</b>
176+
admin@example:/config/dhcp-server/…/192.168.2.10/> <b>set boot file staging.itb</b>
177+
admin@example:/config/dhcp-server/…/192.168.2.10/> <b>leave</b>
178+
</code></pre>
179+
180+
143181
## Monitoring
144182

145183
View active leases and server statistics:

‎doc/tftp.md‎

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
TFTP Server
2+
===========
3+
4+
The TFTP server hands out files to devices on the local network, for
5+
example a fallback boot image for devices whose own firmware partition
6+
has failed, or configuration files for IP phones and similar equipment.
7+
It is read-only, so clients cannot upload files.
8+
9+
Files are served from a root directory, by default `/var/lib/tftpboot`.
10+
This directory is persistent on all supported boards and writable by
11+
admin users, so files can be placed there from the CLI or a shell. A
12+
directory on USB media, e.g., `/media/usb/tftp`, can be used instead.
13+
14+
> [!IMPORTANT]
15+
> Only world-readable files are served. Files copied with the CLI
16+
> `copy` command are made world-readable automatically, files copied
17+
> from a shell must be given mode `0644` or similar.
18+
19+
> [!CAUTION]
20+
> TFTP has no authentication. Every file below the root can be read by
21+
> any client that reaches the server, so never keep a saved system
22+
> configuration there -- it carries password hashes, keys, and other
23+
> secrets. Limit the server to the interfaces facing the clients.
24+
25+
26+
## Basic Configuration
27+
28+
<pre class="cli"><code>admin@example:/> <b>configure</b>
29+
admin@example:/config/> <b>set tftp enabled true</b>
30+
admin@example:/config/> <b>leave</b>
31+
</code></pre>
32+
33+
The server listens on all interfaces by default. To restrict it to a
34+
subset, list the interfaces to serve on:
35+
36+
<pre class="cli"><code>admin@example:/config/> <b>edit tftp</b>
37+
admin@example:/config/tftp/> <b>set interface eth1</b>
38+
admin@example:/config/tftp/> <b>set interface eth2</b>
39+
admin@example:/config/tftp/> <b>leave</b>
40+
</code></pre>
41+
42+
When the firewall is enabled, the `tftp` service must also be allowed
43+
in the zone facing the clients, see [Firewall](firewall.md).
44+
45+
46+
## Uploading Files
47+
48+
Files can be fetched to the TFTP root with the `copy` command from any
49+
of the supported remote sources, or copied from USB media. A directory
50+
destination keeps the source file name:
51+
52+
<pre class="cli"><code>admin@example:/> <b>copy tftp://192.168.1.1/fallback.itb /var/lib/tftpboot/</b>
53+
admin@example:/> <b>copy /media/usb/phones.cfg /var/lib/tftpboot/</b>
54+
admin@example:/> <b>dir /var/lib/tftpboot</b>
55+
/var/lib/tftpboot directory
56+
fallback.itb phones.cfg
57+
</code></pre>
58+
59+
Files are removed with the `remove` command, which asks for
60+
confirmation:
61+
62+
<pre class="cli"><code>admin@example:/> <b>remove /var/lib/tftpboot/phones.cfg</b>
63+
Remove /var/lib/tftpboot/phones.cfg, are you sure? (y/N)? y
64+
</code></pre>
65+
66+
67+
## Per-Client Directories
68+
69+
Some devices, IP phones in particular, expect a configuration file with
70+
a fixed name that differs per device. With `client-directory` set, the
71+
server first looks for the requested file in a subdirectory of the root
72+
named after the client, and falls back to the root itself if there is
73+
none:
74+
75+
<pre class="cli"><code>admin@example:/config/tftp/> <b>set client-directory mac</b>
76+
</code></pre>
77+
78+
With this setting a request for `config.xml` from the device with MAC
79+
address `00:11:22:33:44:55` is answered with
80+
`/var/lib/tftpboot/00-11-22-33-44-55/config.xml` if that file exists,
81+
otherwise with `/var/lib/tftpboot/config.xml`. Use `ip` instead of
82+
`mac` to name the directories after the client IP address.
83+
84+
85+
## Network Boot
86+
87+
Devices that boot over the network learn the boot file name and TFTP
88+
server address from the DHCP server. See [Network Boot](dhcp.md#network-boot)
89+
in the DHCP server documentation for how to hand these out.
90+
91+
92+
## Monitoring
93+
94+
<pre class="cli"><code>admin@example:/> <b>show tftp</b>
95+
Root directory : /var/lib/tftpboot
96+
Interfaces : all
97+
Client directory : none
98+
99+
<span class="header">NAME SIZE MODIFIED </span>
100+
fallback.itb 7.0M 2026-09-18 05:18
101+
phones.cfg 812B 2026-09-17 12:00
102+
</code></pre>
103+
104+
The file list is the operational view of the root directory and shows
105+
only files the server can actually hand out. A file missing from the
106+
list is either not world-readable or outside the configured root.

‎mkdocs.yml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ nav:
2424
- Network Calculator: cli/netcalc.md
2525
- Network Monitoring: cli/tcpdump.md
2626
- Quickstart Guide: cli/quick.md
27-
- Text Editor: cli/text-editor.md
27+
- Text Editor: cli/edit.md
2828
- Upgrading: cli/upgrade.md
2929
- Docker Containers: container.md
3030
- Networking:
@@ -50,6 +50,7 @@ nav:
5050
- NTP Server: ntp.md
5151
- PTP (IEEE 1588/802.1AS): ptp.md
5252
- SNMP: snmp.md
53+
- TFTP Server: tftp.md
5354
- System:
5455
- Boot Procedure: boot.md
5556
- Configuration: system.md

‎package/bin/bin.mk‎

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,4 @@ define BIN_BUILD_PYTHON
4141
endef
4242
BIN_POST_INSTALL_TARGET_HOOKS += BIN_BUILD_PYTHON
4343

44-
define BIN_INSTALL_BASH_COMPLETION
45-
install -D $(@D)/bash_completion.d/show \
46-
$(TARGET_DIR)/etc/bash_completion.d/show
47-
endef
48-
BIN_POST_INSTALL_TARGET_HOOKS += BIN_INSTALL_BASH_COMPLETION
49-
5044
$(eval $(autotools-package))

0 commit comments

Comments
 (0)