From a76c7651bcbbefc412d042edbc3053fdd09ff6d4 Mon Sep 17 00:00:00 2001 From: Nathan Skrzypczak Date: Tue, 9 Apr 2024 14:33:36 +0200 Subject: [PATCH] Support setting failsafepolicy net This patch adds support for setting the .net property when defining the failsafepolicies in felixconfig. This is a not so well documented feature that slipped through when aiming for feature parity. For reference, here is the documentation for [0] felixconfig. - setting failsafeInboundHostPorts to e.g. {Net: "10.0.0.0/24", ...} will add a filter for inbound packets for their source prefix to match 10.0.0/24. - setting failsafeOutboundHostPorts to e.g. {Net: "20.0.0.0/24". ...} will add a filter for outbound packets for their destination prefix to match 20.0.0/24 [0] https://docs.tigera.io/calico/latest/reference/resources/felixconfig#spec Signed-off-by: Nathan Skrzypczak --- calico-vpp-agent/policy/policy_server.go | 115 +++++++++++++---------- 1 file changed, 63 insertions(+), 52 deletions(-) diff --git a/calico-vpp-agent/policy/policy_server.go b/calico-vpp-agent/policy/policy_server.go index 37488b93..73b156d9 100644 --- a/calico-vpp-agent/policy/policy_server.go +++ b/calico-vpp-agent/policy/policy_server.go @@ -1713,29 +1713,83 @@ func (s *Server) createEndpointToHostPolicy( /*may be return*/ ) (err error) { return nil } +// createFailSafePolicies ensures the failsafe policies defined in the Felixconfiguration exist in VPP. +// check https://github.com/projectcalico/calico/blob/master/felix/rules/static.go :: failsafeInChain for the linux implementation +// To be noted. This does not implement the doNotTrack case as we do not yet support doNotTrack policies. func (s *Server) createFailSafePolicies() (err error) { failSafePol := &Policy{ Policy: &types.Policy{}, VppID: types.InvalidID, } - var failSafeInboundRules, failSafeOutboundRules []*Rule if len(s.felixConfig.FailsafeInboundHostPorts) != 0 { - failSafeInboundRules, err = getfailSafeRules(s.felixConfig.FailsafeInboundHostPorts) - if err != nil { - return err + for _, protoPort := range s.felixConfig.FailsafeInboundHostPorts { + protocol, err := parseProtocol(&proto.Protocol{NumberOrName: &proto.Protocol_Name{Name: protoPort.Protocol}}) + if err != nil { + s.log.WithError(err).Error("Failed to parse protocol in inbound failsafe rule. Skipping failsafe rule") + continue + } + rule := &Rule{ + VppID: types.InvalidID, + RuleID: fmt.Sprintf("failsafe-in-%s-%s-%d", protoPort.Net, protoPort.Protocol, protoPort.Port), + Rule: &types.Rule{ + Action: types.ActionAllow, + // Ports are always filtered on the destination of packets + DstPortRange: []types.PortRange{{First: protoPort.Port, Last: protoPort.Port}}, + Filters: []types.RuleFilter{{ + ShouldMatch: true, + Type: types.CapoFilterProto, + Value: int(protocol), + }}, + }, + } + if protoPort.Net != "" { + _, protoPortNet, err := net.ParseCIDR(protoPort.Net) + if err != nil { + s.log.WithError(err).Error("Failed to parse CIDR in inbound failsafe rule. Skipping failsafe rule") + continue + } + // Inbound packets are checked for where they come FROM + rule.Rule.SrcNet = append(rule.Rule.SrcNet, *protoPortNet) + } + failSafePol.InboundRules = append(failSafePol.InboundRules, rule) } } if len(s.felixConfig.FailsafeOutboundHostPorts) != 0 { - failSafeOutboundRules, err = getfailSafeRules(s.felixConfig.FailsafeOutboundHostPorts) - if err != nil { - return err + for _, protoPort := range s.felixConfig.FailsafeOutboundHostPorts { + protocol, err := parseProtocol(&proto.Protocol{NumberOrName: &proto.Protocol_Name{Name: protoPort.Protocol}}) + if err != nil { + s.log.WithError(err).Error("Failed to parse protocol in outbound failsafe rule. Skipping failsafe rule") + continue + } + rule := &Rule{ + VppID: types.InvalidID, + RuleID: fmt.Sprintf("failsafe-out-%s-%s-%d", protoPort.Net, protoPort.Protocol, protoPort.Port), + Rule: &types.Rule{ + Action: types.ActionAllow, + // Ports are always filtered on the destination of packets + DstPortRange: []types.PortRange{{First: protoPort.Port, Last: protoPort.Port}}, + Filters: []types.RuleFilter{{ + ShouldMatch: true, + Type: types.CapoFilterProto, + Value: int(protocol), + }}, + }, + } + if protoPort.Net != "" { + _, protoPortNet, err := net.ParseCIDR(protoPort.Net) + if err != nil { + s.log.WithError(err).Error("Failed to parse CIDR in outbound failsafe rule. Skipping failsafe rule") + continue + } + // Outbound packets are checked for where they go TO + rule.Rule.DstNet = append(rule.Rule.DstNet, *protoPortNet) + } + failSafePol.OutboundRules = append(failSafePol.OutboundRules, rule) } } - failSafePol.InboundRules = failSafeInboundRules - failSafePol.OutboundRules = failSafeOutboundRules if s.failSafePolicy == nil { err = failSafePol.Create(s.vpp, nil) @@ -1750,46 +1804,3 @@ func (s *Server) createFailSafePolicies() (err error) { s.log.Infof("Created failsafe policy with ID %+v", s.failSafePolicy.VppID) return nil } - -func getProtocolRules(protocolName string, failSafe []felixConfig.ProtoPort) (*Rule, error) { - portRanges := []types.PortRange{} - - for _, protoPort := range failSafe { - if protoPort.Protocol == protocolName { - portRanges = append(portRanges, types.PortRange{ - First: protoPort.Port, - Last: protoPort.Port, - }) - } - } - protocol, err := parseProtocol(&proto.Protocol{NumberOrName: &proto.Protocol_Name{Name: protocolName}}) - if err != nil { - return nil, err - } - r_failsafe := &Rule{ - VppID: types.InvalidID, - RuleID: "failsafe" + protocolName, - Rule: &types.Rule{ - Action: types.ActionAllow, - DstPortRange: portRanges, - Filters: []types.RuleFilter{{ - ShouldMatch: true, - Type: types.CapoFilterProto, - Value: int(protocol), - }}, - }, - } - return r_failsafe, nil -} - -func getfailSafeRules(failSafe []felixConfig.ProtoPort) ([]*Rule, error) { - r_failsafe_tcp, err := getProtocolRules("tcp", failSafe) - if err != nil { - return nil, errors.Errorf("failsafe has wrong format") - } - r_failsafe_udp, err := getProtocolRules("udp", failSafe) - if err != nil { - return nil, errors.Errorf("failsafe has wrong format") - } - return []*Rule{r_failsafe_tcp, r_failsafe_udp}, nil -}