| Age | Commit message (Collapse) | Author |
|
openvpn: T9370: add chacha20poly1305 data cipher
|
|
bgp: T9404: do not render "bgp default local-preference" when the value is 100
|
|
T9351: Accel-PPP-NG: Add CLI support for Message Authenticator attribute in Access packets (Blast-RADIUS mitigation)
|
|
FRR does not print "bgp default local-preference 100" in show
running-config because 100 is its default, so frr-reload never finds the
line in the running config and sends it again on every reload. The
command handler calls bgp_clear_star_soft_in() whether or not the value
changed, so every reload ran "clear bgp * soft in" (once in pass 0 and
twice in pass 1), a full inbound re-evaluation of every peer.
Add the default (100) to the XML definition of parameters default
local-pref. Templates cannot query the XML definition while rendering,
so frrender takes the value from the config defaults of "parameters
default" (conf.get_config_defaults) and adds it to the BGP dict as the
key xml_default_local_pref, for the global instance and for every VRF
instance. bgpd.frr.j2 renders the line only when the configured value
differs from xml_default_local_pref, so the value 100 is not hardcoded
in the template. A real value change (100 to 200, or 200 to 100) is
still sent: frr-reload sees the difference between the rendered file and
the running config.
|
|
"show secure-boot" only reported whether Secure Boot is enabled, not who
signed the running Linux Kernel. An image signed by the VyOS CA could not be
told apart from one signed with a custom Machine Owner Key.
"show secure-boot detail" now lists issuer and subject of the Kernel image
signature certificates. The same information is added to the raw
"show version" data as "secure_boot_detail", issuer and subject delimited by
",". The key is only present when Secure Boot is enabled and a signature
could be read.
|
|
config-mgmt: T9399: read the committed config once in the post-commit hooks
|
|
frrender: T9377: do not save the FRR configuration a second time
|
|
pppoe: T9395: render default routes via FRR only
|
|
01vyos-commit-revision built a full Config() in ConfigMgmt() (active and
working config, the working one read through the session's unionfs-fuse)
and then ran vyos-save-config.py, which built a second full Config() to
write the same active config out. On a config with a 2,000-entry
prefix-list the hook took 17 s of every commit.
The hooks run after the active config has been written, so read it once
from the active store, and write the archive copy and config.json from the
tree already held. The read is made as ConfigSourceSession makes it
(stdout only, unmodified), and as there, no running config is passed
until the boot configuration is complete, so the boot commit's hooks see
no effective values and 02vyos-commit-archive uploads nothing at boot.
After a commit that does not end in COMMIT_STATUS=SUCCESS (PARTIAL in
the tests; a conf_mode script failure gives PARTIAL, and FAILURE takes
the same branch) the hooks read the config the stock way (a full Config()
of the working config); only the archive copy is still written from that
tree in-process instead of by vyos-save-config.py.
vyatta-cfg's discard removes the session's changes underneath the
unionfs-fuse mount, and a set within about a second of it fails while the
kernel still caches the removed directories; the stock hook takes longer
than that and so hides it. A faster hook after a failed commit made
smoketest test_bgp_27_route_reflector_client fail; on stock it fails the
same way with 01vyos-commit-revision removed.
confirm and initialize_revision use the same archive helper, so they also
write the archive copy from the tree already held instead of starting
vyos-save-config.py.
|
|
Deleting a PPPoE interface shortly after its session came up could fail the
commit with "% could not lock candidate DS". The default routes were installed
via vtysh both from the commit and from the asynchronous ppp ip-up hook, so the
two FRR sessions raced for the mgmtd lock. The IPv6 default route was also
missing from the rendered FRR configuration, so an FRR reload could remove it.
Both default routes are now part of the rendered FRR configuration, which
vyos-configd applies at the end of every commit. PPPoE no longer calls vtysh.
|
|
|
|
T9341: Fix consistency of legacy templates, xml_ref cache, and reference tree
|
|
apply() runs frr-reload.py --reload without --daemon, and in that mode
frr-reload.py saves the configuration itself (vtysh "write" to
/etc/frr/frr.conf, bind-mounted from /run/frr/config/frr.conf). The
following vtysh -n --writeconfig, added by T3217 when VyOS still reloaded
one daemon at a time with --daemon, writes the same file again and asks
every daemon for its configuration once more.
That second round trip comes several seconds after the change was
applied. On a large FRR configuration it reaches bgpd after the route-map
update timer (5 s) has started a policy walk, and the commit then waits
until the walk ends: on a 20,000-line FRR configuration with 100 update
groups and 500,000 routes a policy commit took 497.9/501.0 s against
381.1/381.6 s without this second save.
|
|
Offer chacha20poly1305 in "encryption data-ciphers" and
"encryption data-ciphers-fallback", rendered as CHACHA20-POLY1305 and
allowed with DCO.
|
|
modem_connect() only ran the --3gpp-set-initial-eps-bearer-settings
pre-step (added by T9326) when the requested ip-type included IPv6,
gated on options.partition('ip-type=')[2].partition(',')[0] in ('ipv6',
'ipv4v6'). A bare 'ipv4' request skipped it entirely.
Confirmed live (same MC7455/Google Fi setup as T9326's original report)
that the underlying ip-version-mismatch rejection isn't IPv6-specific:
with the attach committed to ip-type=ipv6, a bare ip-type=ipv4
--simple-connect fails outright with the same QMI CallEndReason, not
just the ipv4v6 case's silent single-family drop. A WWAN interface
whose configured address family changes (e.g. dropping dhcpv6 down to
dhcp-only) can hit exactly this, and won't self-heal on retry since
nothing about a failed connect touches the attach-committed PDN type -
vyos-netlinkd's reconcile loop would back off and retry the identical
failing request forever.
Removed the gate entirely: the pre-step already fails silently and
falls through unchanged on a modem/network that doesn't need it, so
there's no reason to skip it for any particular family.
|
|
wwan: T7487: identify modem by port ownership, not interface number
|
|
mmcli can return empty or non-JSON output while ModemManager is still
starting (T6604), and json.loads() raising on that is a ValueError, not an
OSError - it was propagating straight out of _modem_owning_port() uncaught.
Under wait=True that reaches wait_for()'s callback and aborts the poll loop
instead of retrying, exactly the startup race this module exists to ride
out.
Co-authored-by: Christian Breunig <christian@breunig.cc>
|
|
Bugfix which allows for BaseX link modes reported by ethtool to be correctly
validated, so config can be commited.
|
|
Requested by c-po: json.loads and wait_for were only used by
_modem_owning_port() and modem_index()'s wait branch respectively, so
importing them at module level cost every other caller of this module an
import it does not need.
|
|
modem_index() unconditionally polled for up to modem_wait_timeout before
returning None, so every caller that expects "no modem" as a routine
outcome - interface teardown, admin-disconnect markers, disconnect/connect
status checks, and vyos-netlinkd's reconcile loop checking every configured
interface once per pass - blocked on it too. The reconcile loop in
particular calls is_wwan_connected() synchronously, not per-interface, so
one interface with no modem stalled the whole daemon's reconcile pass,
including unrelated DHCP interfaces, every cycle.
Add a wait parameter, default False (single check, returns immediately).
Only start_modem_manager() passes wait=True, where waiting for a
still-initializing modem is the actual point.
|
|
modem_index() in python/vyos/utils/wwan.py derived the ModemManager modem index straight from the interface name (wwan0 -> 0). The kernel-assigned WWAN interface number and ModemManager's own modem index are two independently enumerated values and are not guaranteed to match - on a box with more than one modem, they can enumerate in different orders, so modem index 0 does not necessarily own wwan0. Every function in this module funnels through modem_index() (start_modem_manager, modem_disconnect, modem_connect, and both admin-disconnect marker helpers), and is_wwan_connected() in vyos.utils.network had its own separate copy of the same assumption - so a single reversed enumeration order silently commands the wrong physical modem for every one of them.
modem_index() now lists every modem ModemManager currently knows about and returns whichever one actually has ifname in its own port list, instead of assuming the correspondence. A modem that has not shown up on the bus yet looks the same as one that will never exist, so this waits up to the same modem_wait_timeout budget start_modem_manager() already gives a modem to become dialable, then returns None if nothing claimed the interface. is_wwan_connected() now calls the same resolver instead of keeping its own copy of the old assumption.
None is a real, expected return now, not just a theoretical one - the three callers that previously assumed modem_index() always produced *a* string (right or wrong) are updated: start_modem_manager() bails out early (matching the tolerance its own caller's w.exists() check already has for hardware that isn't detected yet), modem_disconnect() and modem_connect() treat it as nothing to disconnect / a failed dial, matching each function's own existing return contract. show_wwan.py's modem_info() raises a clear, specific error instead, which its own top-level exception handler already reports cleanly. set_admin_disconnected()/clear_admin_disconnected() only ever used modem_index() to validate the interface name looks like a WWAN interface in the first place, so they're unaffected.
|
|
not related to T9351)
|
|
in Access packets
Add CLI options to control Message-Authenticator handling for Access
packets.
The new options allow including Message-Authenticator in Access-Request
packets and requiring it in Access-Accept, Access-Reject and
Access-Challenge responses. Render the corresponding accel-ppp-ng
configuration options and warn when response validation is configured as
required while Access-Request packets are not configured to include
Message-Authenticator.
|
|
wwan: T9350: possible dead interface after boot, disconnect does not stick
|
|
op-mode: T9354: replace "reset connection" with "reconnect interface"
|
|
T8977: Fix regression with is_addr_assigned and is_listen_port_bind_service
|
|
(#5504)
The Router Solicitation kicked off when an IPv6 autoconf PPPoE link comes up
is best effort - the peer may answer late or not at all. rdisc6(8) then blocks
for the duration of all retries and exits non-zero, which aborted the commit:
FileNotFoundError: [Errno 2] failed to run command: rdisc6 --single --retry 3 pppoe1000
returned: Soliciting ff02::2 (ff02::2) on pppoe1000...
Timed out.
No response.
exit code: 2
Run rdisc6(8) detached in a transient systemd unit, so neither the delay nor
the exit code reaches the commit. The outcome is logged to the journal.
|
|
accel-ppp: T9357: do not require local users when any-login is configured
|
|
create_private_key() took one "size" for every key type, but the number
means entirely different things per type: for RSA and DSA it is a modulus
size, for EC it selects a curve. CodeQL is not path sensitive across the
key_type comparison, so it follows the 256 an EC caller passes into the
RSA and DSA branches that caller can never reach, and reports pki.py as
generating 256 bit RSA and DSA keys.
Build elliptic curve keys in create_ec_private_key() instead and route the
op mode generator and the OpenVPN smoketest there, so no curve size ever
enters the function that also builds RSA and DSA keys.
create_private_key() now refuses a key type it does not build rather than
returning None, so a caller cannot drift back into the old shape
unnoticed.
|
|
The "default to SECP256R1" branch stored an instance where the other branch
stored the class, so the shared call at the end invoked the instance and
raised "'SECP256R1' object is not callable". Any curve size without a
matching SECP<size>R1 - the very case the default exists for - aborted
instead of falling back.
Take the default through getattr() so both paths carry the class.
|
|
Commit a9e9c4acfa ("pki: openvpn: T3642: Migrate OpenVPN to PKI and refactor")
was the last caller of verify_diffie_hellman_length() despite the build time
testcases.
Unused code is dead code, drop it.
|
|
The Diffie-Hellman length check stringified the requested minimum keysize
into a variable it never used, then converted the original argument again
for the comparison. A non-numeric value therefore escaped as an uncaught
ValueError from the comparison rather than being reported where it was
passed in.
The argument is now typed and validated up front: a non-integer raises
TypeError, booleans included, and a non-positive integer raises
ValueError. Both indicate a logic error in the calling script and should
fail loudly instead of silently reporting an undersized key. The only
callers today are the unit tests, so the contract is free to change.
A file that exists but holds no readable parameters is user input rather
than a logic error, and now fails the check instead of propagating the
error from openssl.
The bit-count pattern becomes a raw string, and the tests pass integer
keysizes and cover the new paths.
|
|
Whether an interface belongs to a VRF was only established when a VRF was
named explicitly. A lookup for the default VRF - what most callers ask for -
thus reported an address living in an arbitrary VRF, or on a VRF device
itself, as locally assigned, and accepted a configuration the daemon cannot
bind later on. Naming the VRF device remains the way to ask for the
addresses it carries.
Probing the next address family after a failed bind was unreachable, as the
argument is validated as a literal beforehand and then resolves to exactly
one family. The early return is restored.
An interface address carries host bits and must stay valid, but the range
variants classify untrusted input handed over to nftables, which only
accepts a prefix in canonical form. Those reject host bits again.
All of it is covered by tests.
|
|
The list of listening sockets is a snapshot, and the owning process was
looked up afterwards. A process exiting in between, or one belonging to
another user when we do not run privileged, raised an exception that
escaped and aborted the conf-mode script asking whether a port is taken.
Neither case can be the service being looked for, so such a socket is
skipped now.
|
|
Whether an address may be bound is not the same question as whether it is
currently configured somewhere: the kernel permits binding a foreign address
once the non-local bind sysctl is set, which is how a failover address is
commonly handed over after the commit. Callers can now opt into that, the
default is unchanged.
A lookup no longer skips the VRF device itself, which carries the addresses
of its own L3 domain, and an interface name can be retrieved for an address
without the caller asking a yes/no question in the first place.
Address and range validation is split into separate checks, so a caller no
longer has to accept ranges where only an address is meaningful. The two
duplicated version checks are dropped in favour of the existing ones.
Co-authored-by: Christian Breunig <christian@breunig.cc>
|
|
Committing "service pppoe-server authentication any-login" failed with
"Authentication mode local requires local users to be configured!" even
though any-login accepts any username/password combination, making local
user accounts pointless.
verify_accel_ppp_authentication() raised unconditionally whenever
authentication mode was "local" - the default - without ever consulting
the any-login option. Skip that requirement when any-login is set, while
keeping the password and rate-limit validation for users that are
configured.
|
|
"connect interface" and "disconnect interface" cannot be combined to restart a
dialer interface when the CLI session runs over that very interface, yet the
command which does cover it was filed under an unrelated top-level node and
was hard to find.
It is now "reconnect interface <ifname>", mirroring the existing connect and
disconnect commands. "reset connection" is removed, no alias is kept.
The removed command was reachable through the REST API as a "reset" path. A
dedicated "/reconnect" endpoint takes its place, so the operation stays
available to API consumers.
|
|
vpp: T9146: Enable DPDK no-multi-seg when a full frame fits the buffer
|
|
ppp: T9060: do not derive a link-local address from a synthetic MAC
|
|
A modem is on the bus while still probing, so a dial could start too early and
leave the interface without a session until something re-dialled it.
|
|
A PPP link has no hardware address. RFC 5072 defines the IPv6 interface
identifier of such a link as the one negotiated via IPV6CP, and that
negotiation is also what guarantees its uniqueness - which is why duplicate
address detection is skipped for the link-local address on PPP links.
VyOS additionally derived an EUI-64 link-local address from a synthetic MAC
address and assigned it to PPPoE and SSTP client interfaces. The peer knows
nothing about that address and its uniqueness was never verified. With two
link-local addresses present, RFC 6724 source address selection falls through
to rule 8 and may pick the synthetic one, so DHCPv6 prefix delegation and
router solicitations leave from an address the BRAS never negotiated. ISPs
validating the source address silently drop them and delegation stalls.
Both interface types now carry only the address negotiated via IPV6CP. A
leftover address on an established session is removed on commit.
|
|
vxlan: T9319: add VXLAN-GBP support
|
|
Two things kept an operator disconnect from taking effect.
vyos-netlinkd re-dials every configured WWAN interface that has lost its
session. It cannot tell a session lost to RF signal loss from one that was
taken down on purpose, so the disconnect was undone on the next reconcile
pass, at most 30 seconds later:
$ disconnect interface wwan0
08:53:33 t+10s state=registered
08:53:43 t+20s state=registered
08:53:53 t+30s state=connected <- re-dialled
08:54:03 t+40s state=connected
vyos-netlinkd: WWAN interface wwan0 is not connected, re-dialling...
vyos-netlinkd: Re-dialled WWAN interface wwan0
Record the intent instead of inferring it from the session state. op-mode
"disconnect" drops a marker in /run/vyos-wwan, which _reconcile_wwan() honours
and "connect" removes again. conf_mode removes it too, so a commit keeps
re-asserting the configured state - the marker only survives for as long as
nobody says otherwise, and never across a reboot.
|
|
/etc/cron.d/vyos-wwan re-ran the conf_mode script every 5 minutes via
op-mode "connect interface wwanN" to re-establish a session lost to RF
signal loss. Re-entering conf_mode from cron is unsafe: the cron job knows
nothing about the boot commit or about a commit in flight, so when a tick
lands before the running config is populated, get_config() returns nothing
but {'ifname', 'deleted'} and apply() takes the teardown branch instead of
dialling. It then stops the DHCP clients, stops ModemManager and unlinks its
own cron file, leaving wwan0 down with no way to recover until the next
commit:
CRON[1869]: (root) CMD (/usr/libexec/vyos/vyos-check-wwan.py)
systemd[1]: Stopping DHCP client on wwan0...
systemd[1]: Stopping Modem Manager...
This is timing dependent - it only bites when a */5 tick coincides with
boot - which is why it presents as an intermittently dead WWAN interface.
Move the re-dial into vyos-netlinkd, which already tracks these interfaces
for DHCP and already gates all of its work behind boot_configuration_complete()
and commit_in_progress2(). A re-dial can therefore no longer land on a
half-applied configuration. It also dials ModemManager directly rather than
re-entering conf_mode, so the teardown branch is no longer reachable from
the recovery path at all.
|
|
|
|
|
|
|
|
Expose the Group Policy extension as a valueless gbp CLI node and pass
it to iproute2 when creating VXLAN links. Recreate links when GBP
changes and reject configurations combining GBP with GPE.
Reject conflicting GBP settings on a shared UDP port, accounting for
address families and underlay VRF socket bindings. Warn that external
mode carries GBP in tunnel metadata instead of the packet mark.
Add smoketest coverage for GBP creation, removal and re-enabling,
retained interface settings, rejection without changing the running
tunnel, shared ports, separate address families, and external mode.
|
|
configtree: T9307: serialize libvyosconfig calls across threads
|
|
test_dhcp_vrf and test_dhcpv6_vrf resolved the DHCP client's PID, then read
the VRF's process list and asserted the PID appeared in it. The client
re-executes itself at moments of its own choosing, so between those two
steps it can come back with a new PID - the process is in the VRF, it simply
is not the process we asked about:
AssertionError: '13502' not found in '14322 dhclient\n
14324 dhclient-script\n
14485 dhclient...'
Observed failing and passing on the same image, so it costs a rerun rather
than telling anyone anything.
get_process_cmdline() already fights the same race for the /proc read.
Resolve the PID and read the VRF's list as one step and retry, so a restart
costs a retry instead of a failure.
The probe itself was also loose: substring-matching the raw output of
"ip vrf pids <name>" lets a PID check be satisfied by the process name
column or by a longer PID containing ours. Add
vyos.utils.network.get_vrf_pids(), which parses the command's
"<pid> <name>" lines into (pid, process_name) tuples and returns an empty
list for an unknown VRF, and compare the PID field as an int.
|