DHCP relay is forwarding unicast packets as well as broadcasts

Hi all, I wanted to ask about some weird behaviour I’m seeing with dhcp-relay - I have a client here sending DHCPREQUEST messages, it is just sat here renewing a 10 minute lease, so it sends a DHCPREQUEST every 5 minutes to the DHCP server, this packet is unicast as it’s a lease renewal. But for some strange reason the dhcp relay is intercepting the packet and forwarding it to both my DHCP servers (running as a failover pair). Each DHCP server then responds to the dhcp relay, and that in turns replies back to the client.

This means that for each unicast DHCPREQUEST packet I get 3 DHCPACK packets back, one direct from the DHCP server and two via the relay agent.

It seems like the dhcp relay is NOT looking at broadcast flag, it should only really forward the packet if it’s a broadcast not a unicast. Maybe there’s an option I’m missing somewhere, has anyone seen this behaviour or knows if it might be a bug?

I’m on a very recent 1.5 rolling release: 2025.04.29-0019-rolling.

I wanted to post some screenshots but because I’m new here it restricts me, so I did put a post up on reddit with more info, maybe you can have a look there…

https://www.reddit.com/r/vyos/comments/1kc65h8/dhcp_relay_is_forwarding_unicast_packets_as_well/

This is my config:

vyos@vyos:~$ show configuration
interfaces {
    ethernet eth0 {
        address 192.168.1.254/24
        description PROD
        hw-id 00:0c:29:1d:a8:07
        offload {
            gro
            gso
            sg
            tso
        }
    }
    ethernet eth1 {
        address 192.168.56.1/24
        description LAB
        hw-id 00:0c:29:1d:a8:11
        offload {
            gro
            gso
            sg
            tso
        }
    }
    ethernet eth2 {
        address 192.168.129.1/24
        address 2001:dead:beef:1::1/64
        description "4N lab"
        hw-id 00:0c:29:1d:a8:1b
        offload {
            gro
            gso
            sg
            tso
        }
    }
    ethernet eth3 {
        address 2001:db8::1/64
        hw-id 00:0c:29:1d:a8:25
        offload {
            gro
            gso
            sg
            tso
        }
    }
    ethernet eth4 {
        address 10.1.0.1/16
        description "Lab 10.1/16 network"
        hw-id 00:0c:29:1d:a8:2f
        offload {
            gro
            gso
            sg
            tso
        }
    }
    loopback lo {
    }
}
protocols {
    static {
        route 0.0.0.0/0 {
            next-hop 192.168.1.1 {
            }
        }
    }
}
service {
    dhcp-relay {
        listen-interface eth4
        relay-options {
            relay-agents-packets discard
        }
        server 192.168.56.11
        server 192.168.56.12
        upstream-interface eth1
    }
    ntp {
        allow-client {
            address 127.0.0.0/8
            address 169.254.0.0/16
            address 10.0.0.0/8
            address 172.16.0.0/12
            address 192.168.0.0/16
            address ::1/128
            address fe80::/10
            address fc00::/7
        }
        server 192.168.1.6 {
        }
        server time1.vyos.net {
        }
        server time2.vyos.net {
        }
        server time3.vyos.net {
        }
    }
    router-advert {
        interface eth3 {
            default-lifetime 3600
            default-preference high
            interval {
                max 60
                min 3
            }
            no-send-advert
            prefix ::/64 {
                preferred-lifetime 3600
                valid-lifetime 86400
            }
        }
    }
    ssh {
        port 22
    }
}
system {
    config-management {
        commit-revisions 100
    }
    console {
    }
    domain-search cn.corp
    host-name vyos
    login {
        user vyos {
            authentication {
                encrypted-password ****************
                plaintext-password ****************
            }
        }
    }
    name-server 192.168.1.6
    option {
    }
    syslog {
        local {
            facility all {
                level info
            }
            facility local7 {
                level debug
            }
        }
    }
}
vyos@vyos:~$

Is this behaviour expected or some kind of bug? Or maybe there’s a config option I can set to stop it forwarding unicasts?

I was just thinking about this some more, if it is a bug, maybe the solution is to add another relay option so that this behaviour can be maintained for people who need it?

As well as the relay-agents-packets option, we could have something like “forward-unicast yes|no”

Next question, what would we set the default to? If we set it to “no” that would change all the existing deployed implementations out there and I guess could cause an outage, although IMHO from a purists perspective, “no” is the incorrect setting. Setting the default to “yes” would reduce the risk of any outages when people upgrade, but means this issue isn’t fixed unless someone specifically sets the value to “no” - and they’d probably have to be quite nerdy to know about this (and care).

eg:

        relay-options {
            relay-agents-packets discard
            forward-unicast no
        }

Thoughts?