Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions calico-vpp-agent/cni/cni_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -538,10 +538,17 @@ func (s *Server) createRedirectToHostRules() (uint32, error) {
return types.InvalidID, fmt.Errorf("no main interface found")
}
for _, rule := range config.GetCalicoVppInitialConfig().RedirectToHostRules {
mainInterfaceAddress := mainInterface.GetAddress(vpplink.IPFamilyFromIP(rule.IP))
if mainInterfaceAddress == nil {
return types.InvalidID, fmt.Errorf("error installing rule %v no address found on uplink", rule)
}
err = s.vpp.AddSessionRedirect(&types.SessionRedirect{
FiveTuple: types.NewDst3Tuple(rule.Proto, net.ParseIP(rule.IP), rule.Port),
FiveTuple: types.NewDst3Tuple(rule.Proto, rule.IP, rule.Port),
TableIndex: index,
}, &types.RoutePath{Gw: config.VppHostPuntFakeGatewayAddress, SwIfIndex: mainInterface.TapSwIfIndex})
}, &types.RoutePath{
Gw: mainInterfaceAddress.IP,
SwIfIndex: mainInterface.TapSwIfIndex,
})
if err != nil {
return types.InvalidID, err
}
Expand Down
20 changes: 18 additions & 2 deletions calico-vpp-agent/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,19 @@ func FullyQualified(addr net.IP) *net.IPNet {
}
}

var (
ErrNoNodeIPv4 = errors.New("no ip4 address for node")
ErrNoNodeIPv6 = errors.New("no ip6 address for node")
)

func IsMissingNodeIP(err error) bool {
if err == nil {
return false
}
cause := errors.Cause(err)
return cause == ErrNoNodeIPv4 || cause == ErrNoNodeIPv6
}

const (
aggregatedPrefixSetBaseName = "aggregated"
hostPrefixSetBaseName = "host"
Expand Down Expand Up @@ -258,7 +271,7 @@ func MakePath(prefix string, isWithdrawal bool, nodeIPv4 *net.IP, nodeIPv6 *net.

if ipNet.IP.To4() != nil {
if nodeIPv4 == nil {
return nil, fmt.Errorf("no ip4 address for node")
return nil, ErrNoNodeIPv4
}
family = &BgpFamilyUnicastIPv4
if vni != 0 {
Expand All @@ -267,6 +280,9 @@ func MakePath(prefix string, isWithdrawal bool, nodeIPv4 *net.IP, nodeIPv6 *net.
var nhAttr *apb.Any

if *config.GetCalicoVppFeatureGates().SRv6Enabled {
if nodeIPv6 == nil {
return nil, ErrNoNodeIPv6
}
nhAttr, err = apb.New(&bgpapi.NextHopAttribute{
NextHop: nodeIPv6.String(),
})
Expand All @@ -281,7 +297,7 @@ func MakePath(prefix string, isWithdrawal bool, nodeIPv4 *net.IP, nodeIPv6 *net.
attrs = append(attrs, nhAttr)
} else {
if nodeIPv6 == nil {
return nil, fmt.Errorf("no ip6 address for node")
return nil, ErrNoNodeIPv6
}
family = &BgpFamilyUnicastIPv6
if vni != 0 {
Expand Down
21 changes: 17 additions & 4 deletions calico-vpp-agent/routing/bgp_watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -543,10 +543,14 @@ func (s *Server) WatchBGPPath(t *tomb.Tomb) error {
peer := localPeer.Peer
filters := localPeer.BGPFilterNames
// create a neighbor set to apply filter only on specific peer using a global policy
prefixLen := "/32"
if ip := net.ParseIP(peer.Conf.NeighborAddress); ip != nil && ip.To4() == nil {
prefixLen = "/128"
}
neighborSet := &bgpapi.DefinedSet{
Name: peer.Conf.NeighborAddress + "neighbor",
DefinedType: bgpapi.DefinedType_NEIGHBOR,
List: []string{peer.Conf.NeighborAddress + "/32"},
List: []string{peer.Conf.NeighborAddress + prefixLen},
}
err := s.BGPServer.AddDefinedSet(context.Background(), &bgpapi.AddDefinedSetRequest{
DefinedSet: neighborSet,
Expand Down Expand Up @@ -580,9 +584,18 @@ func (s *Server) WatchBGPPath(t *tomb.Tomb) error {
if err != nil {
return errors.Wrapf(err, "error cleaning peer filters up")
}
err = s.BGPServer.DeleteDefinedSet(context.Background(), &bgpapi.DeleteDefinedSetRequest{DefinedSet: s.bgpPeers[addr].NeighborSet, All: true})
if err != nil {
return errors.Wrapf(err, "error deleting prefix set")
if s.bgpPeers[addr] == nil {
s.log.Warnf("Trying to delete unknown BGP peer %s", addr)
} else if s.bgpPeers[addr].NeighborSet == nil {
s.log.Warnf("Trying to delete BGP peer %s with empty NeighborSet", addr)
} else {
err = s.BGPServer.DeleteDefinedSet(context.Background(), &bgpapi.DeleteDefinedSetRequest{
DefinedSet: s.bgpPeers[addr].NeighborSet,
All: true,
})
if err != nil {
return errors.Wrapf(err, "error deleting prefix set")
}
}
err := s.BGPServer.DeletePeer(
context.Background(),
Expand Down
48 changes: 42 additions & 6 deletions calico-vpp-agent/routing/routing_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (

"github.com/projectcalico/vpp-dataplane/v3/calico-vpp-agent/common"
"github.com/projectcalico/vpp-dataplane/v3/calico-vpp-agent/watchers"
"github.com/projectcalico/vpp-dataplane/v3/config"
"github.com/projectcalico/vpp-dataplane/v3/vpplink"
)

Expand Down Expand Up @@ -114,17 +115,28 @@ func (s *Server) ServeRouting(t *tomb.Tomb) (err error) {
}

for t.Alive() {
globalConfig, err := s.getGoBGPGlobalConfig()
nodeIP4, nodeIP6 := common.GetBGPSpecAddresses(s.nodeBGPSpec)
globalConfig, err := s.getGoBGPGlobalConfig(*config.BGPServerMode)
if err != nil {
return fmt.Errorf("cannot get global configuration: %v", err)
}

err = s.BGPServer.StartBgp(context.Background(), &bgpapi.StartBgpRequest{Global: globalConfig})
if err != nil {
if err != nil && *config.BGPServerMode == config.BGPServerModeDualStack && nodeIP4 != nil {
s.log.Warnf("Failed to start BGP server in dualStack mode: %v. Retrying with IPv4-only listener", err)
globalConfig, err = s.getGoBGPGlobalConfig(config.BGPServerModeV4Only)
if err != nil {
return errors.Wrap(err, "cannot get IPv4-only BGP configuration for fallback")
}
err = s.BGPServer.StartBgp(context.Background(), &bgpapi.StartBgpRequest{Global: globalConfig})
if err != nil {
return errors.Wrap(err, "failed to start BGP server after IPv4-only fallback")
}
s.log.Warn("BGP server started in degraded IPv4-only mode because IPv6 listener failed")
} else if err != nil {
return errors.Wrap(err, "failed to start BGP server")
}

nodeIP4, nodeIP6 := common.GetBGPSpecAddresses(s.nodeBGPSpec)
if nodeIP4 != nil {
err = s.initialPolicySetting(false /* isv6 */)
if err != nil {
Expand Down Expand Up @@ -176,7 +188,7 @@ func (s *Server) getLogSeverityScreen() string {
return s.BGPConf.LogSeverityScreen
}

func (s *Server) getGoBGPGlobalConfig() (*bgpapi.Global, error) {
func (s *Server) getGoBGPGlobalConfig(mode config.BGPServerModeType) (*bgpapi.Global, error) {
var routerID string
listenAddresses := make([]string, 0)
asn := s.nodeBGPSpec.ASNumber
Expand All @@ -185,11 +197,25 @@ func (s *Server) getGoBGPGlobalConfig() (*bgpapi.Global, error) {
}

nodeIP4, nodeIP6 := common.GetBGPSpecAddresses(s.nodeBGPSpec)
if nodeIP6 != nil {
useIP4 := nodeIP4 != nil
useIP6 := nodeIP6 != nil

switch mode {
case config.BGPServerModeDualStack:
case config.BGPServerModeV4Only:
useIP6 = false
if !useIP4 {
return nil, fmt.Errorf("BGP server mode set to v4Only but no IPv4 node address configured")
}
default:
return nil, fmt.Errorf("unsupported BGP server mode %q", mode)
}

if useIP6 {
routerID = nodeIP6.String()
listenAddresses = append(listenAddresses, routerID)
}
if nodeIP4 != nil {
if useIP4 {
routerID = nodeIP4.String() // Override v6 ID if v4 is available
listenAddresses = append(listenAddresses, routerID)
}
Expand Down Expand Up @@ -232,6 +258,11 @@ func (s *Server) announceLocalAddress(addr *net.IPNet, vni uint32) error {
nodeIP4, nodeIP6 := common.GetBGPSpecAddresses(s.nodeBGPSpec)
path, err := common.MakePath(addr.String(), false /* isWithdrawal */, nodeIP4, nodeIP6, vni, uint32(*s.BGPConf.ASNumber))
if err != nil {
if common.IsMissingNodeIP(err) {
s.log.WithError(err).Warnf("Skipping BGP announce for %s: node IP missing", addr.String())
s.localAddressMap[addr.String()] = localAddress{ipNet: addr, vni: vni}
return nil
}
return errors.Wrap(err, "error making path to announce")
}
s.localAddressMap[addr.String()] = localAddress{ipNet: addr, vni: vni}
Expand All @@ -247,6 +278,11 @@ func (s *Server) withdrawLocalAddress(addr *net.IPNet, vni uint32) error {
nodeIP4, nodeIP6 := common.GetBGPSpecAddresses(s.nodeBGPSpec)
path, err := common.MakePath(addr.String(), true /* isWithdrawal */, nodeIP4, nodeIP6, vni, uint32(*s.BGPConf.ASNumber))
if err != nil {
if common.IsMissingNodeIP(err) {
s.log.WithError(err).Warnf("Skipping BGP withdraw for %s: node IP missing", addr.String())
delete(s.localAddressMap, addr.String())
return nil
}
return errors.Wrap(err, "error making path to withdraw")
}
delete(s.localAddressMap, addr.String())
Expand Down
8 changes: 8 additions & 0 deletions calico-vpp-agent/watchers/prefix_watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ func (w *PrefixWatcher) WatchPrefix(t *tomb.Tomb) error {
ip4, ip6 := common.GetBGPSpecAddresses(w.nodeBGPSpec)
path, err := common.MakePath(prefix, false /* isWithdrawal */, ip4, ip6, 0, 0)
if err != nil {
if common.IsMissingNodeIP(err) {
w.log.WithError(err).Warnf("Skipping prefix announcement for %s: node IP missing", prefix)
continue
}
return errors.Wrap(err, "error making new path for assigned prefix")
}
toAdd = append(toAdd, path)
Expand All @@ -90,6 +94,10 @@ func (w *PrefixWatcher) WatchPrefix(t *tomb.Tomb) error {
ip4, ip6 := common.GetBGPSpecAddresses(w.nodeBGPSpec)
path, err := common.MakePath(p, true /* isWithdrawal */, ip4, ip6, 0, 0)
if err != nil {
if common.IsMissingNodeIP(err) {
w.log.WithError(err).Warnf("Skipping prefix withdrawal for %s: node IP missing", p)
continue
}
return errors.Wrap(err, "error making new path for removed prefix")
}
toRemove = append(toRemove, path)
Expand Down
51 changes: 41 additions & 10 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,22 @@ const (
BaseVppSideHardwareAddress = "02:ca:11:c0:fd:00"
)

type BGPServerModeType string

const (
BGPServerModeDualStack BGPServerModeType = "dualStack"
BGPServerModeV4Only BGPServerModeType = "v4Only"
)

var (
// fake constants for place where we need a pointer to true or false
True = true
False = false

NodeName = RequiredStringEnvVar("NODENAME")
LogLevel = EnvVar("CALICOVPP_LOG_LEVEL", logrus.InfoLevel, logrus.ParseLevel)
BGPLogLevel = EnvVar("CALICOVPP_BGP_LOG_LEVEL", apipb.SetLogLevelRequest_INFO, BGPLogLevelParse)
NodeName = RequiredStringEnvVar("NODENAME")
LogLevel = EnvVar("CALICOVPP_LOG_LEVEL", logrus.InfoLevel, logrus.ParseLevel)
BGPLogLevel = EnvVar("CALICOVPP_BGP_LOG_LEVEL", apipb.SetLogLevelRequest_INFO, BGPLogLevelParse)
BGPServerMode = EnvVar("CALICOVPP_BGP_SERVER_MODE", BGPServerModeDualStack, BGPServerModeParse)

ServiceCIDRs = PrefixListEnvVar("SERVICE_PREFIX")
IPSecIkev2Psk = StringEnvVar("CALICOVPP_IPSEC_IKEV2_PSK", "")
Expand Down Expand Up @@ -113,7 +121,8 @@ var (
DefaultHookScript string

/* Run this before getLinuxConfig() in case this is a script
* that's responsible for creating the interface */
* that's responsible for creating the interface.
* Also captures host udev ID_NET_NAME_* properties before driver unbind. */
HookScriptBeforeIfRead = StringEnvVar("CALICOVPP_HOOK_BEFORE_IF_READ", DefaultHookScript) // InitScriptTemplate
/* Bash script template run just after getting config
from $CALICOVPP_INTERFACE & before starting VPP */
Expand All @@ -133,11 +142,17 @@ var (
HookScriptVppErrored,
}

Info = &VppManagerInfo{}
Info = &VppManagerInfo{
UplinkStatuses: make(map[string]UplinkStatus),
PhysicalNets: make(map[string]PhysicalNetwork),
}

// VppHostPuntFakeGatewayAddress is the fake gateway we use with a static neighbor
// in the punt table to route punted packets to the host
VppHostPuntFakeGatewayAddress = net.ParseIP("169.254.0.1")
// VppsideTap0Address is the IP address we add to the tap0
// so that it can receive ipv4 packets
VppsideTap0Address = PrefixEnvVar(
"CALICOVPP_TAP0_ADDR",
MustParseCIDR("169.254.0.1/32"),
)
)

func RunHook(hookScript *string, hookName string, params *VppManagerParams, log *logrus.Logger) {
Expand All @@ -150,7 +165,7 @@ func RunHook(hookScript *string, hookName string, params *VppManagerParams, log
return
}

cmd := exec.Command("/bin/bash", "-c", template, hookName)
cmd := exec.Command("/bin/bash", "-c", template, hookName, params.UplinksSpecs[0].InterfaceName)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
Expand Down Expand Up @@ -271,7 +286,7 @@ func (u *UplinkInterfaceSpec) String() string {

type RedirectToHostRulesConfigType struct {
Port uint16 `json:"port,omitempty"`
IP string `json:"ip,omitempty"`
IP net.IP `json:"ip,omitempty"`
/* "tcp", "udp",... */
Proto types.IPProto `json:"proto,omitempty"`
}
Expand All @@ -280,6 +295,7 @@ type CalicoVppDebugConfigType struct {
ServicesEnabled *bool `json:"servicesEnabled,omitempty"`
GSOEnabled *bool `json:"gsoEnabled,omitempty"`
SpreadTxQueuesOnWorkers *bool `json:"spreadTxQueuesOnWorkers,omitempty"`
EnableUdevNetNameRules *bool `json:"enableUdevNetNameRules,omitempty"`
}

func (cfg *CalicoVppDebugConfigType) String() string {
Expand All @@ -297,6 +313,9 @@ func (cfg *CalicoVppDebugConfigType) Validate() (err error) {
if cfg.SpreadTxQueuesOnWorkers == nil {
cfg.SpreadTxQueuesOnWorkers = &False
}
if cfg.EnableUdevNetNameRules == nil {
cfg.EnableUdevNetNameRules = &True
}
return
}

Expand Down Expand Up @@ -567,6 +586,17 @@ type UplinkStatus struct {
// FakeNextHopIP6 is the computed next hop for v6 routes added
// in linux to (ServiceCIDR, podCIDR, etc...) towards this interface
FakeNextHopIP6 net.IP

UplinkAddresses []*net.IPNet
}

func (uplinkStatus *UplinkStatus) GetAddress(ipFamily vpplink.IPFamily) *net.IPNet {
for _, addr := range uplinkStatus.UplinkAddresses {
if vpplink.IPFamilyFromIPNet(addr) == ipFamily {
return addr
}
}
return nil
}

type PhysicalNetwork struct {
Expand Down Expand Up @@ -643,6 +673,7 @@ type LinuxInterfaceState struct {
IsUp bool
Addresses []netlink.Addr
Routes []netlink.Route
Neighbors []netlink.Neigh
HardwareAddr net.HardwareAddr
PromiscOn bool
NumTxQueues int
Expand Down
24 changes: 23 additions & 1 deletion config/config_parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,18 @@ func prefixParser(value string) (net.IPNet, error) {
func RequiredPrefixEnvVar(varName string) *net.IPNet {
return RequiredEnvVar(varName, net.IPNet{}, prefixParser)
}
func PrefixEnvVar(varName string) *net.IPNet { return EnvVar(varName, net.IPNet{}, prefixParser) }

func PrefixEnvVar(varName string, defaultValue *net.IPNet) *net.IPNet {
return EnvVar(varName, *defaultValue, prefixParser)
}

func MustParseCIDR(str string) *net.IPNet {
_, cidr, err := net.ParseCIDR(str)
if err != nil {
logrus.Fatalf("error parsing %s as cidr %v", str, err)
}
return cidr
}

func prefixListParser(value string) ([]*net.IPNet, error) {
chunks := strings.Split(value, ",")
Expand Down Expand Up @@ -257,3 +268,14 @@ func BGPLogLevelParse(lvl string) (apipb.SetLogLevelRequest_Level, error) {
var l apipb.SetLogLevelRequest_Level
return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
}

func BGPServerModeParse(mode string) (BGPServerModeType, error) {
switch strings.ToLower(mode) {
case strings.ToLower(string(BGPServerModeDualStack)):
return BGPServerModeDualStack, nil
case "v4only":
return BGPServerModeV4Only, nil
}

return BGPServerModeDualStack, fmt.Errorf("not a valid BGP server mode: %q", mode)
}
Loading