Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 101 additions & 1 deletion doc/DHCPv6_relay/DHCPv6-relay-agent-High-Level-Design.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
- [RADV modification](#radv-modification)
- [CoPP manager](#copp-manager)
- [Source IP](#source-ip)
- [Dynamic Configuration (no container restart)](#dynamic-configuration-no-container-restart)
- [VRF Support](#vrf-support)
* [Performance](#performance)
* [Testing](#testing)

Expand All @@ -41,7 +43,7 @@ DUID: DHCP Unique Identifier (Each DHCPv6 client and server has a DUID. DHCPv6 s

SONiC currently supports DHCPv4 Relay via the use of open source ISC DHCP package. However, DHCPv6 specification does not define a way to communicate client link-layer address to the DHCP server where DHCP server is not connected to the same network link as DHCP client. DHCPv6 requires all clients prepare and send a DUID as the client identifier in all DHCPv6 message exchanges. However, these methods do not provide a simple way to extract a client's link-layer address. Providing option 79 in DHCPv6 Relay-Forward messages will help carry the client link-layer address explicitly. The server needs to know the client's MAC address to allow DHCP Reservation, which provides pre-set IP address to specific client based on its physical MAC address. The DHCPv6 relay agent is able to read the source MAC address of DHCPv6 messages that it received from client, and encapsulate these messages within a DHCPv6 Relay-Forward message, inserting the client MAC address as option 79 in the Relay-Forward header sent to the server.

With heterogenous DHCP client implementation across the network, DUIDs could not resolve IP resource tracking issue. The two types of DUIDs, DUID-LL and DUID-LLT used to facilitate resource tracking both have link layer addresses embedded. The current client link-layer address option in DHCPv6 specification limits the DHCPv6 Relay to first hop to provide the client link layer address, which are relay agents that are connected to the same link as the client, and that limits SONiC DHCPv6 deployment to ToR/MoR switches for early stages. One solution would be to provide SONiC's own DHCPv6 relay agent feature. ISC DHCP currently has no support for option 79. Configuration wise, using ISC DHCP configuration requires restarting container as configuration is provided through the commandline. The plan is to eventually move away from ISC DHCP configuration, which is fairly complex, and provide SONiC's own configuration.
With heterogenous DHCP client implementation across the network, DUIDs could not resolve IP resource tracking issue. The two types of DUIDs, DUID-LL and DUID-LLT used to facilitate resource tracking both have link layer addresses embedded. The current client link-layer address option in DHCPv6 specification limits the DHCPv6 Relay to first hop to provide the client link layer address, which are relay agents that are connected to the same link as the client, and that limits SONiC DHCPv6 deployment to ToR/MoR switches for early stages. One solution would be to provide SONiC's own DHCPv6 relay agent feature. ISC DHCP currently has no support for option 79. Configuration wise, using ISC DHCP configuration requires restarting container as configuration is provided through the commandline. The plan is to eventually move away from ISC DHCP configuration, which is fairly complex, and provide SONiC's own configuration. As part of providing SONiC's own configuration, the DHCPv6 relay agent applies relay configuration changes at runtime without restarting the `dhcp_relay` container; see [Dynamic Configuration (no container restart)](#dynamic-configuration-no-container-restart).

# DHCPv6

Expand Down Expand Up @@ -220,6 +222,91 @@ VLAN SVI IP

Configurable option to use loopback address for dual ToR

# Dynamic Configuration (no container restart)

Historically the DHCPv6 relay agent read its configuration only once, at process start. The `dhcp6relay` process reads the `DHCP_RELAY` table during initialization, builds a per-VLAN `relay_config` map, and then enters the libevent packet-processing loop. Any subsequent change to relay configuration was not applied to the running process: the relay logged `relay config changed, need restart container to take effect`, and the operator had to restart the `dhcp_relay` container for the new configuration to take effect. Restarting the container is disruptive, as it tears down relay state for every VLAN and interrupts DHCPv6 service for all VLANs while the container restarts, even if only a single VLAN's configuration changed.

This section describes a Config Manager that allows the DHCPv6 relay agent to apply configuration changes at runtime, without restarting the container.

## Config Manager

A dedicated Config Manager thread subscribes to the relevant CONFIG_DB and STATE_DB tables and applies changes incrementally to the running relay. The following is a non-exhaustive list of the tables monitored by the Config Manager:

- **DHCP_RELAY table:** per-VLAN DHCPv6 relay configuration, i.e. the `dhcpv6_servers` list and the DHCPv6 relay options (`dhcpv6_option|rfc6939_support` for option 79 and `dhcpv6_option|interface_id` for the interface-id option).
- **VLAN_INTERFACE table:** presence of a global or site-scoped IPv6 address on the downstream VLAN interface. A relay instance for a VLAN is only meaningful once the VLAN interface has an IPv6 address configured, so the Config Manager uses this to decide when a VLAN's relay can be activated.
- **VLAN table:** VLAN creation and deletion, so relay instances are added or removed as VLANs are configured or unconfigured.
- **STATE_DB INTERFACE_TABLE:** interface readiness, i.e. the link-local address becoming available on a VLAN interface, so a VLAN's relay is reconciled as soon as its interface comes up instead of waiting for the periodic link-local readiness check.

On each notification, the Config Manager computes the new desired set of `relay_config` entries and synchronizes them with the relay main thread.

Each notification triggers a full recomputation of the desired `relay_config` set from the current CONFIG_DB and STATE_DB contents rather than the application of individual deltas, so reconciliation is idempotent: a burst of rapid configuration changes coalesces to the latest desired state, and a redundant notification results in at most a harmless redundant reconcile rather than an incorrect intermediate state. If a database read fails while the desired set is being recomputed (for example a transient CONFIG_DB access error), the Config Manager logs the failure, leaves the last successfully applied desired configuration in place, and retries on the next notification; a transient database error therefore neither disrupts relaying with the current configuration nor terminates the relay process.

## Applying changes at runtime

The relay main thread owns all sockets and libevent events. When configuration changes, the relay applies the minimal set of actions required, rather than restarting:

- **Server list or option change for an existing VLAN:** the in-memory `relay_config` for that VLAN is updated. Subsequent client messages are relayed using the updated server list and options; no socket changes are required.
- **VLAN added (with an IPv6 address):** the relay configuration is created and the associated libevent socket events for that VLAN are armed.
- **VLAN removed, its IPv6 address removed, or its relay config deleted:** the relay configuration is torn down and the associated libevent socket events for that VLAN are freed, leaving the relay for other VLANs untouched.

To keep database notifications and the libevent loop within a single event-driven model, the Config Manager wakes the main loop when new configuration is available (for example, through a self-pipe registered as a libevent event). The main loop then re-reads the synchronized configuration and reconciles its sockets and events. This avoids a blocking poll of the databases from the packet path.

The existing periodic link-local readiness check (the 60-second timer that detects when a VLAN interface's link-local address becomes ready) continues to operate and is reused to activate relays for VLANs whose IPv6 readiness changes after configuration is applied.

## Backward compatibility

Runtime reconfiguration is transparent to operators and requires no change to the CONFIG_DB schema. The previous requirement to restart the `dhcp_relay` container for relay configuration changes is removed, and the `need restart container to take effect` log is no longer emitted for supported configuration changes.

# VRF Support

By default the DHCPv6 relay agent forwards relay-forward messages to the DHCPv6 servers using the default (global) routing table. When the relay VLAN or its DHCPv6 servers are reachable only in a non-default VRF, the relay must send and receive the server-facing traffic in that VRF. The relay agent binds its upstream (server-facing) socket to the appropriate VRF using `SO_BINDTODEVICE`, so that relay-forward messages are routed through the VRF's routing table and the corresponding relay-reply messages are received from it. The downstream (client-facing) socket is unaffected; only the server-facing path is VRF aware.

Two deployment models are supported.

## VLAN in a non-default VRF

When the downstream relay VLAN is itself placed in a non-default VRF (its `VLAN_INTERFACE` carries a `vrf_name`), the DHCPv6 servers are reachable in that same VRF. The relay binds the per-VLAN upstream (server-facing) socket to the VLAN's VRF with `SO_BINDTODEVICE`. The VLAN's `vrf_name` is read from the `VLAN_INTERFACE` table; when it is unset the socket remains in the default routing table exactly as before. On success the relay logs `Bound upstream socket for <vlan> to VRF <vrf>`.

## Servers in a different VRF (`server_vrf`)

When the DHCPv6 servers are reachable in a VRF different from the VLAN's own routing table, an explicit `server_vrf` can be configured on the VLAN's `DHCP_RELAY` row. Because several VLANs may share the same `server_vrf`, the relay opens one shared upstream socket per `server_vrf`, bound to `in6addr_any` on port 547 and `SO_BINDTODEVICE`'d to that VRF (logged as `Created shared upstream socket for server VRF <vrf>`). Relay-forward messages for any VLAN whose `server_vrf` matches are sent on that shared socket, and relay-reply messages received on it are demultiplexed back to the originating VLAN using the link-address that the relay placed in the relay-forward message — the same shared-socket / link-address demultiplexing already used for the dual-ToR loopback socket. A `server_vrf` equal to the VLAN's own VRF is treated as "no separate server VRF", and the per-VLAN socket is used.

## CONFIG DB schema

<pre>
DHCP_RELAY|Vlan&lt;id&gt;|dhcpv6_servers: ["dhcp-server-0", ...]
DHCP_RELAY|Vlan&lt;id&gt;|server_vrf: "&lt;vrf-name&gt;" # optional; servers reachable in this VRF

VLAN_INTERFACE|Vlan&lt;id&gt;|vrf_name: "&lt;vrf-name&gt;" # existing; places the VLAN (and its relay) in a VRF
</pre>

`server_vrf` is optional and accepts a VRF name (a user-defined `Vrf*` instance, `mgmt`, or `default` for the global table). When it is absent the relay forwards in the VLAN's own routing table.

## YANG model

A `server_vrf` leaf is added to the `DHCP_RELAY` list in `sonic-dhcpv6-relay.yang`:

<pre>
leaf server_vrf {
type string {
length "1..15";
}
description "VRF in which the DHCPv6 servers are reachable.";
}
</pre>

It is modeled as a string (length 1..15, the `SO_BINDTODEVICE` `IFNAMSIZ` limit) rather than a leafref so that the reserved names `default` (global table) and `mgmt` are accepted in addition to user-defined `Vrf*` instances.

## Runtime behavior

VRF binding is applied at runtime by the Config Manager described in [Dynamic Configuration (no container restart)](#dynamic-configuration-no-container-restart), without restarting the `dhcp_relay` container. Adding, changing, or removing a VLAN's `vrf_name` or a `DHCP_RELAY` `server_vrf` re-binds (or tears down and re-opens) the affected upstream socket in place; the relay process PID is unchanged.

If a configured VRF does not exist when the relay attempts to bind (for example the `Vrf` instance has not yet been created), the `SO_BINDTODEVICE` call fails; the relay logs the failure and leaves the affected upstream socket unbound rather than silently falling back to the default routing table, so server-facing packets for that VLAN are dropped until the VRF exists and the next reconciliation re-binds the socket. Relay-reply messages received on a VRF-scoped upstream socket are validated as `RELAY-REPL` messages with a well-formed DHCPv6 relay header before being demultiplexed to the originating VLAN by their link-address; a message that is not a valid relay-reply, or whose link-address matches no VLAN served by that socket, is dropped.

## Backward compatibility

VRF support is opt-in. When neither a VLAN `vrf_name` nor a `server_vrf` is configured, the relay binds its upstream socket in the default routing table exactly as before, and the on-the-wire behavior is unchanged. No existing CONFIG_DB row needs to be modified to retain the previous behavior.

# Performance

SONiC DHCP relay agent is currently not relaying many DHCP requests. Frequency arrival rate of DHCP packets is not high so it is not going to affect performance.
Expand All @@ -233,3 +320,16 @@ Check validity of DHCP message content
Validate control plane behavior when DHCPv6 is enabled/disabled

Configuration validation

Validate runtime reconfiguration without restarting the `dhcp_relay` container:

- Add, modify, and remove `dhcpv6_servers` for a VLAN and confirm DHCPv6 messages are relayed to the updated server set, without a container restart
- Toggle the option 79 (`rfc6939_support`) and interface-id options and confirm the relayed packets reflect the change
- Add and remove a VLAN (and its IPv6 address) and confirm relay instances are created and torn down while other VLANs continue relaying uninterrupted
- Confirm the `need restart container to take effect` log is no longer emitted for the above changes

Validate VRF support:

- Place a relay VLAN in a non-default VRF and confirm the relay binds its upstream socket to that VRF and relays DHCPv6 messages to servers reachable in it
- Configure a `server_vrf` and confirm the relay opens a shared upstream socket in that VRF and relays and receives messages for the servers reachable there
- Add, change, and remove a VLAN's VRF or `server_vrf` at runtime and confirm the upstream socket is re-bound without restarting the `dhcp_relay` container, and that DHCPv6 traffic is relayed over the correct VRF socket and received back at the client