Last active
January 22, 2026 16:45
-
-
Save Palatis/d8947cb416be61355cfcc4981a4b0ad8 to your computer and use it in GitHub Desktop.
OpenWrt podman use br-lan
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [engine] | |
| hooks_dir = [ | |
| "/etc/containers/oci/hooks.d/", | |
| ] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "version": "1.0.0", | |
| "hook": { | |
| "path": "/root/bin/podman-lan-bridge-hook.sh" | |
| }, | |
| "when": { | |
| "annotations": { | |
| "io.podman.annotations.lan-bridge": "true" | |
| } | |
| }, | |
| "stages": [ "prestart", "poststart", "poststop" ] | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| LOG="/dev/null" | |
| #LOG="/tmp/podman-hook.log" | |
| PIDFILE_DIR="/var/run/podman/dhcp" | |
| echo "=== Hook called at $(date) ===" >> "$LOG" | |
| # Read all input | |
| input=$(cat) | |
| echo "Stdin: $input" >> "$LOG" | |
| CONTAINER_ID=$(echo "$input" | sed -n 's/.*"id":"\([^"]*\)".*/\1/p') | |
| CONTAINER_PID=$(echo "$input" | sed -n 's/.*"pid":\([0-9]*\).*/\1/p') | |
| STATUS=$(echo "$input" | sed -n 's/.*"status":"\([^"]*\)".*/\1/p') | |
| echo "Container ID: $CONTAINER_ID" >> "$LOG" | |
| echo "Container PID: $CONTAINER_PID" >> "$LOG" | |
| echo "Status: $STATUS" >> "$LOG" | |
| DHCP4_PIDFILE="$PIDFILE_DIR/$CONTAINER_ID.udhcpc.pid" | |
| DHCP6_PIDFILE="$PIDFILE_DIR/$CONTAINER_ID.odhcp6c.pid" | |
| # Determine stage based on status | |
| if [ "$STATUS" = "created" ] && [ -n "$CONTAINER_PID" ]; then | |
| STAGE="prestart" | |
| elif [ "$STATUS" = "running" ]; then | |
| STAGE="poststart" | |
| elif [ "$STATUS" = "stopped" ]; then | |
| STAGE="poststop" | |
| else | |
| echo "Unknown stage, exiting" >> "$LOG" | |
| exit 0 | |
| fi | |
| echo "Stage: $STAGE" >> "$LOG" | |
| case "$STAGE" in | |
| prestart) | |
| mkdir -p $PIDFILE_DIR | |
| # IPv6 | |
| nsenter -t "$CONTAINER_PID" -a sysctl -w net.ipv6.conf.all.accept_ra=2 >> "$LOG" 2>&1 | |
| nsenter -t "$CONTAINER_PID" -a sysctl -w net.ipv6.conf.default.accept_ra=2 >> "$LOG" 2>&1 | |
| nsenter -t "$CONTAINER_PID" -a sysctl -w net.ipv6.conf.eth0.accept_ra=2 >> "$LOG" 2>&1 | |
| nsenter -t "$CONTAINER_PID" -a sysctl -w net.ipv6.conf.eth0.autoconf=1 >> "$LOG" 2>&1 | |
| ;; | |
| poststart) | |
| # Hostname | |
| CONTAINER_HOSTNAME=$(nsenter -t $CONTAINER_PID -u cat /proc/sys/kernel/hostname) | |
| [ -z "$CONTAINER_HOSTNAME" ] && CONTAINER_HOSTNAME="ctr-${CONTAINER_ID:0:10}" | |
| echo "Container Hostname: $CONTAINER_HOSTNAME" >> "$LOG" | |
| # IPv4 | |
| nsenter -t $CONTAINER_PID -m umount /etc/resolv.conf >> "$LOG" | |
| nsenter -t $CONTAINER_PID -a udhcpc -f -R -S -i eth0 -x hostname:$CONTAINER_HOSTNAME 2>&1 >> "$LOG" & | |
| UDHCPC_PID=$! | |
| echo "$UDHCPC_PID" > "$DHCP4_PIDFILE" | |
| echo "udhcpc pid: ${UDHCPC_PID}" >> "$LOG" | |
| # IPv6 | |
| DUID="00048e3c${CONTAINER_ID:0:26}" | |
| nsenter -t "$CONTAINER_PID" -n -u odhcp6c -s /root/bin/dhcpv6.simple.script -c "$DUID" -t120 eth0 2>&1 >> "$LOG" & | |
| ODHCP6C_PID=$! | |
| echo "$ODHCP6C_PID" > "$DHCP6_PIDFILE" | |
| echo "odhcp6c pid: ${ODHCP6C_PID}" >> "$LOG" | |
| ;; | |
| poststop) | |
| echo "Stopping DHCP client..." >> "$LOG" | |
| # kill IPv4 udhcpc | |
| [ -f "$DHCP4_PIDFILE" ] && kill $(cat "$DHCP4_PIDFILE") 2>/dev/null | |
| rm -f "$DHCP4_PIDFILE" | |
| # kill IPv6 odhcpc6 | |
| [ -f "$DHCP6_PIDFILE" ] && kill $(cat "$DHCP6_PIDFILE") 2>/dev/null | |
| rm -f "$DHCP6_PIDFILE" | |
| echo "Cleanup complete" >> "$LOG" | |
| ;; | |
| esac | |
| echo "=== Hook finished ===" >> "$LOG" | |
| exit 0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/sh | |
| # Minimal odhcp6c script for container (IPv6 only) | |
| # Works with RA_ADDRESSES / ADDRESSES | |
| IF="$1" | |
| ACTION="$2" | |
| case "$ACTION" in | |
| ra-updated|add|bound) | |
| # assign addresses from ADDRESSES | |
| for addr in $ADDRESSES; do | |
| ip_addr=$(echo "$addr" | cut -d',' -f1) | |
| [ -n "$ip_addr" ] && ip -6 addr add "$ip_addr" dev "$IF" | |
| done | |
| # set default routes from RA_ROUTES | |
| for route in $RA_ROUTES; do | |
| dst=$(echo "$route" | cut -d',' -f1) | |
| gw=$(echo "$route" | cut -d',' -f2) | |
| [ -n "$dst" ] && [ -n "$gw" ] && ip -6 route add "$dst" via "$gw" dev "$IF" || true | |
| done | |
| # update resolv.conf from RDNSS | |
| if [ -n "$RDNSS" ]; then | |
| echo > /etc/resolv.conf | |
| for dns in $RDNSS; do | |
| echo "nameserver $dns" >> /etc/resolv.conf | |
| done | |
| fi | |
| ;; | |
| del|debound) | |
| # remove assigned addresses | |
| for addr in $ADDRESSES; do | |
| ip_addr=$(echo "$addr" | cut -d',' -f1) | |
| [ -n "$ip_addr" ] && ip -6 addr del "$ip_addr" dev "$IF" | |
| done | |
| ;; | |
| esac | |
| exit 0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # podman network create --disable-dns -d bridge --ipv6 --interface-name br-lan --help --ipam-driver dhcp br-lan | |
| # podman inspect br-lan | |
| [ | |
| { | |
| "name": "br-lan", | |
| "id": "...", | |
| "driver": "bridge", | |
| "network_interface": "br-lan", | |
| "created": "2026-01-20T20:37:44.707081309+08:00", | |
| "ipv6_enabled": true, | |
| "internal": false, | |
| "dns_enabled": false, | |
| "ipam_options": { | |
| "driver": "dhcp" | |
| } | |
| } | |
| ] | |
| # podman run -d --name samba --network=br-lan \ | |
| --annotation io.podman.annotations.lan-bridge=true \ | |
| -h samba \ | |
| --mac-address "11:22:33:44:55:66" \ | |
| ghcr.io/servercontainers/samba:smbd-wsdd2-latest |
Author
Author
Issues
- i was going to use
podman inspectto obtain the hostname, as it might be more reliable. however can't use that in a hook script because inside hooks the container is in a limbo state andpodman inspectjust hang forever (kill -9to stop it). - hostname can only be obtained during
poststartphase. it's not available duringprestart, the command just retrieve router's hostname instead container's hostname duringprestart. - cannot just
podman exec $CONTAINER_ID udhcpc, container don't have the permission to modify network settings. - was going to use
loggerso things logs into syslog, but dunno why a simplelogger hellodoesn't write anything tologread.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Environment
OpenWrt (25.10) + podman (5.2.2)
podman network stack:
netavarkProblem
i want to run a samba / syncthing container on my OpenWrt router, as if they're a physical machine plugged into the LAN port.
the container should try to obtain it's ip address from the dhcp server running on the router (
dnsmasq & odhcpd).the ip addresses are managed with dhcp/dns server running on the router, and netwokr access is protected by router's firewall rules.
Solution
vethmac is randomly generated during device creation, so use container id for dhcp0x3d(device id) field, so the server assigns the same ip to the container, instead of a different ip everytime.podman --mac-address "11:22:33:44:55:66"00048e3c), since we're in a linux container.Usage
/etc/containers/containers.conf, add the hooks./etc/containers/oci/hooks.d/lan-bridge.json)dhcpv6.simple.scriptsomewhere the hook script can find (see line#63)podman network createcommand to create a networkpodman inspect <network_name>to check the configurationpodman runcommand to create a containerio.podman.annotations.lan-bridge=trueso the container will use the hookFAQ
macvlanin bridge mode cannot talk to the host.macvlandevice with a parent which is already a bridge (br-lan)macvlanneeds a physical port with carrier, which might not present.TODO
DNS:udhcpcran withnsentercannot replace/etc/resolv.confinside a container, dunno how.umountit first.IPv6:udhcpcshould obtain a v6 address, however it's not, dunno why.odhcp6cfor the task, andsysctlparameters needed to enable autoconf.netavark dhcp-proxycan do the task properly then when don't need this hook.