<feed xmlns='http://www.w3.org/2005/Atom'>
<title>procd/jail/uxc-net, branch master</title>
<subtitle>OpenWrt service / process manager</subtitle>
<id>https://git.openwrt.org/project/procd/atom?h=master</id>
<link rel='self' href='https://git.openwrt.org/project/procd/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/'/>
<updated>2026-10-18T11:31:24Z</updated>
<entry>
<title>uxc-net: join a bridge through the device bridge options</title>
<updated>2026-10-18T11:31:24Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-10-15T17:04:01Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=0c4f3c6b32d61db85169f9be317502565053cfe8'/>
<id>urn:sha1:0c4f3c6b32d61db85169f9be317502565053cfe8</id>
<content type='text'>
netifd adds a device to a bridge it does not own through the bridge and
bridge_vlan options of the device itself, read from procd device data.
uxc-net publishes the host end of a bridged container's veth as a
separate bridge-port entry, which netifd does not read, so the veth
never joins the bridge of the attach network.

Set bridge and bridge_vlan on the published veth device and drop the
bridge-port data. bridge_vlan follows netifd's hotplug vlan syntax: the
attach network's own VLAN becomes "&lt;vid&gt;:u", the untagged PVID member,
and each org.openwrt.network.vlans entry "&lt;vid&gt;:t" becomes "&lt;vid&gt;", a
tagged member. An untagged entry is refused, as the syntax ties untagged
to the PVID and a port has one PVID.

Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
<entry>
<title>uxc-net: take ingress entries in the podman port order</title>
<updated>2026-10-18T11:31:24Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-10-15T16:07:36Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=62a6dd9af45793966e398f86ebe6e2ebbbac3e2e'/>
<id>urn:sha1:62a6dd9af45793966e398f86ebe6e2ebbbac3e2e</id>
<content type='text'>
An ingress entry names one port, which is both the external port and
the port inside the container, so a container service cannot be
published on a different external port.

Accept &lt;zone&gt;:&lt;ext&gt;[:&lt;int&gt;][/&lt;proto&gt;], the order podman -p and compose
use, with tcp as the default protocol, and set the redirect's dest_port
to &lt;int&gt;. A port range is only accepted untranslated, as the DNAT
keeps an external port only when it lies in the target range and does
not shift a range. The &lt;zone&gt;:&lt;proto&gt;/&lt;port&gt; form sealed into existing
images keeps its meaning.

Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
<entry>
<title>uxc-net: publish the applied network summary</title>
<updated>2026-10-18T11:25:50Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-10-09T00:56:18Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=f91f91f019ba618406c8aba009d85a94aca89231'/>
<id>urn:sha1:f91f91f019ba618406c8aba009d85a94aca89231</id>
<content type='text'>
The host side of a container's network is published as five typed
service data keys that netifd, fw4 and dnsmasq consume, so a reader
that wants the attach mode, the zone, the veth pair or the forwardings
in effect has to re-parse config.json, the annotation sidecar and the
published objects to reconstruct them. Add a sixth key,
container-network, to the one set_data call in instance_publish(): it
carries attach, network, the egress, ingress and host token lists,
proto, proto6, the backhaul segment, the derived interface, zone and
device names and the names of the redirects created from the ingress
list. routed_up(), bridged_up() and backhaul_up() fill it from the
parsed annotations and their derived names; bridged reports empty
egress, ingress and host lists because those annotations are ignored
there. The key has to travel in the same publish because set_data
replaces the whole instance blob and a second call would withdraw the
other five. Containers with attach none or host publish nothing.

Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
<entry>
<title>uxc-net: store MAC addresses in the directory from ujail</title>
<updated>2026-10-18T11:25:50Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-10-18T10:22:41Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=78f0adae3bffab44960c4e9fe0f61ff7c50f9438'/>
<id>urn:sha1:78f0adae3bffab44960c4e9fe0f61ff7c50f9438</id>
<content type='text'>
uxc-net stores the MAC addresses it generates for a container under a
fixed path on the uvol metadata volume, so a container registered
under /etc/uxc keeps them on a volume with an unrelated lifecycle, and
loses them on every start when uvol is not installed. Take the
persistent directory of the container from the fourth argument, which
ujail passes from its -K option, and keep the addresses there. Without
that argument the addresses are generated for the run only.

