From e4be8cf6cda4b85df0e5f83586cc0b679a9ef187 Mon Sep 17 00:00:00 2001 From: gran Date: Tue, 6 Apr 2021 11:23:47 +0800 Subject: [PATCH] Support AntreaNetworkPolicy reject action in Traceflow --- build/yamls/antrea-aks.yml | 2 + build/yamls/antrea-eks.yml | 2 + build/yamls/antrea-gke.yml | 2 + build/yamls/antrea-ipsec.yml | 2 + build/yamls/antrea.yml | 2 + build/yamls/base/crds.yml | 2 + pkg/agent/controller/traceflow/packetin.go | 70 +++++++++---------- pkg/apis/crd/v1alpha1/types.go | 11 +++ .../crd/v1alpha1/zz_generated.deepcopy.go | 9 ++- test/e2e/traceflow_test.go | 69 +++++++++++++++++- 10 files changed, 131 insertions(+), 40 deletions(-) diff --git a/build/yamls/antrea-aks.yml b/build/yamls/antrea-aks.yml index 212c669de3e..2139f0fb22a 100644 --- a/build/yamls/antrea-aks.yml +++ b/build/yamls/antrea-aks.yml @@ -1361,6 +1361,8 @@ spec: type: string pod: type: string + rejectAction: + type: string translatedDstIP: type: string translatedSrcIP: diff --git a/build/yamls/antrea-eks.yml b/build/yamls/antrea-eks.yml index 4fbf4b00c54..a25379db5b5 100644 --- a/build/yamls/antrea-eks.yml +++ b/build/yamls/antrea-eks.yml @@ -1361,6 +1361,8 @@ spec: type: string pod: type: string + rejectAction: + type: string translatedDstIP: type: string translatedSrcIP: diff --git a/build/yamls/antrea-gke.yml b/build/yamls/antrea-gke.yml index f473c28beb6..1b0a7c549e3 100644 --- a/build/yamls/antrea-gke.yml +++ b/build/yamls/antrea-gke.yml @@ -1361,6 +1361,8 @@ spec: type: string pod: type: string + rejectAction: + type: string translatedDstIP: type: string translatedSrcIP: diff --git a/build/yamls/antrea-ipsec.yml b/build/yamls/antrea-ipsec.yml index 43b404e2662..2270454612f 100644 --- a/build/yamls/antrea-ipsec.yml +++ b/build/yamls/antrea-ipsec.yml @@ -1361,6 +1361,8 @@ spec: type: string pod: type: string + rejectAction: + type: string translatedDstIP: type: string translatedSrcIP: diff --git a/build/yamls/antrea.yml b/build/yamls/antrea.yml index 7aab91ef04c..80b2d33da17 100644 --- a/build/yamls/antrea.yml +++ b/build/yamls/antrea.yml @@ -1361,6 +1361,8 @@ spec: type: string pod: type: string + rejectAction: + type: string translatedDstIP: type: string translatedSrcIP: diff --git a/build/yamls/base/crds.yml b/build/yamls/base/crds.yml index 78b2e1f277e..3842e1bf435 100644 --- a/build/yamls/base/crds.yml +++ b/build/yamls/base/crds.yml @@ -205,6 +205,8 @@ spec: type: string action: type: string + rejectAction: + type: string pod: type: string dstMAC: diff --git a/pkg/agent/controller/traceflow/packetin.go b/pkg/agent/controller/traceflow/packetin.go index b294ba3b636..467620abd30 100644 --- a/pkg/agent/controller/traceflow/packetin.go +++ b/pkg/agent/controller/traceflow/packetin.go @@ -32,6 +32,7 @@ import ( "github.com/vmware-tanzu/antrea/pkg/agent/openflow" crdv1alpha1 "github.com/vmware-tanzu/antrea/pkg/apis/crd/v1alpha1" binding "github.com/vmware-tanzu/antrea/pkg/ovs/openflow" + "github.com/vmware-tanzu/antrea/pkg/util/ip" ) func (c *Controller) HandlePacketIn(pktIn *ofctrl.PacketIn) error { @@ -69,23 +70,36 @@ func (c *Controller) HandlePacketIn(pktIn *ofctrl.PacketIn) error { func (c *Controller) parsePacketIn(pktIn *ofctrl.PacketIn) (*crdv1alpha1.Traceflow, *crdv1alpha1.NodeResult, error) { matchers := pktIn.GetMatches() - var match *ofctrl.MatchField // Get data plane tag. // Directly read data plane tag from packet. - var tag uint8 + var err error + var tag, prot uint8 + var ctNwDst, ipDst string if pktIn.Data.Ethertype == protocol.IPv4_MSG { ipPacket, ok := pktIn.Data.Data.(*protocol.IPv4) if !ok { return nil, nil, errors.New("invalid traceflow IPv4 packet") } tag = ipPacket.DSCP + prot = ipPacket.Protocol + ctNwDst, err = getCTDstValue(matchers, false) + if err != nil { + return nil, nil, err + } + ipDst = ipPacket.NWDst.String() } else if pktIn.Data.Ethertype == protocol.IPv6_MSG { ipv6Packet, ok := pktIn.Data.Data.(*protocol.IPv6) if !ok { return nil, nil, errors.New("invalid traceflow IPv6 packet") } tag = ipv6Packet.TrafficClass >> 2 + prot = ipv6Packet.NextHeader + ctNwDst, err = getCTDstValue(matchers, true) + if err != nil { + return nil, nil, err + } + ipDst = ipv6Packet.NWDst.String() } else { return nil, nil, fmt.Errorf("unsupported traceflow packet Ethertype: %d", pktIn.Data.Ethertype) } @@ -129,32 +143,6 @@ func (c *Controller) parsePacketIn(pktIn *ofctrl.PacketIn) (*crdv1alpha1.Tracefl } // Collect Service DNAT. - ctNwDst := "" - ipDst := "" - switch pktIn.Data.Ethertype { - case protocol.IPv4_MSG: - ipPacket, ok := pktIn.Data.Data.(*protocol.IPv4) - if !ok { - return nil, nil, errors.New("invalid traceflow IPv4 packet") - } - ctNwDst, err = getCTDstValue(matchers, false) - if err != nil { - return nil, nil, err - } - ipDst = ipPacket.NWDst.String() - case protocol.IPv6_MSG: - ipPacket, ok := pktIn.Data.Data.(*protocol.IPv6) - if !ok { - return nil, nil, errors.New("invalid traceflow IPv6 packet") - } - ctNwDst, err = getCTDstValue(matchers, true) - if err != nil { - return nil, nil, err - } - ipDst = ipPacket.NWDst.String() - default: - return nil, nil, fmt.Errorf("unsupported traceflow packet ether type %d", pktIn.Data.Ethertype) - } if isValidCtNw(ctNwDst) && ipDst != ctNwDst { ob := &crdv1alpha1.Observation{ Component: crdv1alpha1.ComponentLB, @@ -165,7 +153,7 @@ func (c *Controller) parsePacketIn(pktIn *ofctrl.PacketIn) (*crdv1alpha1.Tracefl } // Collect egress conjunctionID and get NetworkPolicy from cache. - if match = getMatchRegField(matchers, uint32(openflow.EgressReg)); match != nil { + if match := getMatchRegField(matchers, uint32(openflow.EgressReg)); match != nil { egressInfo, err := getRegValue(match, nil) if err != nil { return nil, nil, err @@ -179,7 +167,7 @@ func (c *Controller) parsePacketIn(pktIn *ofctrl.PacketIn) (*crdv1alpha1.Tracefl } // Collect ingress conjunctionID and get NetworkPolicy from cache. - if match = getMatchRegField(matchers, uint32(openflow.IngressReg)); match != nil { + if match := getMatchRegField(matchers, uint32(openflow.IngressReg)); match != nil { ingressInfo, err := getRegValue(match, nil) if err != nil { return nil, nil, err @@ -195,14 +183,24 @@ func (c *Controller) parsePacketIn(pktIn *ofctrl.PacketIn) (*crdv1alpha1.Tracefl // Get drop table. if tableID == uint8(openflow.EgressMetricTable) || tableID == uint8(openflow.IngressMetricTable) { ob := getNetworkPolicyObservation(tableID, tableID == uint8(openflow.IngressMetricTable)) - if match = getMatchRegField(matchers, uint32(openflow.CNPDenyConjIDReg)); match != nil { + if match := getMatchRegField(matchers, uint32(openflow.CNPDenyConjIDReg)); match != nil { notAllowConjInfo, err := getRegValue(match, nil) if err != nil { return nil, nil, err } - npRef := c.networkPolicyQuerier.GetNetworkPolicyByRuleFlowID(notAllowConjInfo) - if npRef != nil { - ob.NetworkPolicy = npRef.ToString() + if ruleRef := c.networkPolicyQuerier.GetRuleByFlowID(notAllowConjInfo); ruleRef != nil { + if npRef := ruleRef.PolicyRef; npRef != nil { + ob.NetworkPolicy = npRef.ToString() + } + if ruleRef.Action != nil && *ruleRef.Action == crdv1alpha1.RuleActionReject { + rejectAction := crdv1alpha1.RejectActionICMPProhibited + if prot == ip.TCPProtocol { + rejectAction = crdv1alpha1.RejectActionTCPReset + } else if pktIn.Data.Ethertype == protocol.IPv6_MSG { + rejectAction = crdv1alpha1.RejectActionICMPv6Prohibited + } + ob.RejectAction = &rejectAction + } } } obs = append(obs, *ob) @@ -216,14 +214,14 @@ func (c *Controller) parsePacketIn(pktIn *ofctrl.PacketIn) (*crdv1alpha1.Tracefl ob := new(crdv1alpha1.Observation) tunnelDstIP := "" isIPv6 := c.nodeConfig.NodeIPAddr.IP.To4() == nil - if match = getMatchTunnelDstField(matchers, isIPv6); match != nil { + if match := getMatchTunnelDstField(matchers, isIPv6); match != nil { tunnelDstIP, err = getTunnelDstValue(match) if err != nil { return nil, nil, err } } var outputPort uint32 - if match = getMatchRegField(matchers, uint32(openflow.PortCacheReg)); match != nil { + if match := getMatchRegField(matchers, uint32(openflow.PortCacheReg)); match != nil { outputPort, err = getRegValue(match, nil) if err != nil { return nil, nil, err diff --git a/pkg/apis/crd/v1alpha1/types.go b/pkg/apis/crd/v1alpha1/types.go index ed1d49c1917..46c4903e3e4 100644 --- a/pkg/apis/crd/v1alpha1/types.go +++ b/pkg/apis/crd/v1alpha1/types.go @@ -51,6 +51,15 @@ const ( ActionForwardedOutOfOverlay TraceflowAction = "ForwardedOutOfOverlay" ) +type TraceflowRejectAction string + +const ( + RejectActionUnknown TraceflowRejectAction = "Unknown" + RejectActionICMPProhibited TraceflowRejectAction = "ICMPProhibited" + RejectActionICMPv6Prohibited TraceflowRejectAction = "ICMPv6Prohibited" + RejectActionTCPReset TraceflowRejectAction = "TCPReset" +) + // List the supported protocols and their codes in traceflow. // According to code in Antrea agent and controller, default protocol is ICMP if protocol is not inputted by users. const ( @@ -234,6 +243,8 @@ type Observation struct { ComponentInfo string `json:"componentInfo,omitempty" yaml:"componentInfo,omitempty"` // Action is the action to the observation. Action TraceflowAction `json:"action,omitempty" yaml:"action,omitempty"` + // RejectAction is the reject action to the observation. Only available when Action is ActionDropped. + RejectAction *TraceflowRejectAction `json:"rejectAction,omitempty" yaml:"rejectAction,omitempty"` // Pod is the combination of Pod name and Pod Namespace. Pod string `json:"pod,omitempty" yaml:"pod,omitempty"` // DstMAC is the destination MAC. diff --git a/pkg/apis/crd/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/crd/v1alpha1/zz_generated.deepcopy.go index 7cc82efef09..5bb4cf17418 100644 --- a/pkg/apis/crd/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/crd/v1alpha1/zz_generated.deepcopy.go @@ -395,7 +395,9 @@ func (in *NodeResult) DeepCopyInto(out *NodeResult) { if in.Observations != nil { in, out := &in.Observations, &out.Observations *out = make([]Observation, len(*in)) - copy(*out, *in) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } return } @@ -413,6 +415,11 @@ func (in *NodeResult) DeepCopy() *NodeResult { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Observation) DeepCopyInto(out *Observation) { *out = *in + if in.RejectAction != nil { + in, out := &in.RejectAction, &out.RejectAction + *out = new(TraceflowRejectAction) + **out = **in + } return } diff --git a/test/e2e/traceflow_test.go b/test/e2e/traceflow_test.go index 7f9ba452bd7..606e43188b2 100644 --- a/test/e2e/traceflow_test.go +++ b/test/e2e/traceflow_test.go @@ -77,12 +77,12 @@ func TestTraceflowIntraNodeANP(t *testing.T) { failOnError(err, t) node1 := nodeName(0) - node1Pods, _, node1CleanupFn := createTestBusyboxPods(t, data, 2, node1) + node1Pods, _, node1CleanupFn := createTestBusyboxPods(t, data, 3, node1) defer node1CleanupFn() var denyIngress *secv1alpha1.NetworkPolicy denyIngressName := "test-anp-deny-ingress" - if denyIngress, err = data.createANPDenyIngress("antrea-e2e", node1Pods[1], denyIngressName); err != nil { + if denyIngress, err = data.createANPDenyIngress("antrea-e2e", node1Pods[1], denyIngressName, false); err != nil { t.Fatalf("Error when creating Antrea NetworkPolicy: %v", err) } defer func() { @@ -90,11 +90,22 @@ func TestTraceflowIntraNodeANP(t *testing.T) { t.Errorf("Error when deleting Antrea NetworkPolicy: %v", err) } }() + var rejectIngress *secv1alpha1.NetworkPolicy + rejectIngressName := "test-anp-reject-ingress" + if rejectIngress, err = data.createANPDenyIngress("antrea-e2e", node1Pods[2], rejectIngressName, true); err != nil { + t.Fatalf("Error when creating Antrea NetworkPolicy: %v", err) + } + defer func() { + if err = data.deleteAntreaNetworkpolicy(rejectIngress); err != nil { + t.Errorf("Error when deleting Antrea NetworkPolicy: %v", err) + } + }() antreaPod, err := data.getAntreaPodOnNode(node1) if err = data.waitForNetworkpolicyRealized(antreaPod, denyIngressName, v1beta2.AntreaNetworkPolicy); err != nil { t.Fatal(err) } + rejectActionTCPReset := v1alpha1.RejectActionTCPReset testcases := []testcase{ { name: "ANPDenyIngressIPv4", @@ -143,6 +154,54 @@ func TestTraceflowIntraNodeANP(t *testing.T) { }, }, }, + { + name: "ANPRejectIngressIPv4", + ipVersion: 4, + tf: &v1alpha1.Traceflow{ + ObjectMeta: metav1.ObjectMeta{ + Name: randName(fmt.Sprintf("%s-%s-to-%s-%s-", testNamespace, node1Pods[0], testNamespace, node1Pods[2])), + }, + Spec: v1alpha1.TraceflowSpec{ + Source: v1alpha1.Source{ + Namespace: testNamespace, + Pod: node1Pods[0], + }, + Destination: v1alpha1.Destination{ + Namespace: testNamespace, + Pod: node1Pods[2], + }, + Packet: v1alpha1.Packet{ + IPHeader: v1alpha1.IPHeader{ + Protocol: protocolTCP, + }, + TransportHeader: v1alpha1.TransportHeader{ + TCP: &v1alpha1.TCPHeader{ + DstPort: 80, + Flags: 2, + }, + }, + }, + }, + }, + expectedPhase: v1alpha1.Succeeded, + expectedResults: []v1alpha1.NodeResult{ + { + Node: node1, + Observations: []v1alpha1.Observation{ + { + Component: v1alpha1.ComponentSpoofGuard, + Action: v1alpha1.ActionForwarded, + }, + { + Component: v1alpha1.ComponentNetworkPolicy, + ComponentInfo: "IngressMetric", + RejectAction: &rejectActionTCPReset, + Action: v1alpha1.ActionDropped, + }, + }, + }, + }, + }, { name: "ANPDenyIngressIPv6", ipVersion: 6, @@ -1728,6 +1787,7 @@ func compareObservations(expected v1alpha1.NodeResult, actual v1alpha1.NodeResul exObs[i].ComponentInfo != acObs[i].ComponentInfo || exObs[i].Pod != acObs[i].Pod || exObs[i].TranslatedDstIP != acObs[i].TranslatedDstIP || + exObs[i].RejectAction != acObs[i].RejectAction || exObs[i].Action != acObs[i].Action { return fmt.Errorf("Observations should be %v, but got %v", exObs, acObs) } @@ -1736,8 +1796,11 @@ func compareObservations(expected v1alpha1.NodeResult, actual v1alpha1.NodeResul } // createANPDenyIngress creates an Antrea NetworkPolicy that denies ingress traffic for pods of specific label. -func (data *TestData) createANPDenyIngress(key string, value string, name string) (*secv1alpha1.NetworkPolicy, error) { +func (data *TestData) createANPDenyIngress(key string, value string, name string, isReject bool) (*secv1alpha1.NetworkPolicy, error) { dropACT := secv1alpha1.RuleActionDrop + if isReject { + dropACT = secv1alpha1.RuleActionReject + } anp := secv1alpha1.NetworkPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: name,