Linux Distributed Switch Architecture with Local Option82 ISC DHCP server/DHCP relay

Simon Hobson dhcp1 at
Tue Nov 10 11:09:18 UTC 2020

remi rsd <remi.salard at> wrote:

> Customer requirement is: all my IP cameras connected to your product SHALL
> get, by DHCP, their IP address according to their physical port, not their
> hostname.
> All the IP addresses SHALL be in the same network.
> About our product:
> - OS: Linux Yocto Zeus
> - CPU: imx6 based module
> - Switch: 88E6240 Marvell

A lookup suggests the switch is fairly basic - so no DHCP snooping/option-82 insertion

> Software versions:
> - ISC DHCP version: 4.4.1
> - Linux version: 4.14
> Idea: using DHCP option 82 + Distributed Switch Architecture (DSA) in order
> to isolate ports
> What I did:
> DSA enabled => 1 physical port = 1 linux network interface
> 1 bridge created linking all this ports with a dsa_br0 interface
> Option 82 realized by launching a dhcrelay (DHCP relay agent) for each
> interface (-i portX and set as up command in /etc/network/interfaces) with
> "-a" option in order to append option82 with port name as circuit ID.

I don't think that will work. As far as networking (at the IP layer) is concerned, you still have one network ...

> DHCP server config:
> one subnet, the expected one (the subnet of dsa_br0 bridge interface) + 
> hosts declared with "host-identifier option agent.circuit-id "portX";" 
> option
> Side effect (impact ?): I need to declare IP address for each port (in
> /etc/network/interfaces), otherwise dhcrelay will not work (note: all are in
> separate network)

That's right, the relay agent needs an IP address for the interface it's listening on - otherwise it doesn't know what to put in the relayed packets.

>    subnet netmask {
>        option routers;
>        option broadcast-address;
>        option domain-name-servers;
>        host port1 {
>            host-identifier option agent.circuit-id "port1";
>            fixed-address;
>        }
>       host port2 {
>            host-identifier option agent.circuit-id "port2";
>            fixed-address;
>        }
>    }

As a general point, host statements are global - don't declare them inside a subnet as that can cause some "interesting" inheritance problems.

> And network configuration:
>    ifconfig
>    dsa_br0   Link encap:Ethernet  HWaddr 00:e0:4b:6d:e2:70  
>              inet addr:  Bcast: 
> Mask:
>              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
>              RX packets:4130 errors:0 dropped:19 overruns:0 frame:0
>              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
>              collisions:0 txqueuelen:1000 
>              RX bytes:1298917 (1.2 MiB)  TX bytes:0 (0.0 B)
>    eth0      Link encap:Ethernet  HWaddr 00:e0:4b:6d:e2:70  
>              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
>              RX packets:4524 errors:0 dropped:0 overruns:0 frame:0
>              TX packets:2758 errors:0 dropped:0 overruns:0 carrier:0
>              collisions:0 txqueuelen:1000 
>              RX bytes:1555293 (1.4 MiB)  TX bytes:473137 (462.0 KiB)
>    port1     Link encap:Ethernet  HWaddr 00:e0:4b:6d:e2:70  
>              inet addr:  Bcast:  Mask:
>              UP BROADCAST MULTICAST  MTU:1500  Metric:1
>              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
>              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
>              collisions:0 txqueuelen:1000 
>              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
>    port2     Link encap:Ethernet  HWaddr 00:e0:4b:6d:e2:70  
>              inet addr:  Bcast:  Mask:
>              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
>              RX packets:808 errors:0 dropped:0 overruns:0 frame:0
>              TX packets:1950 errors:0 dropped:0 overruns:0 carrier:0
>              collisions:0 txqueuelen:1000 
>              RX bytes:190934 (186.4 KiB)  TX bytes:248827 (242.9 KiB)

As your port addresses are in a different subnet, you need a shared network statement so that the dhcp server can associate relayed packets with (e.g.) in the CI-Addr field with clients in the subnet. That would look like :

shared-network somenamehere {
  subnet netmask {}
  subnet netmask {}
  subnet netmask {
    stuff goes here

> My dhcrelay command:
>    dhcrelay -a -i port2

From memory, the relay agent also needs to listen on the interface through which it talks to the server - this could get messy

> It seems that DHCP server doesn't receive any DHCP frame at all from
> dhcrelay. It receives raw frame (without option82) => so I added ebtables
> rules in order to block them

That sounds about right. Because you've bridged all the ports together, they're one network from the PoV of broadcast traffic.

> - Another idea for solving customer requirement ?

A few thoughts come to mind, none of which I've ever done or tested so some more thought and experimentation might be needed. Also, be aware that once a client has a lease, it will then use unicast packets to communicate directly with the server (NOT via a relay agent) - so you need to take this into account.

I'm assuming the hardware is fixed, so you don't have the option of using a "smarter" switch that can do the Option-82 insertion ? That would be the ideal approach if it were practical.

Don't bridge the ports together, but use proxy-ARP to make it look like they are all on the same subnet. From the DHCP PoV, the relay agents should work. However, I think you'll have to lump them all together in one shared network - and allocate addresses by filtering (use classes ?) on CI-Addr field in the incoming packets in order to get the dhcp server to work.

Write a monitor of some sort that will monitor the state of switch ports and the devices connected. I have in mind something that will :
- When a port becomes active, detect the MAC address of the connected device and configure the dhcp server with a MAC-IP mapping (host declaration ?).
- When a port becomes inactive, remove that mapping. If you only use host statements, there's no lease to remove.
You probably need to configure the hardware switch with one VLAN/port and use bridging in Linux to give access to the per-port MAC tables as I assume the bridge chip doesn't make this visible. Performance wise it would be best to use the hardware bridge as a single interface IFF you can access port status and attached devices list/port (MAC-Port mappings).
There's also an implicit assumption (taken from your original problem statement) that there will only ever be one client attached to a single port. And I'm assuming that there will be one interface which will be excluded from this setup that is used for connection to "the outside world".

Related to this, consider if it's possible to upgrade the hardware to something that can show you port status and MAC tables - then you can let the hardware switch do the switching.

Talk to the client and see if you can compromise on the requirements. If they'd allow multiple subnets then it would significantly simplify your problem.
In principle, it would allow you to just have one subnet per port as a separate network - without needing relay agents etc. But you might still need to do some work to allow device swapping - otherwise if devices are changed/moved, a new device on a port won't be able to get an address until the previous lease has expired.

I'm thinking that the monitor setup might be the easiest. In pseudo-code, it would just need to do something along the lines of :
If ${port_status} is down and ${port_config} is not empty
    echo > ${port}_config
    restart dhcpd
If ${port_status} is up and ${port_config} is empty
  Look for a MAC address for a connected port
  if we got a MAC address
    then echo "host \"port_${port}\" \{ hardware ethernet ${port_mac} \}" > ${port}_config
    restart dhcpd

And in your dhcp config, have include statements to include the port configs (host statements).


More information about the dhcp-users mailing list