Fixes: 794866611371 ("uxc-net: declarative container networking helper")
Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
<entry>
<title>uxc-net: attach containers to multiple bridge VLANs</title>
<updated>2026-09-02T09:07:05Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-09-02T08:41:17Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=6d44d6bb77e27ecbaa881976e2bb4d3cf8ccd253'/>
<id>urn:sha1:6d44d6bb77e27ecbaa881976e2bb4d3cf8ccd253</id>
<content type='text'>
A bridged container joined exactly one VLAN of a VLAN-filtering bridge:
bridge_of() turned the br-X.N device of the attach network into a single
"N:*" element on the bridge-port entry, so the host end of the veth was
an untagged access port and the container could reach no other VLAN of
that bridge.

Add the annotation org.openwrt.network.vlans, a comma-separated list of
&lt;vid&gt;[:t] entries that has a meaning only on top of bridged:&lt;net&gt; where
&lt;net&gt; sits on a VLAN-filtering bridge. The attach network's own VLAN
stays the untagged PVID member exactly as before; a bare &lt;vid&gt; adds the
port as a further untagged member and &lt;vid&gt;:t adds it as a tagged trunk
member. Without the annotation nothing changes, neither for a plain
bridge nor for a single VLAN.

On the host the bridge-port entry keeps its one "vlans" array, which now
carries every requested element, for example ["20:*","30:t","40:t"].
netifd merges each element into the ports list of the UCI bridge-vlan
section with that VID, so a single entry puts the veth into several
VLANs with the right flag on each. VIDs must lie within 1 to 4094,
identical entries are folded, and an entry is rejected when it is listed
both tagged and untagged, when it names the attach network's own VLAN,
or when the bridge has no such VLAN according to network.device status.
The last check matters because netifd merges only into bridge-vlan
sections that exist and a port of a VLAN-filtering bridge without any
membership forwards nothing, so accepting the VID would leave a dead
interface in the container. Networks created on demand are plain
bridges owned by uxc-net and refuse the annotation.

Inside the container a tagged VLAN is reachable only through an 802.1Q
sub-interface, so for every &lt;vid&gt;:t the in-jail network file gains a
device section of type 8021q named eth0.&lt;vid&gt; on top of eth0 and an
interface 'vlan&lt;vid&gt;' on that device. The same netifd runs inside the
jail and creates the device when the interface claims it, given the
8021q module on the host kernel. Untagged members need nothing inside:
their frames are the untagged traffic on eth0. The VLAN interfaces
default to proto none and switch to static when
org.openwrt.network.vlan.&lt;vid&gt;.address supplies an address/prefix. A
container on a trunk typically uses its extra VLANs for plain L2 or for
a service with a fixed address; a DHCP client on every VLAN is unusual
and would compete for the default route that eth0 already provides.

Teardown is unchanged: withdrawing the instance data removes the
bridge-port entry with all of its memberships on the next reload, and
the in-jail file is per container.

Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
<entry>
<title>uxc-net: publish host network as procd service data instead of driving netifd</title>
<updated>2026-09-01T23:28:42Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-09-01T23:11:29Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=8b66962cbf3ed41553e0c7f6bbffa83448bdeb2d'/>
<id>urn:sha1:8b66962cbf3ed41553e0c7f6bbffa83448bdeb2d</id>
<content type='text'>
Describe the host side of a container's network as procd service data
and let netifd materialise it on config load, rather than creating the
objects one by one through the runtime ubus API (network create_device,
network add_dynamic, network.interface.X add_device/remove_device/remove).
Only the read-only queries (interface status, device status, interface
dump, service get_data) remain.

Everything a single container needs is attached to its own instance in
one set_data call: the veth pair as a network-device entry, the jailed
end (and the routed /31 gateway) as network-interface entries, the port
membership in a user-owned UCI bridge as a bridge-port entry, plus the
firewall and dhcp sections that were already published this way. The
blob is assembled by a single function that always emits all five type
keys, because set_data replaces the whole instance blob and a partial
publish would silently withdraw the container's link. The lifecycle of
these objects is therefore bound to the instance record: withdrawing the
data on down, or deleting the instance, removes them on the next reload
without any explicit teardown call.

