Skip to content

Jail/bridge fixes and port-side bridge membership on devices - #89

Open
dangowrt wants to merge 4 commits into
openwrt:masterfrom
dangowrt:netifd-procd-data
Open

dangowrt wants to merge 4 commits into
openwrt:masterfrom
dangowrt:netifd-procd-data

Conversation

@dangowrt

@dangowrt dangowrt commented Sep 2, 2026

Copy link
Copy Markdown
Member

Fix and improve netifd for use with procd/ujail managed containers:

  • bridge: do not free uninitialised vlan_aliases kvlist (fixes 4544f02). A bridge created and freed over ubus without a config load leaves its vlan_aliases kvlist uninitialised, so bridge_free() dereferences it.
  • system-linux: refuse netns move of an unresolved device (fixes d93126d). An unresolved source device sent RTM_NEWLINK with ifi_index = 0, so the kernel matched by name and could move the host's real eth0 into a container.
  • interface: defer jailed device move until the device exists (fixes 1321c1b). A jailed interface whose device did not exist yet was moved as a no-op, leaving the container with no network device; keep the netns and finish the move once the device appears.
  • config: support port-side bridge membership on devices. New option bridge and list bridge_vlan on config device, parsed identically from /etc/config/network and from a procd network-device entry, so a container manager can attach a port to a bridge it does not own without replacing it. Keeping the UCI and procd structures identical is the point of the design.

bridge names the bridge to join; each bridge_vlan names a VLAN on it in the same <vid>[:t][:*] syntax as bridge-vlan ports. The port is folded into the bridge's members from whichever source the bridge is defined (UCI device, legacy option type bridge, or procd bridge) and dropped on the first reload after the declaration goes away; option bridge alone adds a plain member.

Enabling vlan_filtering makes bridge_enable_member() drop the default VLAN from every member and keep only assigned ones, so a port-side bridge_vlan on a bridge that has no bridge-vlan section of its own but already has plain members is refused rather than silently cutting those members off. procd's ujail/uxc-net is the consumer; netifd keeps no dependency on procd and ignores the options when unset.

The vlan_aliases kvlist is initialised lazily in device_vlan_update(),
which only runs during configuration load. A bridge that is created and
freed without going through a config load (e.g. one created at runtime
over ubus) reaches bridge_free() with the kvlist still zero-filled, and
kvlist_free() then walks an uninitialised avl_tree whose list head
contains NULL pointers, crashing netifd. Guard the call on get_len, the
same marker device_vlan_update() uses to tell an initialised kvlist
apart.

Fixes: 4544f02 ("bridge-vlan: add support for defining aliases for vlan ids")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
system_link_netns_move() built its RTM_NEWLINK message with whatever
system_if_resolve() returned for the source device. When the device does
not exist (e.g. a jailed interface whose veth has not been created yet),
system_if_resolve() returns 0, and the message goes out with
ifi_index = 0 while IFLA_IFNAME is set to target_ifname (the jail_device
name).

The kernel's __rtnl_newlink() then selects the target device by name when
ifi_index is 0 (net/core/rtnetlink.c: rtnl_dev_get -> __dev_get_by_name),
so the move operates on whatever host device happens to be named like the
jail_device. With a jail_device of "eth0" this moves the host's real eth0
into the container's network namespace, knocking the host off the
network.

Bail out when the source device cannot be resolved to a real ifindex so
the request is never sent with a zero index.

Fixes: d93126d ("interface: allow renaming interface when moving to jail netns")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
interface_start_jail() moved each jailed interface's main device into the
container netns at the moment the jail's netns was registered. If the
device did not exist yet (e.g. a veth whose creation had not been
triggered), the move was a no-op and the container came up with no
network device; the in-jail netifd then failed DHCP with
"udhcpc: SIOCGIFINDEX: No such device" until the operator manually
brought the host interface up.

Keep a duplicated reference to the jail netns on the interface when the
move cannot be performed yet, and complete it from interface_main_dev_cb
once the device appears (DEV_EVENT_ADD). Any stale pending reference from
an earlier jail incarnation is dropped on the next interface_start_jail()
invocation, so a successful immediate move can never leave a dangling
netns reference behind that would later divert the device into a dead
namespace. interface_stop_jail and interface_free drop any still-pending
reference as well.

Fixes: 1321c1b ("add basic support for jail network namespaces")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
@dangowrt dangowrt changed the title Jail/bridge fixes and a bridge-port procd data type for external bridge membership Jail/bridge fixes and port-side bridge membership on devices Sep 11, 2026
Bridge membership could only be declared from the bridge side, so a
device published as procd network-device data could not join a bridge
it does not own. Add bridge and bridge_vlan device attributes, stored
on struct device and parsed for every device through the existing
device_attr_list, and have bridge_config_init() add every device whose
bridge attribute names this bridge as a member. When the bridge is
vlan_filtering, each member's bridge_vlan entries ("<vid>[:t][:*]") are
registered as hotplug vlan ports. bridge_vlan is ignored on a bridge
that is not vlan_filtering, so a device attribute never turns a plain
bridge into a filtering one and existing members keep their default
vlan.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant