@@ -20,6 +20,7 @@ import (
2020 "github.com/vishvananda/netlink"
2121
2222 "k8s.io/klog/v2"
23+ "sigs.k8s.io/knftables"
2324
2425 "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types"
2526 "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util"
@@ -252,6 +253,13 @@ func setupInterface(netns ns.NetNS, containerID, ifName string, ifInfo *PodInter
252253 ifnameSuffix = fmt .Sprintf ("_%d" , containerVeth .Index )
253254 }
254255
256+ // If we have the ipv6 gateway LLA then this is a primary layer2 UDN
257+ if len (ifInfo .GatewayIPv6LLA ) > 0 {
258+ if err = setupIngressFilter (ifName , ifInfo .GatewayIPv6LLA .String ()); err != nil {
259+ return err
260+ }
261+ }
262+
255263 return nil
256264 })
257265 if err != nil {
@@ -597,6 +605,7 @@ func (*defaultPodRequestInterfaceOps) ConfigureInterface(pr *PodRequest, getter
597605 return nil , fmt .Errorf ("unexpected configuration, pod request on dpu host. " +
598606 "device ID must be provided" )
599607 }
608+
600609 // General case
601610 hostIface , contIface , err = setupInterface (netns , pr .SandboxID , pr .IfName , ifInfo )
602611 }
@@ -818,3 +827,68 @@ func (pr *PodRequest) deletePorts(ifaces []string, podNamespace, podName string)
818827 pr .deletePort (iface , podNamespace , podName )
819828 }
820829}
830+
831+ // setupIngressFilter sets up an ingress filter using nftables to block
832+ // unwanted ICMPv6 Router Advertisement (RA) packets on a specific device.
833+ // It creates a new nftables table, chain, and rule to drop RA packets
834+ // that do not match the specified gateway link-local address (gwLLA) and
835+ // have a Router Advertisement (RA) lifetime not equal to 0.
836+ //
837+ // The nftables rule created by this function looks like:
838+ // `icmpv6 type nd-router-advert ip6 saddr != <gwLLA> @th,48,16 != 0 drop`
839+ //
840+ // Parameters:
841+ // - ifaceName: The name of the network interface where the ingress filter
842+ // should be applied.
843+ // - gwLLA: The gateway link-local address to match against in the RA packets.
844+ //
845+ // Returns:
846+ // - error: An error if the nftables setup fails, otherwise nil.
847+ func setupIngressFilter (ifaceName , gwLLA string ) error {
848+ const (
849+ tableName = "ingress_filter" // Name of the nftables table to be created
850+ rasLifetime = "@th,48,16" // Offset for RA lifetime field in ICMPv6 packets
851+ )
852+
853+ // Initialize a new nftables table for the ingress filter
854+ nft , err := knftables .New (knftables .NetDevFamily , tableName )
855+ if err != nil {
856+ return fmt .Errorf ("failed to initialize table: %w" , err )
857+ }
858+
859+ // Delegate the setup of the filter with the specified table and lifetime matcher
860+ return setupIngressFilterWithTableAndLifetimeMatcher (nft , ifaceName , gwLLA , rasLifetime )
861+ }
862+
863+ func setupIngressFilterWithTableAndLifetimeMatcher (nft knftables.Interface , ifaceName , gwLLA , rasLifetime string ) error {
864+ const (
865+ chainName = "input"
866+ )
867+
868+ tx := nft .NewTransaction ()
869+
870+ tx .Add (& knftables.Table {})
871+
872+ tx .Add (& knftables.Chain {
873+ Name : chainName ,
874+ Type : knftables .PtrTo (knftables .FilterType ),
875+ Hook : knftables .PtrTo (knftables .IngressHook ),
876+ Priority : knftables .PtrTo (knftables .FilterPriority ),
877+ Device : knftables .PtrTo (ifaceName ),
878+ })
879+
880+ tx .Add (& knftables.Rule {
881+ Chain : chainName ,
882+ Rule : knftables .Concat (
883+ "icmpv6" , "type" , "nd-router-advert" , "ip6" , "saddr" , "!=" , gwLLA , rasLifetime , "!=" , "0" , "drop" ,
884+ ),
885+ })
886+
887+ // Execute the transaction
888+ if err := nft .Run (context .Background (), tx ); err != nil {
889+ return fmt .Errorf ("could not update netdev nftables: %v" , err )
890+ }
891+
892+ return nil
893+
894+ }
0 commit comments