Bridges that uxc-net owns (on-demand br-&lt;net&gt; autonets and bhr-&lt;seg&gt;
backhaul segments) are shared between containers and live at service
level on the uxc-net data service, since netifd replaces a bridge's
ports list on every publish and exactly one publisher must hold the
full union. The ports list of each owned bridge is the reference count:
on every up and down the list is pruned to the veth names currently
published by some instance, an owned bridge whose ports run empty is
dropped together with its interface, zone and dhcp entries, and the
service is deleted once nothing is left. The read-modify-write of the
shared data, and the instance publish that follows it, are serialised
with an exclusive flock so that concurrent container starts cannot lose
an update or prune each other's pending port. Subnet selection for a new
autonet also takes the published interfaces into account, not only
netifd's runtime state.

netifd re-reads procd data only during config load, so after each publish
or withdrawal a synchronous network reload is issued before the firewall
and dhcp config.change events. On up a failed reload is fatal, because
the jailed interface and its veth must exist by the time ujail hands the
network namespace over with netns_updown.

The veth blob is byte-stable across republishes: MAC addresses come from
the persisted store and are never regenerated, so a reload never
restarts the pair and never destroys the container's eth0. The in-jail
UCI rendering, annotation parsing and IPv6 handling are unchanged. The
downstream-only persistent attribute is gone from all published
interfaces.

Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
<entry>
<title>uxc-net: publish the host configuration instead of writing UCI</title>
<updated>2026-08-22T22:55:02Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-08-22T22:55:02Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=5f3bd0b66867afc9d7ff8bd9c11721f63a0be131'/>
<id>urn:sha1:5f3bd0b66867afc9d7ff8bd9c11721f63a0be131</id>
<content type='text'>
Every container brought a bridge, an interface, a firewall zone with its
forwardings and rules, and a DHCP section into /etc/config, committed at
ten sites across three packages. That is a flash write to the overlay on
every start and stop, and whatever a power cut interrupts stays behind,
describing a container that no longer exists. Stopping a container also
left the deletions staged in /tmp/.uci rather than applied, so the next
"Save &amp; Apply" decided what the host config would be.

Publish it as procd data instead. A container's zone, forwardings,
redirects, rules and DHCP section go into its own instance data, which
procd drops when the instance goes away, so the host configuration
cannot outlive what it describes. fw4 already folds data of type
"firewall" into its ruleset and odhcpd now does the same for "dhcp", and
both are told to reload with the config.change event they already
trigger on, because attaching data to an instance emits no event of its
own.

Networks created on demand for bridged containers are shared, so they
cannot hang off one container's instance. They live in the data of a
service of their own, and the published set is the state: the list is
read back to add or drop a member, which is why no state file is needed
and why nothing is lost when the volume holding it is not mounted.

The zone a container gets is closed, so the services the host offers
into it are opened explicitly, the way /etc/config/firewall opens them
for wan rather than fw4 inferring anything: DNS always, ICMPv6 whenever
the container has any IPv6 at all because neighbour discovery is ICMPv6
over IP and a static-IPv6 container behind a closed zone cannot resolve
its gateway, and DHCPv6 only when the container asks for it.

Creating the interface no longer waits for a reload to bring it up, so
the thirty second wait for the interface object to appear is gone.

Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
<entry>
<title>uxc-net: remove the host configuration it creates</title>
<updated>2026-08-21T17:56:17Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-08-21T17:53:37Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=66781b2b3544ba552ed31b61897ec6471c4a52e4'/>
<id>urn:sha1:66781b2b3544ba552ed31b61897ec6471c4a52e4</id>
<content type='text'>
ensure_network() commits persistent sections to network, dhcp and
firewall when a bridged attachment names a network that does not exist,
and bridged_down() never removed them, so a single container run left a
network, a DHCP pool and a firewall zone in /etc/config forever.

Removing the sections on bring-down is the right resolution rather than
not committing them in the first place, because the DHCP pool can only
exist as persistent configuration: dnsmasq reads UCI alone and offers no
ubus path to hand it a pool, so an ephemerally created network would be
one without addressing. The per-container firewall sections already
follow this lifecycle for the same reason, stock fw4 ignoring zone data
delivered over ubus: create at start, idempotent, delete on teardown.

