@@ -10,10 +10,13 @@ import (
1010 corev1 "k8s.io/api/core/v1"
1111 listers "k8s.io/client-go/listers/core/v1"
1212 "k8s.io/klog/v2"
13+ utilnet "k8s.io/utils/net"
1314
1415 "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/allocator/id"
1516 "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/allocator/ip"
1617 "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/allocator/ip/subnet"
18+ "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config"
19+ "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/generator/udn"
1720 "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/kube"
1821 "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/persistentips"
1922 "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types"
@@ -434,7 +437,7 @@ func allocatePodAnnotationWithRollback(
434437 }
435438
436439 // handle routes & gateways
437- err = util . AddRoutesGatewayIP (netInfo , node , pod , tentative , network )
440+ err = AddRoutesGatewayIP (netInfo , node , pod , tentative , network )
438441 if err != nil {
439442 return
440443 }
@@ -456,3 +459,187 @@ func allocatePodAnnotationWithRollback(
456459
457460 return
458461}
462+
463+ func joinSubnetToRoute (netinfo util.NetInfo , isIPv6 bool , gatewayIP net.IP ) util.PodRoute {
464+ joinSubnet := netinfo .JoinSubnetV4 ()
465+ if isIPv6 {
466+ joinSubnet = netinfo .JoinSubnetV6 ()
467+ }
468+ return util.PodRoute {
469+ Dest : joinSubnet ,
470+ NextHop : gatewayIP ,
471+ }
472+ }
473+
474+ func serviceCIDRToRoute (isIPv6 bool , gatewayIP net.IP ) []util.PodRoute {
475+ var podRoutes []util.PodRoute
476+ for _ , serviceSubnet := range config .Kubernetes .ServiceCIDRs {
477+ if isIPv6 == utilnet .IsIPv6CIDR (serviceSubnet ) {
478+ podRoutes = append (podRoutes , util.PodRoute {
479+ Dest : serviceSubnet ,
480+ NextHop : gatewayIP ,
481+ })
482+ }
483+ }
484+ return podRoutes
485+ }
486+
487+ func hairpinMasqueradeIPToRoute (isIPv6 bool , gatewayIP net.IP ) util.PodRoute {
488+ ip := config .Gateway .MasqueradeIPs .V4OVNServiceHairpinMasqueradeIP
489+ if isIPv6 {
490+ ip = config .Gateway .MasqueradeIPs .V6OVNServiceHairpinMasqueradeIP
491+ }
492+ return util.PodRoute {
493+ Dest : & net.IPNet {
494+ IP : ip ,
495+ Mask : util .GetIPFullMask (ip ),
496+ },
497+ NextHop : gatewayIP ,
498+ }
499+ }
500+
501+ // addRoutesGatewayIP updates the provided pod annotation for the provided pod
502+ // with the gateways derived from the allocated IPs
503+ func AddRoutesGatewayIP (
504+ netinfo util.NetInfo ,
505+ node * corev1.Node ,
506+ pod * corev1.Pod ,
507+ podAnnotation * util.PodAnnotation ,
508+ network * nadapi.NetworkSelectionElement ) error {
509+
510+ // generate the nodeSubnets from the allocated IPs
511+ nodeSubnets := util .IPsToNetworkIPs (podAnnotation .IPs ... )
512+
513+ if netinfo .IsSecondary () {
514+ // for secondary network, see if its network-attachment's annotation has default-route key.
515+ // If present, then we need to add default route for it
516+ podAnnotation .Gateways = append (podAnnotation .Gateways , network .GatewayRequest ... )
517+ topoType := netinfo .TopologyType ()
518+ switch topoType {
519+ case types .LocalnetTopology :
520+ // no route needed for directly connected subnets
521+ return nil
522+ case types .Layer2Topology :
523+ if ! util .IsNetworkSegmentationSupportEnabled () || ! netinfo .IsPrimaryNetwork () {
524+ return nil
525+ }
526+ for _ , podIfAddr := range podAnnotation .IPs {
527+ isIPv6 := utilnet .IsIPv6CIDR (podIfAddr )
528+ nodeSubnet , err := util .MatchFirstIPNetFamily (isIPv6 , nodeSubnets )
529+ if err != nil {
530+ return err
531+ }
532+ gatewayIPnet := util .GetNodeGatewayIfAddr (nodeSubnet )
533+ // Ensure default service network traffic always goes to OVN
534+ podAnnotation .Routes = append (podAnnotation .Routes , serviceCIDRToRoute (isIPv6 , gatewayIPnet .IP )... )
535+ // Ensure UDN join subnet traffic always goes to UDN LSP
536+ podAnnotation .Routes = append (podAnnotation .Routes , joinSubnetToRoute (netinfo , isIPv6 , gatewayIPnet .IP ))
537+ if network != nil && len (network .GatewayRequest ) == 0 { // if specific default route for pod was not requested then add gatewayIP
538+ podAnnotation .Gateways = append (podAnnotation .Gateways , gatewayIPnet .IP )
539+ }
540+ }
541+ // Until https://github.com/ovn-kubernetes/ovn-kubernetes/issues/4876 is fixed, it is limited to IC only
542+ if config .OVNKubernetesFeature .EnableInterconnect {
543+ if _ , isIPv6Mode := netinfo .IPMode (); isIPv6Mode {
544+ joinAddrs , err := udn .GetGWRouterIPs (node , netinfo .GetNetInfo ())
545+ if err != nil {
546+ if util .IsAnnotationNotSetError (err ) {
547+ return types .NewSuppressedError (err )
548+ }
549+ return fmt .Errorf ("failed parsing node gateway router join addresses, network %q, %w" , netinfo .GetNetworkName (), err )
550+ }
551+ podAnnotation .GatewayIPv6LLA = util .HWAddrToIPv6LLA (util .IPAddrToHWAddr (joinAddrs [0 ].IP ))
552+ }
553+ }
554+ return nil
555+ case types .Layer3Topology :
556+ for _ , podIfAddr := range podAnnotation .IPs {
557+ isIPv6 := utilnet .IsIPv6CIDR (podIfAddr )
558+ nodeSubnet , err := util .MatchFirstIPNetFamily (isIPv6 , nodeSubnets )
559+ if err != nil {
560+ return err
561+ }
562+ gatewayIPnet := util .GetNodeGatewayIfAddr (nodeSubnet )
563+ for _ , clusterSubnet := range netinfo .Subnets () {
564+ if isIPv6 == utilnet .IsIPv6CIDR (clusterSubnet .CIDR ) {
565+ podAnnotation .Routes = append (podAnnotation .Routes , util.PodRoute {
566+ Dest : clusterSubnet .CIDR ,
567+ NextHop : gatewayIPnet .IP ,
568+ })
569+ }
570+ }
571+ if ! util .IsNetworkSegmentationSupportEnabled () || ! netinfo .IsPrimaryNetwork () {
572+ continue
573+ }
574+ // Ensure default service network traffic always goes to OVN
575+ podAnnotation .Routes = append (podAnnotation .Routes , serviceCIDRToRoute (isIPv6 , gatewayIPnet .IP )... )
576+ // Ensure UDN join subnet traffic always goes to UDN LSP
577+ podAnnotation .Routes = append (podAnnotation .Routes , joinSubnetToRoute (netinfo , isIPv6 , gatewayIPnet .IP ))
578+ if network != nil && len (network .GatewayRequest ) == 0 { // if specific default route for pod was not requested then add gatewayIP
579+ podAnnotation .Gateways = append (podAnnotation .Gateways , gatewayIPnet .IP )
580+ }
581+ }
582+ return nil
583+ }
584+ return fmt .Errorf ("topology type %s not supported" , topoType )
585+ }
586+
587+ // if there are other network attachments for the pod, then check if those network-attachment's
588+ // annotation has default-route key. If present, then we need to skip adding default route for
589+ // OVN interface
590+ networks , err := util .GetK8sPodAllNetworkSelections (pod )
591+ if err != nil {
592+ return fmt .Errorf ("error while getting network attachment definition for [%s/%s]: %v" ,
593+ pod .Namespace , pod .Name , err )
594+ }
595+ otherDefaultRouteV4 := false
596+ otherDefaultRouteV6 := false
597+ for _ , network := range networks {
598+ for _ , gatewayRequest := range network .GatewayRequest {
599+ if utilnet .IsIPv6 (gatewayRequest ) {
600+ otherDefaultRouteV6 = true
601+ } else {
602+ otherDefaultRouteV4 = true
603+ }
604+ }
605+ }
606+
607+ for _ , podIfAddr := range podAnnotation .IPs {
608+ isIPv6 := utilnet .IsIPv6CIDR (podIfAddr )
609+ nodeSubnet , err := util .MatchFirstIPNetFamily (isIPv6 , nodeSubnets )
610+ if err != nil {
611+ return err
612+ }
613+
614+ gatewayIPnet := util .GetNodeGatewayIfAddr (nodeSubnet )
615+
616+ // Ensure default pod network traffic always goes to OVN
617+ for _ , clusterSubnet := range config .Default .ClusterSubnets {
618+ if isIPv6 == utilnet .IsIPv6CIDR (clusterSubnet .CIDR ) {
619+ podAnnotation .Routes = append (podAnnotation .Routes , util.PodRoute {
620+ Dest : clusterSubnet .CIDR ,
621+ NextHop : gatewayIPnet .IP ,
622+ })
623+ }
624+ }
625+
626+ if podAnnotation .Role == types .NetworkRolePrimary {
627+ // Ensure default service network traffic always goes to OVN
628+ podAnnotation .Routes = append (podAnnotation .Routes , serviceCIDRToRoute (isIPv6 , gatewayIPnet .IP )... )
629+ // Ensure service hairpin masquerade traffic always goes to OVN
630+ podAnnotation .Routes = append (podAnnotation .Routes , hairpinMasqueradeIPToRoute (isIPv6 , gatewayIPnet .IP ))
631+ otherDefaultRoute := otherDefaultRouteV4
632+ if isIPv6 {
633+ otherDefaultRoute = otherDefaultRouteV6
634+ }
635+ if ! otherDefaultRoute {
636+ podAnnotation .Gateways = append (podAnnotation .Gateways , gatewayIPnet .IP )
637+ }
638+ }
639+
640+ // Ensure default join subnet traffic always goes to OVN
641+ podAnnotation .Routes = append (podAnnotation .Routes , joinSubnetToRoute (netinfo , isIPv6 , gatewayIPnet .IP ))
642+ }
643+
644+ return nil
645+ }
0 commit comments