apple / apple/containerization

[Bug]: LinuxContainer installs a link-scope default route when ipv4Gateway is nil

Open
#825 1 comment 0 reactions 1 assignee Claimed by @jglogan View on GitHub
Dominant language
Swift
Stars
8.9k
Forks
359
Avg merge
2d 20h
Merged PRs (30d)
13

Description

### I have done the following

- [x] I have searched the existing issues
- [x] I reproduced the issue with Containerization 0.33.1 and confirmed the same logic remains on `main`

### Steps to reproduce

1. Create a custom type conforming to `Containerization.Interface`.
2. Supply an IPv4 address and return `nil` from `ipv4Gateway`:

```swift
struct GatewaylessInterface: Interface, VZInterface {
let base: any Interface & VZInterface
var ipv4Address: CIDRv4 { base.ipv4Address }
var ipv4Gateway: IPv4Address? { nil }
var macAddress: MACAddress? { base.macAddress }
var mtu: UInt32 { base.mtu }
func device() throws -> VZVirtioNetworkDeviceConfiguration { try base.device() }
}
```

3. Start a `LinuxContainer` whose first and only interface is this gatewayless interface.
4. Inside the guest, run:

```sh
ip -4 route show
ip route get 1.1.1.1
```

The issue is deterministic in a real Virtualization.framework Linux microVM.

The 0.33.1 public contract documents `ipv4Gateway` as “The IP address for the default route, or nil for no default route”:
https://github.com/apple/containerization/blob/0.33.1/Sources/Containerization/Interface.swift#L25-L26

However, `LinuxContainer` calls `routeAddDefault(name:ipv4Gateway:)` with `nil` for the first interface:
https://github.com/apple/containerization/blob/0.33.1/Sources/Containerization/LinuxContainer.swift#L631-L654

Current `main` retains the same semantic behavior through `setupInterface`: when both gateways are nil it logs “no gateway” and still calls `routeAddDefault`:
https://github.com/apple/containerization/blob/main/Sources/Containerization/VirtualMachineAgent%2BInterface.swift#L76-L82

### Current behavior

The guest receives an on-link default route even though `ipv4Gateway` is nil:

```text
default dev eth0 scope link
172.16.0.0/24 dev eth0 scope link src 172.16.0.3
```

Consequently, route lookup treats arbitrary external destinations as directly reachable on `eth0` and attempts neighbor resolution on that link.

This does not necessarily provide external connectivity when the backing attachment is private and has no uplink, but it contradicts the public `Interface` contract and prevents callers from creating a first interface with no default route.

### Expected behavior

When `ipv4Gateway` is nil, `LinuxContainer` should configure the IPv4 address and link route without installing an IPv4 default route. IPv4 and IPv6 default-route installation should follow their respective optional gateway values.

### Environment

- OS: macOS 26.5.2 (25F84)
- Xcode: 26.6 (17F113)
- Swift: Apple Swift 6.3.1
- Containerization: 0.33.1
- Architecture: Apple silicon / arm64

### Relevant log output

```text
default dev eth0 scope link
172.16.0.0/24 dev eth0 scope link src 172.16.0.3
```

### Code of Conduct

- [x] I agree to follow this projects Code of Conduct

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.