Record each auto-created network in a marker under the uxc state
directory and name the created sections deterministically, the device
section as &lt;net&gt;_dev and the firewall zone section as &lt;net&gt;. On
bring-down of a bridged container the network is removed again when the
marker exists and its bridge has no members left, deleting exactly the
four sections creation wrote, and the marker with them. A network the
operator defined is never touched, because it has no marker, and a
network still carrying other containers keeps its configuration until
the last member goes down. The marker lives on the uvol metadata volume,
so a firmware re-image that wipes /etc/config but keeps the container
registrations simply leads to the network being recreated on the next
start.

A marker can go stale if an auto-created network is never brought down
and the operator later defines the same network by hand; the next
last-member bring-down would then remove the hand-written sections. The
window is narrow and the alternative is refcounting operator intent,
which UCI cannot express.

With this, bring-down restores /etc/config for bridged containers as
well; while a container is up, its container-lifetime sections exist,
exactly as the routed firewall and router advertisement sections do.

Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
<entry>
<title>uxc-net: make routed containers a delegated-prefix downstream</title>
<updated>2026-08-21T17:56:17Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-08-21T17:51:47Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=85dce23e684aef2abbfb6e48b02613ab39258471'/>
<id>urn:sha1:85dce23e684aef2abbfb6e48b02613ab39258471</id>
<content type='text'>
A routed container had no IPv6 path at all: the host gateway interface
carried only the /31 IPv4 address, and nothing on the host answered
router solicitations on the link, so a container asking for proto6 could
never configure itself.

Give the gateway interface created through network.add_dynamic an
ip6assign of 64 and ip6ifaceid '::1'. netifd hands the whole blob to
interface_alloc(), which parses every interface attribute, so netifd
carves a /64 for the link out of the upstream delegated prefix and the
host end takes the deterministic ::1 address within it; no netifd change
is needed.

Prefix assignment alone does not make the link work: router
advertisements must actually be emitted on the gateway interface for the
container to learn the prefix and its default route. That takes an
odhcpd section, which is new host-side state: bring-up commits a
per-container section named after the gateway interface to the
persistent dhcp configuration, with ra and dhcpv6 in server mode, and
reloads it through the config.change service event. Bring-down removes
exactly that section again, following the lifecycle the per-container
firewall sections already use. dnsmasq ignores the section because its
dhcpv4 option defaults to disabled.

fw_reload() becomes pkg_reload() so the firewall and dhcp reload paths
share one helper.

Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
<entry>
<title>uxc-net: add IPv6 addressing and a deterministic interface identifier</title>
<updated>2026-08-21T17:56:17Z</updated>
<author>
<name>Daniel Golle</name>
</author>
<published>2026-08-21T17:50:01Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/procd/commit/?id=ef6d6e62c946efc9497bf886d3c5f8d74e85ac85'/>
<id>urn:sha1:ef6d6e62c946efc9497bf886d3c5f8d74e85ac85</id>
<content type='text'>
The in-jail interface was IPv4 only; nothing in the contract could ask
for an IPv6 address.

Add org.openwrt.network.proto6, accepting 'dhcpv6', 'slaac', 'static'
and 'none'. Where IPv6 is requested a second in-jail interface section
is rendered on the moved device: 'dhcpv6' runs the full odhcp6c cycle,
'slaac' renders proto 'dhcpv6' with reqaddress 'none' and reqprefix 'no'
so odhcp6c only processes router advertisements, and 'static' takes
org.openwrt.network.address6 in address/prefix notation with an optional
org.openwrt.network.gateway6.

Add org.openwrt.network.ip6ifaceid, an address with a zero network part
such as '::1234', rendered as option ip6ifaceid on the dhcpv6 section;
/lib/netifd/proto/dhcpv6.sh hands it to odhcp6c through -i, giving the
container a deterministic interface identifier within whatever prefix
the link advertises.

The default is 'none': no IPv6 section is rendered, preserving the
existing behaviour for containers that do not ask for IPv6.

Signed-off-by: Daniel Golle &lt;daniel@makrotopia.org&gt;
</content>
</entry>
</feed>
