Skip to content

Commit 1ac13ed

Browse files
committed
container: T7736: give container veths a deterministic host_interface_name
Podman's default "vethN" auto-naming for a container's host-side veth can collide with VyOS's own "virtual-ethernet vethN" interfaces. Bump the minimum Podman dependency to 5.8 (which supports "host_interface_name" network connect option) and use it to name every non-macvlan container network attachment "veth-<container name>" instead, eliminating the collision by construction.
1 parent 3a1ce3a commit 1ac13ed

2 files changed

Lines changed: 32 additions & 5 deletions

File tree

debian/control

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ Depends:
332332
kbd,
333333
# End "system option keyboard-layout"
334334
# For "container"
335-
podman (>=4.9.5),
335+
podman (>=5.8),
336336
netavark,
337337
aardvark-dns,
338338
# iptables is only used for containers now, not the the firewall CLI

src/conf_mode/container.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,19 @@ def verify(container):
361361
return None
362362

363363

364-
def generate_run_arguments(name, container_config, host_ident):
364+
def get_container_host_ifname(name: str) -> str:
365+
"""
366+
Deterministic host-side veth interface name for a container's network
367+
attachment (verify() only allows one network per container). Kept
368+
within IFNAMSIZ (15 usable characters) and - thanks to the leading
369+
"veth-" (a hyphen can never appear in a VyOS "vethN" interface name) -
370+
guaranteed to never collide with the VyOS "virtual-ethernet" naming
371+
scheme - T7736.
372+
"""
373+
return f'veth-{name}'[:15]
374+
375+
376+
def generate_run_arguments(name, container_config, host_ident, network_config):
365377
image = container_config['image']
366378
cpu_quota = container_config['cpu_quota']
367379
memory = container_config['memory']
@@ -511,9 +523,21 @@ def generate_run_arguments(name, container_config, host_ident):
511523
else:
512524
ip_param = ''
513525
addr_info = ''
514-
networks = ",".join(container_config['network'])
526+
network_opts = []
515527
for network in container_config['network']:
516528
network_name = network
529+
# T7736: give the host-side veth a name that can never collide
530+
# with a VyOS "virtual-ethernet vethN" interface, instead of
531+
# leaving it to Podman's own "vethN" auto-naming. Not applicable
532+
# to macvlan networks - they attach without a paired host veth.
533+
type_config = dict_search(f'{network}.type', network_config)
534+
is_macvlan = dict_search('macvlan', type_config) is not None
535+
net_opt = network
536+
if not is_macvlan:
537+
ifname = get_container_host_ifname(name)
538+
net_opt += f':host_interface_name={ifname}'
539+
network_opts.append(net_opt)
540+
517541
if 'address' not in container_config['network'][network]:
518542
continue
519543
for address in container_config['network'][network]['address']:
@@ -524,6 +548,8 @@ def generate_run_arguments(name, container_config, host_ident):
524548

525549
addr_info = ''.join(container_config['network'][network]['address'])
526550

551+
networks = ' '.join(f'--network {opt}' for opt in network_opts)
552+
527553
get_mac = dict_search(f'network.{network_name}.mac', container_config)
528554
if get_mac == 'auto' or get_mac is None:
529555
mac_add = gen_mac(name, addr_info, host_ident)
@@ -546,7 +572,7 @@ def generate_run_arguments(name, container_config, host_ident):
546572
delete_cli_node(mac_config_path)
547573
add_cli_node(mac_config_path, value=mac_add)
548574

549-
net = f'--net {networks} {ip_param} {mac_address}'
575+
net = f'{networks} {ip_param} {mac_address}'
550576

551577
return f'{container_base_cmd} {healthcheck} {net} {entrypoint} {image} {command} {command_arguments}'.strip()
552578

@@ -626,12 +652,13 @@ def generate(container):
626652

627653
if 'name' in container:
628654
host_ident = get_host_identity()
655+
network_config = container.get('network', {})
629656
for name, container_config in container['name'].items():
630657
if 'disable' in container_config:
631658
continue
632659

633660
file_path = os.path.join(systemd_unit_path, f'vyos-container-{name}.service')
634-
run_args = generate_run_arguments(name, container_config, host_ident)
661+
run_args = generate_run_arguments(name, container_config, host_ident, network_config)
635662
render(file_path, 'container/systemd-unit.j2', {'name': name, 'run_args': run_args, },
636663
formatter=lambda _: _.replace("&quot;", '"').replace("&apos;", "'"))
637664

0 commit comments

Comments
 (0)