2023-10-07 08:52:31 -05:00

870 lines
26 KiB
Go

package routing
import (
"context"
"errors"
"fmt"
"net"
"reflect"
"sort"
"strconv"
gobgpapi "github.com/osrg/gobgp/v3/api"
v1core "k8s.io/api/core/v1"
"k8s.io/klog/v2"
"github.com/cloudnativelabs/kube-router/pkg/utils"
)
const (
podCIDRSet = "podcidrdefinedset"
podCIDRSetV6 = "podcidrdefinedsetv6"
serviceVIPsSet = "servicevipsdefinedset"
serviceVIPsSetV6 = "servicevipsdefinedsetv6"
allPeerSet = "allpeerset"
allPeerSetV6 = "allpeersetv6"
externalPeerSet = "externalpeerset"
externalPeerSetV6 = "externalpeersetv6"
iBGPPeerSet = "iBGPpeerset"
iBGPPeerSetV6 = "iBGPpeersetv6"
customImportRejectSet = "customimportrejectdefinedset"
defaultRouteSet = "defaultroutedefinedset"
defaultRouteSetV6 = "defaultroutedefinedsetv6"
kubeRouterExportPolicy = "kube_router_export"
kubeRouterImportPolicy = "kube_router_import"
maxIPv4MaskSize = uint32(32)
maxIPv6MaskSize = uint32(128)
)
// AddPolicies adds BGP import and export policies
func (nrc *NetworkRoutingController) AddPolicies() error {
// we are rr server do not add export policies
if nrc.bgpRRServer {
return nil
}
err := nrc.addPodCidrDefinedSet()
if err != nil {
klog.Errorf("Failed to add `podcidrdefinedset` defined set: %s", err)
}
err = nrc.addServiceVIPsDefinedSet()
if err != nil {
klog.Errorf("Failed to add `servicevipsdefinedset` defined set: %s", err)
}
err = nrc.addDefaultRouteDefinedSet()
if err != nil {
klog.Errorf("Failed to add `defaultroutedefinedset` defined set: %s", err)
}
err = nrc.addCustomImportRejectDefinedSet()
if err != nil {
klog.Errorf("Failed to add `customimportrejectdefinedset` defined set: %s", err)
}
iBGPPeerCIDRs, err := nrc.addiBGPPeersDefinedSet()
if err != nil {
klog.Errorf("Failed to add `iBGPpeerset` defined set: %s", err)
}
externalBGPPeerCIDRs, err := nrc.addExternalBGPPeersDefinedSet()
if err != nil {
klog.Errorf("Failed to add `externalpeerset` defined set: %s", err)
}
err = nrc.addAllBGPPeersDefinedSet(iBGPPeerCIDRs, externalBGPPeerCIDRs)
if err != nil {
klog.Errorf("Failed to add `allpeerset` defined set: %s", err)
}
err = nrc.addExportPolicies()
if err != nil {
return err
}
err = nrc.addImportPolicies()
if err != nil {
return err
}
return nil
}
// create a defined set to represent just the pod CIDR associated with the node
func (nrc *NetworkRoutingController) addPodCidrDefinedSet() error {
var currentDefinedSet *gobgpapi.DefinedSet
for setName, cidrs := range map[string][]string{
podCIDRSet: nrc.podIPv4CIDRs,
podCIDRSetV6: nrc.podIPv6CIDRs,
} {
err := nrc.bgpServer.ListDefinedSet(context.Background(),
&gobgpapi.ListDefinedSetRequest{DefinedType: gobgpapi.DefinedType_PREFIX, Name: setName},
func(ds *gobgpapi.DefinedSet) {
currentDefinedSet = ds
})
if err != nil {
return err
}
if currentDefinedSet == nil {
var prefixes []*gobgpapi.Prefix
for _, cidr := range cidrs {
_, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
return fmt.Errorf("couldn't parse CIDR: %s - %v", cidr, err)
}
cidrLen, _ := ipNet.Mask.Size()
var cidrMax int
if setName == podCIDRSet {
cidrMax = 32
} else {
cidrMax = 128
}
if cidrLen < 0 || cidrLen > cidrMax {
return fmt.Errorf("the pod CIDR IP given is not a proper mask: %d", cidrLen)
}
prefixes = append(prefixes, &gobgpapi.Prefix{
IpPrefix: cidr,
MaskLengthMin: uint32(cidrLen),
MaskLengthMax: uint32(cidrLen),
})
}
podCidrDefinedSet := &gobgpapi.DefinedSet{
DefinedType: gobgpapi.DefinedType_PREFIX,
Name: setName,
Prefixes: prefixes,
}
err = nrc.bgpServer.AddDefinedSet(context.Background(),
&gobgpapi.AddDefinedSetRequest{DefinedSet: podCidrDefinedSet})
if err != nil {
return err
}
}
}
return nil
}
// create a defined set to represent all the advertisable IP associated with the services
func (nrc *NetworkRoutingController) addServiceVIPsDefinedSet() error {
var currentDefinedSet *gobgpapi.DefinedSet
for setName, cidrMask := range map[string]uint32{
serviceVIPsSet: maxIPv4MaskSize,
serviceVIPsSetV6: maxIPv6MaskSize,
} {
err := nrc.bgpServer.ListDefinedSet(context.Background(),
&gobgpapi.ListDefinedSetRequest{DefinedType: gobgpapi.DefinedType_PREFIX, Name: setName},
func(ds *gobgpapi.DefinedSet) {
currentDefinedSet = ds
})
if err != nil {
return err
}
advIPPrefixList := make([]*gobgpapi.Prefix, 0)
advIps, _, _ := nrc.getAllVIPs()
for _, ipStr := range advIps {
ip := net.ParseIP(ipStr)
if ip == nil {
// nothing to do for invalid IPs
klog.Warningf("found an invalid IP address in list of service VIPs returned from k8s: %s skipping",
ipStr)
continue
}
switch {
// Only add IPv4 VIPs when we are populating the serviceVIPsSet set
case ip.To4() != nil && setName == serviceVIPsSet:
advIPPrefixList = append(advIPPrefixList,
&gobgpapi.Prefix{
IpPrefix: fmt.Sprintf("%s/%d", ip, cidrMask),
MaskLengthMin: cidrMask,
MaskLengthMax: cidrMask,
})
// Only add IPv6 VIPs when we are populating the serviceVIPsSetV6 set
case ip.To4() == nil && ip.To16() != nil && setName == serviceVIPsSetV6:
advIPPrefixList = append(advIPPrefixList,
&gobgpapi.Prefix{
IpPrefix: fmt.Sprintf("%s/%d", ip, cidrMask),
MaskLengthMin: cidrMask,
MaskLengthMax: cidrMask,
})
}
}
if currentDefinedSet == nil {
clusterIPPrefixSet := &gobgpapi.DefinedSet{
DefinedType: gobgpapi.DefinedType_PREFIX,
Name: setName,
Prefixes: advIPPrefixList,
}
err = nrc.bgpServer.AddDefinedSet(context.Background(),
&gobgpapi.AddDefinedSetRequest{DefinedSet: clusterIPPrefixSet})
if err != nil {
return err
}
continue
}
currentPrefixes := currentDefinedSet.Prefixes
sort.SliceStable(advIPPrefixList, func(i, j int) bool {
return advIPPrefixList[i].IpPrefix < advIPPrefixList[j].IpPrefix
})
sort.SliceStable(currentPrefixes, func(i, j int) bool {
return currentPrefixes[i].IpPrefix < currentPrefixes[j].IpPrefix
})
if reflect.DeepEqual(advIPPrefixList, currentPrefixes) {
return nil
}
toAdd := make([]*gobgpapi.Prefix, 0)
toDelete := make([]*gobgpapi.Prefix, 0)
for _, prefix := range advIPPrefixList {
add := true
for _, currentPrefix := range currentDefinedSet.Prefixes {
if currentPrefix.IpPrefix == prefix.IpPrefix {
add = false
}
}
if add {
toAdd = append(toAdd, prefix)
}
}
for _, currentPrefix := range currentDefinedSet.Prefixes {
shouldDelete := true
for _, prefix := range advIPPrefixList {
if currentPrefix.IpPrefix == prefix.IpPrefix {
shouldDelete = false
}
}
if shouldDelete {
toDelete = append(toDelete, currentPrefix)
}
}
clusterIPPrefixSet := &gobgpapi.DefinedSet{
DefinedType: gobgpapi.DefinedType_PREFIX,
Name: setName,
Prefixes: toAdd,
}
err = nrc.bgpServer.AddDefinedSet(context.Background(),
&gobgpapi.AddDefinedSetRequest{DefinedSet: clusterIPPrefixSet})
if err != nil {
return err
}
clusterIPPrefixSet = &gobgpapi.DefinedSet{
DefinedType: gobgpapi.DefinedType_PREFIX,
Name: setName,
Prefixes: toDelete,
}
err = nrc.bgpServer.DeleteDefinedSet(context.Background(),
&gobgpapi.DeleteDefinedSetRequest{DefinedSet: clusterIPPrefixSet, All: false})
if err != nil {
return err
}
}
return nil
}
// create a defined set to represent just the host default route
func (nrc *NetworkRoutingController) addDefaultRouteDefinedSet() error {
var currentDefinedSet *gobgpapi.DefinedSet
for setName, defaultRoute := range map[string]string{
defaultRouteSet: "0.0.0.0/0",
defaultRouteSetV6: "::/0",
} {
err := nrc.bgpServer.ListDefinedSet(context.Background(),
&gobgpapi.ListDefinedSetRequest{DefinedType: gobgpapi.DefinedType_PREFIX, Name: setName},
func(ds *gobgpapi.DefinedSet) {
currentDefinedSet = ds
})
if err != nil {
return err
}
if currentDefinedSet == nil {
cidrLen := 0
defaultRouteDefinedSet := &gobgpapi.DefinedSet{
DefinedType: gobgpapi.DefinedType_PREFIX,
Name: setName,
Prefixes: []*gobgpapi.Prefix{
{
IpPrefix: defaultRoute,
MaskLengthMin: uint32(cidrLen),
MaskLengthMax: uint32(cidrLen),
},
},
}
err = nrc.bgpServer.AddDefinedSet(context.Background(),
&gobgpapi.AddDefinedSetRequest{DefinedSet: defaultRouteDefinedSet})
if err != nil {
return err
}
}
}
return nil
}
// create a defined set to represent custom annotated routes to be rejected on import
func (nrc *NetworkRoutingController) addCustomImportRejectDefinedSet() error {
var currentDefinedSet *gobgpapi.DefinedSet
err := nrc.bgpServer.ListDefinedSet(context.Background(),
&gobgpapi.ListDefinedSetRequest{DefinedType: gobgpapi.DefinedType_PREFIX, Name: customImportRejectSet},
func(ds *gobgpapi.DefinedSet) {
currentDefinedSet = ds
})
if err != nil {
return err
}
if currentDefinedSet == nil {
prefixes := make([]*gobgpapi.Prefix, 0)
for _, ipNet := range nrc.nodeCustomImportRejectIPNets {
prefix := new(gobgpapi.Prefix)
prefix.IpPrefix = ipNet.String()
mask, _ := ipNet.Mask.Size()
prefix.MaskLengthMin = uint32(mask)
prefix.MaskLengthMax = uint32(ipv4MaskMinBits)
prefixes = append(prefixes, prefix)
}
customImportRejectDefinedSet := &gobgpapi.DefinedSet{
DefinedType: gobgpapi.DefinedType_PREFIX,
Name: customImportRejectSet,
Prefixes: prefixes,
}
return nrc.bgpServer.AddDefinedSet(context.Background(),
&gobgpapi.AddDefinedSetRequest{DefinedSet: customImportRejectDefinedSet})
}
return nil
}
func (nrc *NetworkRoutingController) addiBGPPeersDefinedSet() (map[v1core.IPFamily][]string, error) {
iBGPPeerCIDRs := make(map[v1core.IPFamily][]string)
if !nrc.bgpEnableInternal {
return iBGPPeerCIDRs, nil
}
// Get the current list of the nodes from the local cache
nodes := nrc.nodeLister.List()
for _, node := range nodes {
nodeObj := node.(*v1core.Node)
nodeIP, err := utils.GetPrimaryNodeIP(nodeObj)
if err != nil {
klog.Errorf("Failed to find a node IP and therefore cannot add internal BGP Peer: %v", err)
continue
}
if nodeIP.To4() != nil {
iBGPPeerCIDRs[v1core.IPv4Protocol] = append(iBGPPeerCIDRs[v1core.IPv4Protocol], nodeIP.String()+"/32")
} else {
iBGPPeerCIDRs[v1core.IPv6Protocol] = append(iBGPPeerCIDRs[v1core.IPv6Protocol], nodeIP.String()+"/128")
}
}
for family, setName := range map[v1core.IPFamily]string{
v1core.IPv4Protocol: iBGPPeerSet,
v1core.IPv6Protocol: iBGPPeerSetV6,
} {
var currentDefinedSet *gobgpapi.DefinedSet
err := nrc.bgpServer.ListDefinedSet(context.Background(),
&gobgpapi.ListDefinedSetRequest{DefinedType: gobgpapi.DefinedType_NEIGHBOR, Name: setName},
func(ds *gobgpapi.DefinedSet) {
currentDefinedSet = ds
})
if err != nil {
return iBGPPeerCIDRs, err
}
if currentDefinedSet == nil {
iBGPPeerNS := &gobgpapi.DefinedSet{
DefinedType: gobgpapi.DefinedType_NEIGHBOR,
Name: setName,
List: iBGPPeerCIDRs[family],
}
err = nrc.bgpServer.AddDefinedSet(context.Background(),
&gobgpapi.AddDefinedSetRequest{DefinedSet: iBGPPeerNS})
if err != nil {
return iBGPPeerCIDRs, err
}
continue
}
currentCIDRs := currentDefinedSet.List
sort.Strings(iBGPPeerCIDRs[family])
sort.Strings(currentCIDRs)
if reflect.DeepEqual(iBGPPeerCIDRs, currentCIDRs) {
return iBGPPeerCIDRs, nil
}
toAdd := make([]string, 0)
toDelete := make([]string, 0)
for _, prefix := range iBGPPeerCIDRs[family] {
add := true
for _, currentPrefix := range currentDefinedSet.List {
if prefix == currentPrefix {
add = false
}
}
if add {
toAdd = append(toAdd, prefix)
}
}
for _, currentPrefix := range currentDefinedSet.List {
shouldDelete := true
for _, prefix := range iBGPPeerCIDRs[family] {
if currentPrefix == prefix {
shouldDelete = false
}
}
if shouldDelete {
toDelete = append(toDelete, currentPrefix)
}
}
iBGPPeerNS := &gobgpapi.DefinedSet{
DefinedType: gobgpapi.DefinedType_NEIGHBOR,
Name: setName,
List: toAdd,
}
err = nrc.bgpServer.AddDefinedSet(context.Background(), &gobgpapi.AddDefinedSetRequest{DefinedSet: iBGPPeerNS})
if err != nil {
return iBGPPeerCIDRs, err
}
iBGPPeerNS = &gobgpapi.DefinedSet{
DefinedType: gobgpapi.DefinedType_NEIGHBOR,
Name: setName,
List: toDelete,
}
err = nrc.bgpServer.DeleteDefinedSet(context.Background(),
&gobgpapi.DeleteDefinedSetRequest{DefinedSet: iBGPPeerNS, All: false})
if err != nil {
return iBGPPeerCIDRs, err
}
}
return iBGPPeerCIDRs, nil
}
func (nrc *NetworkRoutingController) addExternalBGPPeersDefinedSet() (map[v1core.IPFamily][]string, error) {
var currentDefinedSet *gobgpapi.DefinedSet
externalBgpPeers := make([]string, 0)
externalBGPPeerCIDRs := make(map[v1core.IPFamily][]string)
for family, extPeerSetName := range map[v1core.IPFamily]string{
v1core.IPv4Protocol: externalPeerSet,
v1core.IPv6Protocol: externalPeerSetV6} {
err := nrc.bgpServer.ListDefinedSet(context.Background(),
&gobgpapi.ListDefinedSetRequest{DefinedType: gobgpapi.DefinedType_NEIGHBOR, Name: extPeerSetName},
func(ds *gobgpapi.DefinedSet) {
currentDefinedSet = ds
})
if err != nil {
return externalBGPPeerCIDRs, err
}
if len(nrc.globalPeerRouters) > 0 {
for _, peer := range nrc.globalPeerRouters {
externalBgpPeers = append(externalBgpPeers, peer.Conf.NeighborAddress)
}
}
if len(nrc.nodePeerRouters) > 0 {
externalBgpPeers = append(externalBgpPeers, nrc.nodePeerRouters...)
}
if len(externalBgpPeers) == 0 {
return externalBGPPeerCIDRs, nil
}
for _, peer := range externalBgpPeers {
ip := net.ParseIP(peer)
if ip == nil {
klog.Warningf("wasn't able to parse the IP of peer: %s - skipping!", peer)
continue
}
if ip.To4() != nil {
// if we're not currently loading the IPv4 family, move on
if family != v1core.IPv4Protocol {
continue
}
externalBGPPeerCIDRs[family] = append(externalBGPPeerCIDRs[family], peer+"/32")
} else if ip.To16() != nil {
// if we're not currently loading the IPv6 family, move on
if family != v1core.IPv6Protocol {
continue
}
externalBGPPeerCIDRs[family] = append(externalBGPPeerCIDRs[family], peer+"/128")
}
}
if currentDefinedSet == nil {
eBGPPeerNS := &gobgpapi.DefinedSet{
DefinedType: gobgpapi.DefinedType_NEIGHBOR,
Name: extPeerSetName,
List: externalBGPPeerCIDRs[family],
}
err = nrc.bgpServer.AddDefinedSet(context.Background(),
&gobgpapi.AddDefinedSetRequest{DefinedSet: eBGPPeerNS})
if err != nil {
return externalBGPPeerCIDRs, err
}
}
}
return externalBGPPeerCIDRs, nil
}
// a slice of all peers is used as a match condition for reject statement of servicevipsdefinedset import policy
func (nrc *NetworkRoutingController) addAllBGPPeersDefinedSet(
iBGPPeerCIDRs, externalBGPPeerCIDRs map[v1core.IPFamily][]string) error {
for family, allPeerSetName := range map[v1core.IPFamily]string{
v1core.IPv4Protocol: allPeerSet,
v1core.IPv6Protocol: allPeerSetV6} {
var currentDefinedSet *gobgpapi.DefinedSet
err := nrc.bgpServer.ListDefinedSet(context.Background(),
&gobgpapi.ListDefinedSetRequest{DefinedType: gobgpapi.DefinedType_NEIGHBOR, Name: allPeerSetName},
func(ds *gobgpapi.DefinedSet) {
currentDefinedSet = ds
})
if err != nil {
return err
}
//nolint:gocritic // We intentionally append to a different array here so as to not change the passed
// in externalBGPPeerCIDRs
allBgpPeers := append(externalBGPPeerCIDRs[family], iBGPPeerCIDRs[family]...)
if currentDefinedSet == nil {
allPeerNS := &gobgpapi.DefinedSet{
DefinedType: gobgpapi.DefinedType_NEIGHBOR,
Name: allPeerSetName,
List: allBgpPeers,
}
err = nrc.bgpServer.AddDefinedSet(context.Background(),
&gobgpapi.AddDefinedSetRequest{DefinedSet: allPeerNS})
if err != nil {
return err
}
continue
}
toAdd := make([]string, 0)
toDelete := make([]string, 0)
for _, peer := range allBgpPeers {
add := true
for _, currentPeer := range currentDefinedSet.List {
if peer == currentPeer {
add = false
}
}
if add {
toAdd = append(toAdd, peer)
}
}
for _, currentPeer := range currentDefinedSet.List {
shouldDelete := true
for _, peer := range allBgpPeers {
if peer == currentPeer {
shouldDelete = false
}
}
if shouldDelete {
toDelete = append(toDelete, currentPeer)
}
}
allPeerNS := &gobgpapi.DefinedSet{
DefinedType: gobgpapi.DefinedType_NEIGHBOR,
Name: allPeerSetName,
List: toAdd,
}
err = nrc.bgpServer.AddDefinedSet(context.Background(), &gobgpapi.AddDefinedSetRequest{DefinedSet: allPeerNS})
if err != nil {
return err
}
allPeerNS = &gobgpapi.DefinedSet{
DefinedType: gobgpapi.DefinedType_NEIGHBOR,
Name: allPeerSetName,
List: toDelete,
}
err = nrc.bgpServer.DeleteDefinedSet(context.Background(),
&gobgpapi.DeleteDefinedSetRequest{DefinedSet: allPeerNS, All: false})
if err != nil {
return err
}
}
return nil
}
// BGP export policies are added so that following conditions are met:
//
// - by default export of all routes from the RIB to the neighbour's is denied, and explicitly statements are added
// to permit the desired routes to be exported
// - each node is allowed to advertise its assigned pod CIDR's to all of its iBGP peer neighbours with same
// ASN if --enable-ibgp=true
// - each node is allowed to advertise its assigned pod CIDR's to all of its external BGP peer neighbours
// only if --advertise-pod-cidr flag is set to true
// - each node is NOT allowed to advertise its assigned pod CIDR's to all of its external BGP peer neighbours
// only if --advertise-pod-cidr flag is set to false
// - each node is allowed to advertise service VIP's (cluster ip, load balancer ip, external IP) ONLY to external
// BGP peers
// - each node is NOT allowed to advertise service VIP's (cluster ip, load balancer ip, external IP) to
// iBGP peers
// - an option to allow overriding the next-hop-address with the outgoing ip for external bgp peers
func (nrc *NetworkRoutingController) addExportPolicies() error {
statements := make([]*gobgpapi.Statement, 0)
var bgpActions gobgpapi.Actions
if nrc.pathPrepend {
prependAsn, err := strconv.ParseUint(nrc.pathPrependAS, 10, asnMaxBitSize)
if err != nil {
return errors.New("Invalid value for kube-router.io/path-prepend.as: " + err.Error())
}
bgpActions = gobgpapi.Actions{
AsPrepend: &gobgpapi.AsPrependAction{
Asn: uint32(prependAsn),
Repeat: uint32(nrc.pathPrependCount),
},
}
}
if nrc.bgpEnableInternal {
actions := gobgpapi.Actions{
RouteAction: gobgpapi.RouteAction_ACCEPT,
}
if nrc.overrideNextHop {
actions.Nexthop = &gobgpapi.NexthopAction{Self: true}
}
// statement to represent the export policy to permit advertising node's IPv4 & IPv6 pod CIDRs
for _, podSet := range []string{podCIDRSet, podCIDRSetV6} {
statements = append(statements,
&gobgpapi.Statement{
Conditions: &gobgpapi.Conditions{
PrefixSet: &gobgpapi.MatchSet{
Type: gobgpapi.MatchSet_ANY,
Name: podSet,
},
NeighborSet: &gobgpapi.MatchSet{
Type: gobgpapi.MatchSet_ANY,
Name: iBGPPeerSet,
},
},
Actions: &actions,
})
}
}
if len(nrc.globalPeerRouters) > 0 || len(nrc.nodePeerRouters) > 0 {
bgpActions.RouteAction = gobgpapi.RouteAction_ACCEPT
if nrc.overrideNextHop {
bgpActions.Nexthop = &gobgpapi.NexthopAction{Self: true}
}
// set BGP communities for the routes advertised to peers for VIPs
if len(nrc.nodeCommunities) > 0 {
bgpActions.Community = &gobgpapi.CommunityAction{
Type: gobgpapi.CommunityAction_ADD,
Communities: nrc.nodeCommunities,
}
}
for _, peerSet := range []string{externalPeerSet, externalPeerSetV6} {
for _, serviceVIPSet := range []string{serviceVIPsSet, serviceVIPsSetV6} {
// statement to represent the export policy to permit advertising Service VIP's
// only to the global BGP peer or node specific BGP peer
statements = append(statements, &gobgpapi.Statement{
Conditions: &gobgpapi.Conditions{
PrefixSet: &gobgpapi.MatchSet{
Type: gobgpapi.MatchSet_ANY,
Name: serviceVIPSet,
},
NeighborSet: &gobgpapi.MatchSet{
Type: gobgpapi.MatchSet_ANY,
Name: peerSet,
},
},
Actions: &bgpActions,
})
}
if nrc.advertisePodCidr {
for _, podSet := range []string{podCIDRSet, podCIDRSetV6} {
// if we are configured to advertise POD CIDRs then add export policies for all of our IPv4 and IPv6
// peers for all IPv4 and IPv6 POD CIDRs
statements = append(statements, &gobgpapi.Statement{
Conditions: &gobgpapi.Conditions{
PrefixSet: &gobgpapi.MatchSet{
Type: gobgpapi.MatchSet_ANY,
Name: podSet,
},
NeighborSet: &gobgpapi.MatchSet{
Type: gobgpapi.MatchSet_ANY,
Name: peerSet,
},
},
Actions: &bgpActions,
})
}
}
}
}
definition := gobgpapi.Policy{
Name: kubeRouterExportPolicy,
Statements: statements,
}
policyAlreadyExists := false
checkExistingPolicy := func(existingPolicy *gobgpapi.Policy) {
if existingPolicy.Name == kubeRouterExportPolicy {
policyAlreadyExists = true
}
}
err := nrc.bgpServer.ListPolicy(context.Background(), &gobgpapi.ListPolicyRequest{}, checkExistingPolicy)
if err != nil {
return errors.New("Failed to verify if kube-router BGP export policy exists: " + err.Error())
}
if !policyAlreadyExists {
err = nrc.bgpServer.AddPolicy(context.Background(), &gobgpapi.AddPolicyRequest{Policy: &definition})
if err != nil {
return errors.New("Failed to add policy: " + err.Error())
}
}
policyAssignmentExists := false
checkExistingPolicyAssignment := func(existingPolicyAssignment *gobgpapi.PolicyAssignment) {
for _, policy := range existingPolicyAssignment.Policies {
if policy.Name == kubeRouterExportPolicy {
policyAssignmentExists = true
}
}
}
err = nrc.bgpServer.ListPolicyAssignment(context.Background(),
&gobgpapi.ListPolicyAssignmentRequest{Name: "global", Direction: gobgpapi.PolicyDirection_EXPORT},
checkExistingPolicyAssignment)
if err != nil {
return errors.New("Failed to verify if kube-router BGP export policy assignment exists: " + err.Error())
}
policyAssignment := gobgpapi.PolicyAssignment{
Name: "global",
Direction: gobgpapi.PolicyDirection_EXPORT,
Policies: []*gobgpapi.Policy{&definition},
DefaultAction: gobgpapi.RouteAction_REJECT,
}
if !policyAssignmentExists {
err = nrc.bgpServer.AddPolicyAssignment(context.Background(),
&gobgpapi.AddPolicyAssignmentRequest{Assignment: &policyAssignment})
if err != nil {
return errors.New("Failed to add policy assignment: " + err.Error())
}
}
return nil
}
// BGP import policies are added so that the following conditions are met:
// - do not import Service VIPs advertised from any peers, instead each kube-router originates and injects
// Service VIPs into local rib.
func (nrc *NetworkRoutingController) addImportPolicies() error {
statements := make([]*gobgpapi.Statement, 0)
actions := gobgpapi.Actions{
RouteAction: gobgpapi.RouteAction_REJECT,
}
for _, peerSet := range []string{allPeerSet, allPeerSetV6} {
for _, vipSet := range []string{serviceVIPsSet, serviceVIPsSetV6} {
statements = append(statements, &gobgpapi.Statement{
Conditions: &gobgpapi.Conditions{
PrefixSet: &gobgpapi.MatchSet{
Type: gobgpapi.MatchSet_ANY,
Name: vipSet,
},
NeighborSet: &gobgpapi.MatchSet{
Type: gobgpapi.MatchSet_ANY,
Name: peerSet,
},
},
Actions: &actions,
})
}
statements = append(statements, &gobgpapi.Statement{
Conditions: &gobgpapi.Conditions{
PrefixSet: &gobgpapi.MatchSet{
Type: gobgpapi.MatchSet_ANY,
Name: defaultRouteSet,
},
NeighborSet: &gobgpapi.MatchSet{
Type: gobgpapi.MatchSet_ANY,
Name: peerSet,
},
},
Actions: &actions,
})
if len(nrc.nodeCustomImportRejectIPNets) > 0 {
statements = append(statements, &gobgpapi.Statement{
Conditions: &gobgpapi.Conditions{
PrefixSet: &gobgpapi.MatchSet{
Name: customImportRejectSet,
Type: gobgpapi.MatchSet_ANY,
},
NeighborSet: &gobgpapi.MatchSet{
Type: gobgpapi.MatchSet_ANY,
Name: peerSet,
},
},
Actions: &actions,
})
}
}
definition := gobgpapi.Policy{
Name: kubeRouterImportPolicy,
Statements: statements,
}
policyAlreadyExists := false
checkExistingPolicy := func(existingPolicy *gobgpapi.Policy) {
if existingPolicy.Name == kubeRouterImportPolicy {
policyAlreadyExists = true
}
}
err := nrc.bgpServer.ListPolicy(context.Background(), &gobgpapi.ListPolicyRequest{}, checkExistingPolicy)
if err != nil {
return errors.New("Failed to verify if kube-router BGP import policy exists: " + err.Error())
}
if !policyAlreadyExists {
err = nrc.bgpServer.AddPolicy(context.Background(), &gobgpapi.AddPolicyRequest{Policy: &definition})
if err != nil {
return errors.New("Failed to add policy: " + err.Error())
}
}
policyAssignmentExists := false
checkExistingPolicyAssignment := func(existingPolicyAssignment *gobgpapi.PolicyAssignment) {
for _, policy := range existingPolicyAssignment.Policies {
if policy.Name == kubeRouterImportPolicy {
policyAssignmentExists = true
}
}
}
err = nrc.bgpServer.ListPolicyAssignment(context.Background(),
&gobgpapi.ListPolicyAssignmentRequest{Name: "global", Direction: gobgpapi.PolicyDirection_IMPORT},
checkExistingPolicyAssignment)
if err != nil {
return errors.New("Failed to verify if kube-router BGP import policy assignment exists: " + err.Error())
}
policyAssignment := gobgpapi.PolicyAssignment{
Name: "global",
Direction: gobgpapi.PolicyDirection_IMPORT,
Policies: []*gobgpapi.Policy{&definition},
DefaultAction: gobgpapi.RouteAction_ACCEPT,
}
if !policyAssignmentExists {
err = nrc.bgpServer.AddPolicyAssignment(context.Background(),
&gobgpapi.AddPolicyAssignmentRequest{Assignment: &policyAssignment})
if err != nil {
return errors.New("Failed to add policy assignment: " + err.Error())
}
}
return nil
}