From d3faddc684b7c36d8b8056648bb83571fd350257 Mon Sep 17 00:00:00 2001 From: Dyanngg Date: Mon, 16 Sep 2024 14:13:05 -0700 Subject: [PATCH] Fix networkpolicy related antctl commands (#6487) This commit contains numerous fixes for antctl, including the get ovsflows and get networkpolicy commands: 1. Update the `antctl get networkpolicy` flag type=ANP to point the correct AdminNetworkPolicy type since ANP<->AntreaNetworkPolicy mapping has been deprecated. 2. Update the `antctl get ovsflows` command so that when dumping flows for a specific policy, a type is now required to eliminate ambiguity. 3. Since v1.13 the `antctl get ovsflows` command will not work properly for dumping networkpolicy flows. This is due to the matcher for dumping flows include priority, which is not supported by openvswitch. This PR fixes this issue. Signed-off-by: Dyanngg --- docs/antctl.md | 27 +- .../handlers/networkpolicy/handler.go | 21 +- .../apiserver/handlers/ovsflows/handler.go | 63 ++++- .../handlers/ovsflows/handler_test.go | 243 +++++++++++++----- pkg/agent/openflow/client.go | 8 +- pkg/agent/openflow/network_policy.go | 19 +- pkg/agent/openflow/network_policy_test.go | 16 +- pkg/agent/openflow/pipeline.go | 8 +- pkg/agent/openflow/testing/mock_openflow.go | 8 +- pkg/antctl/antctl.go | 16 +- pkg/ovs/openflow/utils.go | 19 +- pkg/ovs/openflow/utils_test.go | 79 +++++- pkg/querier/querier.go | 22 +- 13 files changed, 415 insertions(+), 134 deletions(-) diff --git a/docs/antctl.md b/docs/antctl.md index ed7993022e4..5f4cba5c5a8 100644 --- a/docs/antctl.md +++ b/docs/antctl.md @@ -251,7 +251,7 @@ output format. The `NAME` of a control plane NetworkPolicy is the UID of its sou NetworkPolicy. ```bash -antctl get networkpolicy [NAME] [-n NAMESPACE] [-o yaml] +antctl get networkpolicy [NAME] [-n NAMESPACE] [-T K8sNP|ACNP|ANNP|ANP|BANP] [-o yaml] antctl get appliedtogroup [NAME] [-o yaml] antctl get addressgroup [NAME] [-o yaml] ``` @@ -350,7 +350,7 @@ in the specified OVS flow tables, or all or the specified OVS groups. antctl get ovsflows antctl get ovsflows -p POD -n NAMESPACE antctl get ovsflows -S SERVICE -n NAMESPACE -antctl get ovsflows -N NETWORKPOLICY -n NAMESPACE +antctl get ovsflows [-n NAMESPACE] -N NETWORKPOLICY --type NETWORKPOLICY_TYPE antctl get ovsflows -T TABLE_A,TABLE_B antctl get ovsflows -T TABLE_A,TABLE_B_NUM antctl get ovsflows -G all @@ -381,13 +381,22 @@ kube-system kube-dns 160ea6d7-0234-5d1d-8ea0-b703d0aa3b46 1 # Dump OVS flows of NetworkPolicy "kube-dns" $ antctl get of -N kube-dns -n kube-system FLOW -table=90, n_packets=0, n_bytes=0, priority=190,conj_id=1,ip actions=resubmit(,105) -table=90, n_packets=0, n_bytes=0, priority=200,ip actions=conjunction(1,1/3) -table=90, n_packets=0, n_bytes=0, priority=200,ip,reg1=0x5 actions=conjunction(2,2/3),conjunction(1,2/3) -table=90, n_packets=0, n_bytes=0, priority=200,udp,tp_dst=53 actions=conjunction(1,3/3) -table=90, n_packets=0, n_bytes=0, priority=200,tcp,tp_dst=53 actions=conjunction(1,3/3) -table=90, n_packets=0, n_bytes=0, priority=200,tcp,tp_dst=9153 actions=conjunction(1,3/3) -table=100, n_packets=0, n_bytes=0, priority=200,ip,reg1=0x5 actions=drop +table=IngressRule, n_packets=0, n_bytes=0, priority=190,conj_id=1,ip actions=set_field:0x1->reg5,ct(commit,table=IngressMetric,zone=65520,exec(set_field:0x1/0xffffffff->ct_label)) +table=IngressRule, n_packets=0, n_bytes=0, priority=200,ip actions=conjunction(1,1/3) +table=IngressRule, n_packets=0, n_bytes=0, priority=200,ip,reg1=0x5 actions=conjunction(2,2/3),conjunction(1,2/3) +table=IngressRule, n_packets=0, n_bytes=0, priority=200,udp,tp_dst=53 actions=conjunction(1,3/3) +table=IngressRule, n_packets=0, n_bytes=0, priority=200,tcp,tp_dst=53 actions=conjunction(1,3/3) +table=IngressRule, n_packets=0, n_bytes=0, priority=200,tcp,tp_dst=9153 actions=conjunction(1,3/3) +table=IngressDefaultRule, n_packets=0, n_bytes=0, priority=200,ip,reg1=0x5 actions=drop + +# Dump OVS flows of AntreaNetworkPolicy "test-annp" +$ antctl get ovsflows -N test-annp -n default --type ANNP +FLOW +table=AntreaPolicyIngressRule, n_packets=0, n_bytes=0, priority=14900,conj_id=6 actions=set_field:0x6->reg3,set_field:0x400/0x400->reg0,goto_table:IngressMetric +table=AntreaPolicyIngressRule, n_packets=0, n_bytes=0, priority=14900,ip,nw_src=10.20.1.8 actions=conjunction(6,1/3) +table=AntreaPolicyIngressRule, n_packets=0, n_bytes=0, priority=14900,ip,nw_src=10.20.2.8 actions=conjunction(6,1/3) +table=AntreaPolicyIngressRule, n_packets=0, n_bytes=0, priority=14900,reg1=0x3 actions=conjunction(6,2/3) +table=AntreaPolicyIngressRule, n_packets=0, n_bytes=0, priority=14900,tcp,tp_dst=443 actions=conjunction(6,3/3) ``` ### OVS packet tracing diff --git a/pkg/agent/apiserver/handlers/networkpolicy/handler.go b/pkg/agent/apiserver/handlers/networkpolicy/handler.go index 7490d88d489..44f9a0bed6a 100644 --- a/pkg/agent/apiserver/handlers/networkpolicy/handler.go +++ b/pkg/agent/apiserver/handlers/networkpolicy/handler.go @@ -57,15 +57,6 @@ func HandleFunc(aq agentquerier.AgentQuerier) http.HandlerFunc { } } -// From user shorthand input to cpv1beta1.NetworkPolicyType -var mapToNetworkPolicyType = map[string]cpv1beta.NetworkPolicyType{ - "NP": cpv1beta.K8sNetworkPolicy, - "K8SNP": cpv1beta.K8sNetworkPolicy, - "ACNP": cpv1beta.AntreaClusterNetworkPolicy, - "ANNP": cpv1beta.AntreaNetworkPolicy, - "ANP": cpv1beta.AntreaNetworkPolicy, -} - // Create a Network Policy Filter from URL Query func newFilterFromURLQuery(query url.Values) (*querier.NetworkPolicyQueryFilter, string, error) { namespace, pod := query.Get("namespace"), query.Get("pod") @@ -77,9 +68,13 @@ func newFilterFromURLQuery(query url.Values) (*querier.NetworkPolicyQueryFilter, } } strSourceType := strings.ToUpper(query.Get("type")) - npSourceType, ok := mapToNetworkPolicyType[strSourceType] - if strSourceType != "" && !ok { - return nil, "", fmt.Errorf("invalid policy source type. Valid values are K8sNP, ACNP, ANNP and ANP (deprecated)") + var policyType cpv1beta.NetworkPolicyType + if strSourceType != "" { + npSourceType, ok := querier.NetworkPolicyTypeMap[strSourceType] + if !ok { + return nil, "", fmt.Errorf("unknown policy type. Valid types are %v", querier.GetNetworkPolicyTypeShorthands()) + } + policyType = npSourceType } source := query.Get("source") name := query.Get("name") @@ -90,6 +85,6 @@ func newFilterFromURLQuery(query url.Values) (*querier.NetworkPolicyQueryFilter, Name: name, SourceName: source, Namespace: namespace, - SourceType: npSourceType, + SourceType: policyType, }, pod, nil } diff --git a/pkg/agent/apiserver/handlers/ovsflows/handler.go b/pkg/agent/apiserver/handlers/ovsflows/handler.go index 22c3b46f07d..12a9067043f 100644 --- a/pkg/agent/apiserver/handlers/ovsflows/handler.go +++ b/pkg/agent/apiserver/handlers/ovsflows/handler.go @@ -16,6 +16,7 @@ package ovsflows import ( "encoding/json" + "fmt" "net/http" "sort" "strconv" @@ -26,6 +27,7 @@ import ( "antrea.io/antrea/pkg/agent/apis" "antrea.io/antrea/pkg/agent/openflow" agentquerier "antrea.io/antrea/pkg/agent/querier" + cpv1beta "antrea.io/antrea/pkg/apis/controlplane/v1beta2" binding "antrea.io/antrea/pkg/ovs/openflow" "antrea.io/antrea/pkg/querier" ) @@ -35,10 +37,12 @@ var ( getFlowTableName = openflow.GetFlowTableName getFlowTableID = openflow.GetFlowTableID getFlowTableList = openflow.GetTableList + + errAmbiguousQuery = fmt.Errorf("query is ambiguous and matches more than one policy") ) func dumpMatchedFlows(aq agentquerier.AgentQuerier, flowKeys []string) ([]apis.OVSFlowResponse, error) { - resps := []apis.OVSFlowResponse{} + var resps []apis.OVSFlowResponse for _, f := range flowKeys { flowStr, err := aq.GetOVSCtlClient().DumpMatchedFlow(f) if err != nil { @@ -53,7 +57,7 @@ func dumpMatchedFlows(aq agentquerier.AgentQuerier, flowKeys []string) ([]apis.O } func dumpFlows(aq agentquerier.AgentQuerier, table uint8) ([]apis.OVSFlowResponse, error) { - resps := []apis.OVSFlowResponse{} + var resps []apis.OVSFlowResponse var flowStrs []string var err error if table != binding.TableIDAll { @@ -170,19 +174,27 @@ func getServiceFlows(aq agentquerier.AgentQuerier, serviceName, namespace string return append(resps, groupResps...), nil } -func getNetworkPolicyFlows(aq agentquerier.AgentQuerier, npName, namespace string) ([]apis.OVSFlowResponse, error) { - if len(aq.GetNetworkPolicyInfoQuerier().GetNetworkPolicies(&querier.NetworkPolicyQueryFilter{SourceName: npName, Namespace: namespace})) == 0 { +func getNetworkPolicyFlows(aq agentquerier.AgentQuerier, npName, namespace string, policyType cpv1beta.NetworkPolicyType) ([]apis.OVSFlowResponse, error) { + npFilter := &querier.NetworkPolicyQueryFilter{ + SourceName: npName, + Namespace: namespace, + SourceType: policyType, + } + nps := aq.GetNetworkPolicyInfoQuerier().GetNetworkPolicies(npFilter) + if len(nps) == 0 { // NetworkPolicy not found. return nil, nil + } else if len(nps) > 1 { + return nil, errAmbiguousQuery } - - flowKeys := aq.GetOpenflowClient().GetNetworkPolicyFlowKeys(npName, namespace) + namespace, policyType = nps[0].SourceRef.Namespace, nps[0].SourceRef.Type + flowKeys := aq.GetOpenflowClient().GetNetworkPolicyFlowKeys(npName, namespace, policyType) return dumpMatchedFlows(aq, flowKeys) } -func getTableNames(aq agentquerier.AgentQuerier) []apis.OVSFlowResponse { - resps := []apis.OVSFlowResponse{} - names := []string{} +func getTableNames() []apis.OVSFlowResponse { + var resps []apis.OVSFlowResponse + var names []string for _, t := range getFlowTableList() { names = append(names, t.GetName()) } @@ -201,6 +213,7 @@ func HandleFunc(aq agentquerier.AgentQuerier) http.HandlerFunc { pod := r.URL.Query().Get("pod") service := r.URL.Query().Get("service") networkPolicy := r.URL.Query().Get("networkpolicy") + policyType := strings.ToUpper(r.URL.Query().Get("type")) namespace := r.URL.Query().Get("namespace") table := r.URL.Query().Get("table") groups := r.URL.Query().Get("groups") @@ -214,16 +227,31 @@ func HandleFunc(aq agentquerier.AgentQuerier) http.HandlerFunc { } if tableNamesOnly { - resps = getTableNames(aq) + resps = getTableNames() encodeResp() return } - if (pod != "" || service != "" || networkPolicy != "") && namespace == "" { + if (pod != "" || service != "") && namespace == "" { http.Error(w, "namespace must be provided", http.StatusBadRequest) return } - + if networkPolicy != "" && policyType != "" { + _, ok := querier.NetworkPolicyTypeMap[policyType] + if !ok { + errorMsg := fmt.Sprintf("unknown policy type. Valid types are %v", querier.GetNetworkPolicyTypeShorthands()) + http.Error(w, errorMsg, http.StatusBadRequest) + return + } + if querier.NamespaceScopedPolicyTypes.Has(policyType) && namespace == "" { + http.Error(w, "policy Namespace must be provided for policy type "+policyType, http.StatusBadRequest) + return + } + if !querier.NamespaceScopedPolicyTypes.Has(policyType) && namespace != "" { + http.Error(w, "policy Namespace should not be provided for cluster-scoped policy type "+policyType, http.StatusBadRequest) + return + } + } if pod == "" && service == "" && networkPolicy == "" && namespace == "" && table == "" && groups == "" { resps, err = dumpFlows(aq, binding.TableIDAll) } else if pod != "" { @@ -236,7 +264,16 @@ func HandleFunc(aq agentquerier.AgentQuerier) http.HandlerFunc { } resps, err = getServiceFlows(aq, service, namespace) } else if networkPolicy != "" { - resps, err = getNetworkPolicyFlows(aq, networkPolicy, namespace) + var cpPolicyType cpv1beta.NetworkPolicyType + if policyType != "" { + // policyType string has already been validated above + cpPolicyType = querier.NetworkPolicyTypeMap[policyType] + } + resps, err = getNetworkPolicyFlows(aq, networkPolicy, namespace, cpPolicyType) + if err == errAmbiguousQuery { + http.Error(w, errAmbiguousQuery.Error(), http.StatusBadRequest) + return + } } else if table != "" { resps, err = getTableFlows(aq, table) if err == nil && resps == nil { diff --git a/pkg/agent/apiserver/handlers/ovsflows/handler_test.go b/pkg/agent/apiserver/handlers/ovsflows/handler_test.go index 99ebc33bf58..9b3dac9ec62 100644 --- a/pkg/agent/apiserver/handlers/ovsflows/handler_test.go +++ b/pkg/agent/apiserver/handlers/ovsflows/handler_test.go @@ -44,22 +44,36 @@ var ( testDumpGroups = []string{"group1", "group2"} testResponses = []apis.OVSFlowResponse{{Flow: "flow1"}, {Flow: "flow2"}} testGroupResponses = []apis.OVSFlowResponse{{Flow: "group1"}, {Flow: "group2"}} + + testNetworkPolicy = &cpv1beta.NetworkPolicy{ + SourceRef: &cpv1beta.NetworkPolicyReference{ + Type: cpv1beta.K8sNetworkPolicy, + Namespace: "default", + }, + } + testANNP = &cpv1beta.NetworkPolicy{ + SourceRef: &cpv1beta.NetworkPolicyReference{ + Type: cpv1beta.AntreaNetworkPolicy, + Namespace: "default", + }, + } ) type testCase struct { - test string - name string - namespace string - query string - expectedStatus int - resps []apis.OVSFlowResponse + testName string + name string + namespace string + policyType cpv1beta.NetworkPolicyType + policyTypeToReturn cpv1beta.NetworkPolicyType + query string + expectedStatus int + resps []apis.OVSFlowResponse } func TestBadRequests(t *testing.T) { badRequests := map[string]string{ "Pod only": "?pod=pod1", "Service only": "?service=svc1", - "NetworkPolicy only": "?networkpolicy=np1", "Namespace only": "?namespace=ns1", "Pod and NetworkPolicy": "?pod=pod1&&networkpolicy=np1", "Pod and table": "?pod=pod1&&table=0", @@ -88,14 +102,14 @@ func TestPodFlows(t *testing.T) { testInterface := &interfacestore.InterfaceConfig{InterfaceName: "interface0"} testcases := []testCase{ { - test: "Existing Pod", + testName: "Existing Pod", name: "pod1", namespace: "ns1", query: "?pod=pod1&&namespace=ns1", expectedStatus: http.StatusOK, }, { - test: "Non-existing Pod", + testName: "Non-existing Pod", name: "pod2", namespace: "ns2", query: "?pod=pod2&&namespace=ns2", @@ -130,7 +144,7 @@ func TestServiceFlows(t *testing.T) { ctrl := gomock.NewController(t) testcases := []testCase{ { - test: "Existing Service", + testName: "Existing Service", name: "svc1", namespace: "ns1", query: "?service=svc1&&namespace=ns1", @@ -138,7 +152,7 @@ func TestServiceFlows(t *testing.T) { resps: append(testResponses, testGroupResponses...), }, { - test: "Non-existing Service", + testName: "Non-existing Service", name: "svc2", namespace: "ns2", query: "?service=svc2&&namespace=ns2", @@ -169,48 +183,153 @@ func TestServiceFlows(t *testing.T) { } } -func TestNetworkPolicyFlows(t *testing.T) { - ctrl := gomock.NewController(t) - testNetworkPolicy := &cpv1beta.NetworkPolicy{} +func TestNetworkPolicyFlowsSuccess(t *testing.T) { testcases := []testCase{ { - test: "Existing NetworkPolicy", + testName: "Existing NetworkPolicy", name: "np1", - namespace: "ns1", - query: "?networkpolicy=np1&&namespace=ns1", + namespace: "default", + policyType: cpv1beta.K8sNetworkPolicy, + query: "?networkpolicy=np1&namespace=default&type=K8sNP", expectedStatus: http.StatusOK, }, { - test: "Non-existing NetworkPolicy", - name: "np2", - namespace: "ns2", - query: "?networkpolicy=np2&&namespace=ns2", - expectedStatus: http.StatusNotFound, + testName: "Existing ACNP", + name: "acnp1", + policyType: cpv1beta.AntreaClusterNetworkPolicy, + query: "?networkpolicy=acnp1&type=ACNP", + expectedStatus: http.StatusOK, + }, + { + testName: "Existing ANNP", + name: "annp1", + namespace: "default", + policyType: cpv1beta.AntreaNetworkPolicy, + query: "?networkpolicy=annp1&namespace=default&type=ANNP", + expectedStatus: http.StatusOK, + }, + { + testName: "Existing ANNP - no type provided", + name: "annp1", + namespace: "default", + query: "?networkpolicy=annp1&namespace=default", + policyTypeToReturn: cpv1beta.AntreaNetworkPolicy, + expectedStatus: http.StatusOK, + }, + { + testName: "Existing ANP", + name: "anp1", + policyType: cpv1beta.AdminNetworkPolicy, + query: "?networkpolicy=anp1&type=ANP", + expectedStatus: http.StatusOK, }, } for i := range testcases { tc := testcases[i] - npq := queriertest.NewMockAgentNetworkPolicyInfoQuerier(ctrl) - q := aqtest.NewMockAgentQuerier(ctrl) - q.EXPECT().GetNetworkPolicyInfoQuerier().Return(npq).Times(1) - - if tc.expectedStatus != http.StatusNotFound { + t.Run(tc.testName, func(t *testing.T) { + ctrl := gomock.NewController(t) + npq := queriertest.NewMockAgentNetworkPolicyInfoQuerier(ctrl) + q := aqtest.NewMockAgentQuerier(ctrl) ofc := oftest.NewMockClient(ctrl) ovsctl := ovsctltest.NewMockOVSCtlClient(ctrl) - npq.EXPECT().GetNetworkPolicies(&querier.NetworkPolicyQueryFilter{SourceName: tc.name, Namespace: tc.namespace}).Return([]cpv1beta.NetworkPolicy{*testNetworkPolicy}).Times(1) - ofc.EXPECT().GetNetworkPolicyFlowKeys(tc.name, tc.namespace).Return(testFlowKeys).Times(1) + npFilter := &querier.NetworkPolicyQueryFilter{ + SourceName: tc.name, + Namespace: tc.namespace, + SourceType: tc.policyType, + } + q.EXPECT().GetNetworkPolicyInfoQuerier().Return(npq).Times(1) + if tc.policyTypeToReturn == "" { + tc.policyTypeToReturn = tc.policyType + } + npq.EXPECT().GetNetworkPolicies(npFilter).Return([]cpv1beta.NetworkPolicy{ + { + SourceRef: &cpv1beta.NetworkPolicyReference{ + Type: tc.policyTypeToReturn, + Namespace: tc.namespace, + Name: tc.name, + }, + }, + }).Times(1) + ofc.EXPECT().GetNetworkPolicyFlowKeys(tc.name, tc.namespace, tc.policyTypeToReturn).Return(testFlowKeys).Times(1) q.EXPECT().GetOpenflowClient().Return(ofc).Times(1) q.EXPECT().GetOVSCtlClient().Return(ovsctl).Times(len(testFlowKeys)) for i := range testFlowKeys { ovsctl.EXPECT().DumpMatchedFlow(testFlowKeys[i]).Return(testDumpFlows[i], nil).Times(1) } - } else { - npq.EXPECT().GetNetworkPolicies(&querier.NetworkPolicyQueryFilter{SourceName: tc.name, Namespace: tc.namespace}).Return(nil).Times(1) - } + runHTTPTest(t, &tc, q) + }) + } +} - runHTTPTest(t, &tc, q) +func TestNetworkPolicyFlowsBadRequest(t *testing.T) { + testcases := []testCase{ + { + testName: "ACNP bad request - namespace should not be provided", + name: "acnp2", + policyType: cpv1beta.AntreaClusterNetworkPolicy, + query: "?networkpolicy=acnp2&type=ACNP&namespace=default", + expectedStatus: http.StatusBadRequest, + }, + { + testName: "ANNP bad request - namespace should be provided", + name: "annp2", + policyType: cpv1beta.AntreaNetworkPolicy, + query: "?networkpolicy=annp2&type=ANNP", + expectedStatus: http.StatusBadRequest, + }, } + for i := range testcases { + tc := testcases[i] + t.Run(tc.testName, func(t *testing.T) { + ctrl := gomock.NewController(t) + q := aqtest.NewMockAgentQuerier(ctrl) + runHTTPTest(t, &tc, q) + }) + } +} +func TestNetworkPolicyFlowsPolicyAmbiguousQuery(t *testing.T) { + tc := &testCase{ + testName: "Ambiguous query", + name: "np-annp-same-name", + namespace: "ns1", + query: "?networkpolicy=np-annp-same-name&namespace=ns1", + expectedStatus: http.StatusBadRequest, + } + ctrl := gomock.NewController(t) + q := aqtest.NewMockAgentQuerier(ctrl) + // Simulates an ambiguous query where more than one matching NP is returned + npq := queriertest.NewMockAgentNetworkPolicyInfoQuerier(ctrl) + q.EXPECT().GetNetworkPolicyInfoQuerier().Return(npq).Times(1) + npFilter := &querier.NetworkPolicyQueryFilter{ + SourceName: tc.name, + Namespace: tc.namespace, + SourceType: tc.policyType, + } + npq.EXPECT().GetNetworkPolicies(npFilter).Return([]cpv1beta.NetworkPolicy{*testNetworkPolicy, *testANNP}).Times(1) + runHTTPTest(t, tc, q) +} + +func TestNetworkPolicyFlowsPolicyNotFound(t *testing.T) { + tc := &testCase{ + testName: "Non-existing NetworkPolicy", + name: "np1", + namespace: "ns1", + policyType: cpv1beta.K8sNetworkPolicy, + query: "?networkpolicy=np1&namespace=ns1&type=K8sNP", + expectedStatus: http.StatusNotFound, + } + ctrl := gomock.NewController(t) + q := aqtest.NewMockAgentQuerier(ctrl) + npq := queriertest.NewMockAgentNetworkPolicyInfoQuerier(ctrl) + q.EXPECT().GetNetworkPolicyInfoQuerier().Return(npq).Times(1) + npFilter := &querier.NetworkPolicyQueryFilter{ + SourceName: tc.name, + Namespace: tc.namespace, + SourceType: tc.policyType, + } + npq.EXPECT().GetNetworkPolicies(npFilter).Return(nil).Times(1) + runHTTPTest(t, tc, q) } func TestTableFlows(t *testing.T) { @@ -219,33 +338,34 @@ func TestTableFlows(t *testing.T) { getFlowTableID = mockGetFlowTableID testcases := []testCase{ { - test: "Table 80", + testName: "Table 80", query: "?table=80", expectedStatus: http.StatusOK, }, { - test: "Table IngressRule", + testName: "Table IngressRule", query: "?table=IngressRule", expectedStatus: http.StatusOK, }, } for i := range testcases { tc := testcases[i] - ovsctl := ovsctltest.NewMockOVSCtlClient(ctrl) - q := aqtest.NewMockAgentQuerier(ctrl) - q.EXPECT().GetOVSCtlClient().Return(ovsctl).Times(1) - ovsctl.EXPECT().DumpTableFlows(gomock.Any()).Return(testDumpFlows, nil).Times(1) + t.Run(tc.testName, func(t *testing.T) { + ovsctl := ovsctltest.NewMockOVSCtlClient(ctrl) + q := aqtest.NewMockAgentQuerier(ctrl) + q.EXPECT().GetOVSCtlClient().Return(ovsctl).Times(1) + ovsctl.EXPECT().DumpTableFlows(gomock.Any()).Return(testDumpFlows, nil).Times(1) - runHTTPTest(t, &tc, q) + runHTTPTest(t, &tc, q) + }) } - } func TestTableNamesOnly(t *testing.T) { ctrl := gomock.NewController(t) getFlowTableList = mockGetTableList tc := testCase{ - test: "Get table names only", + testName: "Get table names only", query: "?table-names-only", expectedStatus: http.StatusOK, resps: []apis.OVSFlowResponse{{Flow: "table0"}, {Flow: "table1"}}, @@ -284,7 +404,7 @@ func TestGroups(t *testing.T) { }{ { testCase: testCase{ - test: "All groups", + testName: "All groups", query: "?groups=all", expectedStatus: http.StatusOK, resps: testGroupResponses, @@ -293,7 +413,7 @@ func TestGroups(t *testing.T) { }, { testCase: testCase{ - test: "Group 1234", + testName: "Group 1234", query: "?groups=1234", expectedStatus: http.StatusOK, resps: []apis.OVSFlowResponse{{Flow: "group1234"}}, @@ -303,7 +423,7 @@ func TestGroups(t *testing.T) { }, { testCase: testCase{ - test: "Non-existing group 1234", + testName: "Non-existing group 1234", query: "?groups=1234", expectedStatus: http.StatusOK, resps: []apis.OVSFlowResponse{}, @@ -313,7 +433,7 @@ func TestGroups(t *testing.T) { }, { testCase: testCase{ - test: "Group 10, 100, and 1000", + testName: "Group 10, 100, and 1000", query: "?groups=10,100,1000", expectedStatus: http.StatusOK, resps: []apis.OVSFlowResponse{{Flow: "group10"}, {Flow: "group1000"}}, @@ -323,21 +443,22 @@ func TestGroups(t *testing.T) { }, } for _, tc := range testcases { - ovsctl := ovsctltest.NewMockOVSCtlClient(ctrl) - q := aqtest.NewMockAgentQuerier(ctrl) - if tc.groupIDs == nil { - // Get all. - q.EXPECT().GetOVSCtlClient().Return(ovsctl).Times(1) - ovsctl.EXPECT().DumpGroups().Return(tc.dumpedGroups, nil).Times(1) - } else { - // Get all. - q.EXPECT().GetOVSCtlClient().Return(ovsctl).Times(len(tc.groupIDs)) - for i, id := range tc.groupIDs { - ovsctl.EXPECT().DumpGroup(id).Return(tc.dumpedGroups[i], nil).Times(1) + t.Run(tc.testName, func(t *testing.T) { + ovsctl := ovsctltest.NewMockOVSCtlClient(ctrl) + q := aqtest.NewMockAgentQuerier(ctrl) + if tc.groupIDs == nil { + // Get all. + q.EXPECT().GetOVSCtlClient().Return(ovsctl).Times(1) + ovsctl.EXPECT().DumpGroups().Return(tc.dumpedGroups, nil).Times(1) + } else { + // Get all. + q.EXPECT().GetOVSCtlClient().Return(ovsctl).Times(len(tc.groupIDs)) + for i, id := range tc.groupIDs { + ovsctl.EXPECT().DumpGroup(id).Return(tc.dumpedGroups[i], nil).Times(1) + } } - } - - runHTTPTest(t, &tc.testCase, q) + runHTTPTest(t, &tc.testCase, q) + }) } } @@ -348,7 +469,7 @@ func runHTTPTest(t *testing.T, tc *testCase, aq agentquerier.AgentQuerier) { recorder := httptest.NewRecorder() handler.ServeHTTP(recorder, req) - assert.Equal(t, tc.expectedStatus, recorder.Code, tc.test) + assert.Equal(t, tc.expectedStatus, recorder.Code, tc.testName) if tc.expectedStatus == http.StatusOK { var received []apis.OVSFlowResponse diff --git a/pkg/agent/openflow/client.go b/pkg/agent/openflow/client.go index e6d3ec5ef87..e6e77d0cebd 100644 --- a/pkg/agent/openflow/client.go +++ b/pkg/agent/openflow/client.go @@ -216,7 +216,7 @@ type Client interface { // flows for a NetworkPolicy. Flows are grouped by policy rules, and duplicated // entries can be added due to conjunctive match flows shared by multiple // rules. - GetNetworkPolicyFlowKeys(npName, npNamespace string) []string + GetNetworkPolicyFlowKeys(npName, npNamespace string, npType v1beta2.NetworkPolicyType) []string // ReassignFlowPriorities takes a list of priority updates, and update the actionFlows to replace // the old priority with the desired one, for each priority update on that table. @@ -445,7 +445,7 @@ func (c *client) addFlowsWithMultipleKeys(cache *flowCategoryCache, keyToFlows m for _, flow := range flows { msg := getFlowModMessage(flow, binding.AddMessage) allMessages = append(allMessages, msg) - fCache[getFlowKey(msg)] = msg + fCache[getFlowModKey(msg)] = msg } flowCacheMap[flowCacheKey] = fCache } @@ -473,7 +473,7 @@ func (c *client) modifyFlows(cache *flowCategoryCache, flowCacheKey string, flow for _, flow := range flows { msg := getFlowModMessage(flow, binding.AddMessage) messages = append(messages, msg) - fCache[getFlowKey(msg)] = msg + fCache[getFlowModKey(msg)] = msg } err = c.ofEntryOperations.AddAll(messages) } else { @@ -695,7 +695,7 @@ func (c *client) getFlowKeysFromCache(cache *flowCategoryCache, cacheKey string) c.replayMutex.RLock() defer c.replayMutex.RUnlock() for _, flow := range fCache { - flowKeys = append(flowKeys, getFlowKey(flow)) + flowKeys = append(flowKeys, getFlowModKey(flow)) } return flowKeys } diff --git a/pkg/agent/openflow/network_policy.go b/pkg/agent/openflow/network_policy.go index 95e61760eb7..babc9e83971 100644 --- a/pkg/agent/openflow/network_policy.go +++ b/pkg/agent/openflow/network_policy.go @@ -1515,13 +1515,12 @@ func (c *policyRuleConjunction) calculateChangesForRuleDeletion() []*conjMatchFl return ctxChanges } -// getAllFlowKeys returns the matching strings of actions flows of -// policyRuleConjunction, as well as matching flows of all its clauses. +// getAllFlowKeys returns the match strings used in ovs-ofctl dump-flows command, including +// actions flows of policyRuleConjunction, as well as matching flows of all its clauses. func (c *policyRuleConjunction) getAllFlowKeys() []string { - flowKeys := []string{} - dropFlowKeys := []string{} + var flowKeys, dropFlowKeys []string for _, flow := range c.actionFlows { - flowKeys = append(flowKeys, getFlowKey(flow)) + flowKeys = append(flowKeys, getFlowDumpKey(flow)) } addClauseFlowKeys := func(clause *clause) { @@ -1530,10 +1529,10 @@ func (c *policyRuleConjunction) getAllFlowKeys() []string { } for _, ctx := range clause.matches { if ctx.flow != nil { - flowKeys = append(flowKeys, getFlowKey(ctx.flow)) + flowKeys = append(flowKeys, getFlowDumpKey(ctx.flow)) } if ctx.dropFlow != nil { - dropFlowKeys = append(dropFlowKeys, getFlowKey(ctx.dropFlow)) + dropFlowKeys = append(dropFlowKeys, getFlowDumpKey(ctx.dropFlow)) } } } @@ -1710,8 +1709,8 @@ func (c *client) DeletePolicyRuleAddress(ruleID uint32, addrType types.AddressTy return c.featureNetworkPolicy.applyConjunctiveMatchFlows(changes) } -func (c *client) GetNetworkPolicyFlowKeys(npName, npNamespace string) []string { - flowKeys := []string{} +func (c *client) GetNetworkPolicyFlowKeys(npName, npNamespace string, npType v1beta2.NetworkPolicyType) []string { + var flowKeys []string // Hold replayMutex write lock to protect flows from being modified by // NetworkPolicy updates and replayFlows. This is more for logic // cleanliness, as: for now flow updates do not impact the matching string @@ -1727,7 +1726,7 @@ func (c *client) GetNetworkPolicyFlowKeys(npName, npNamespace string) []string { if conj.npRef == nil { continue } - if conj.npRef.Name == npName && conj.npRef.Namespace == npNamespace { + if conj.npRef.Name == npName && conj.npRef.Namespace == npNamespace && conj.npRef.Type == npType { // There can be duplicated flows added due to conjunctive matches // shared by multiple policy rules (clauses). flowKeys = append(flowKeys, conj.getAllFlowKeys()...) diff --git a/pkg/agent/openflow/network_policy_test.go b/pkg/agent/openflow/network_policy_test.go index 40f10570c3f..3951e1b0afd 100644 --- a/pkg/agent/openflow/network_policy_test.go +++ b/pkg/agent/openflow/network_policy_test.go @@ -240,11 +240,11 @@ func TestInstallPolicyRuleFlows(t *testing.T) { err = c.featureNetworkPolicy.applyConjunctiveMatchFlows(ctxChanges2) require.Nil(t, err) - assert.Equal(t, 0, len(c.GetNetworkPolicyFlowKeys("np1", "ns1"))) + assert.Equal(t, 0, len(c.GetNetworkPolicyFlowKeys("np1", "ns1", v1beta2.K8sNetworkPolicy))) err = c.InstallPolicyRuleFlows(rule2) require.Nil(t, err) checkConjunctionConfig(t, ruleID2, 1, 2, 1, 0) - assert.Equal(t, 6, len(c.GetNetworkPolicyFlowKeys("np1", "ns1"))) + assert.Equal(t, 6, len(c.GetNetworkPolicyFlowKeys("np1", "ns1", v1beta2.K8sNetworkPolicy))) ruleID3 := uint32(103) port1 := intstr.FromInt(8080) @@ -288,7 +288,7 @@ func TestInstallPolicyRuleFlows(t *testing.T) { err = c.InstallPolicyRuleFlows(rule3) require.Nil(t, err, "Failed to invoke InstallPolicyRuleFlows") checkConjunctionConfig(t, ruleID3, 1, 2, 1, 3) - assert.Equal(t, 15, len(c.GetNetworkPolicyFlowKeys("np1", "ns1"))) + assert.Equal(t, 15, len(c.GetNetworkPolicyFlowKeys("np1", "ns1", v1beta2.K8sNetworkPolicy))) ctxChanges4 := conj.calculateChangesForRuleDeletion() matchFlows4, dropFlows4 := getChangedFlows(ctxChanges4) @@ -305,7 +305,7 @@ func TestInstallPolicyRuleFlows(t *testing.T) { assert.Equal(t, 2, getChangedFlowOPCount(matchFlows5, deletion)) assert.Equal(t, 1, getChangedFlowOPCount(matchFlows5, modification)) err = c.featureNetworkPolicy.applyConjunctiveMatchFlows(ctxChanges5) - assert.Equal(t, 12, len(c.GetNetworkPolicyFlowKeys("np1", "ns1"))) + assert.Equal(t, 12, len(c.GetNetworkPolicyFlowKeys("np1", "ns1", v1beta2.K8sNetworkPolicy))) require.Nil(t, err) } @@ -741,11 +741,11 @@ func TestInstallPolicyRuleFlowsInDualStackCluster(t *testing.T) { err = c.featureNetworkPolicy.applyConjunctiveMatchFlows(ctxChanges2) require.Nil(t, err) - assert.Equal(t, 0, len(c.GetNetworkPolicyFlowKeys("np1", "ns1"))) + assert.Equal(t, 0, len(c.GetNetworkPolicyFlowKeys("np1", "ns1", v1beta2.K8sNetworkPolicy))) err = c.InstallPolicyRuleFlows(rule2) require.Nil(t, err) checkConjunctionConfig(t, ruleID2, 2, 3, 1, 0) - assert.Equal(t, 9, len(c.GetNetworkPolicyFlowKeys("np1", "ns1"))) + assert.Equal(t, 9, len(c.GetNetworkPolicyFlowKeys("np1", "ns1", v1beta2.K8sNetworkPolicy))) ruleID3 := uint32(103) port1 := intstr.FromInt(8080) @@ -788,7 +788,7 @@ func TestInstallPolicyRuleFlowsInDualStackCluster(t *testing.T) { err = c.InstallPolicyRuleFlows(rule3) require.Nil(t, err, "Failed to invoke InstallPolicyRuleFlows") checkConjunctionConfig(t, ruleID3, 2, 2, 1, 4) - assert.Equal(t, 20, len(c.GetNetworkPolicyFlowKeys("np1", "ns1"))) + assert.Equal(t, 20, len(c.GetNetworkPolicyFlowKeys("np1", "ns1", v1beta2.K8sNetworkPolicy))) ctxChanges4 := conj.calculateChangesForRuleDeletion() matchFlows4, dropFlows4 := getChangedFlows(ctxChanges4) @@ -805,7 +805,7 @@ func TestInstallPolicyRuleFlowsInDualStackCluster(t *testing.T) { assert.Equal(t, 3, getChangedFlowOPCount(matchFlows5, deletion)) assert.Equal(t, 1, getChangedFlowOPCount(matchFlows5, modification)) err = c.featureNetworkPolicy.applyConjunctiveMatchFlows(ctxChanges5) - assert.Equal(t, 15, len(c.GetNetworkPolicyFlowKeys("np1", "ns1"))) + assert.Equal(t, 15, len(c.GetNetworkPolicyFlowKeys("np1", "ns1", v1beta2.K8sNetworkPolicy))) require.Nil(t, err) } diff --git a/pkg/agent/openflow/pipeline.go b/pkg/agent/openflow/pipeline.go index 601e910f186..a1eb6655d35 100644 --- a/pkg/agent/openflow/pipeline.go +++ b/pkg/agent/openflow/pipeline.go @@ -368,7 +368,7 @@ func copyFlowWithNewPriority(flowMod *openflow15.FlowMod, priority uint16) *open } func flowMessageMatched(oldFlow, newFlow *openflow15.FlowMod) bool { - return oldFlow.Priority == newFlow.Priority && getFlowKey(oldFlow) == getFlowKey(newFlow) + return oldFlow.Priority == newFlow.Priority && getFlowModKey(oldFlow) == getFlowModKey(newFlow) } // isDropFlow returns true if no instructions are defined in the OpenFlow modification message. @@ -378,10 +378,14 @@ func isDropFlow(f *openflow15.FlowMod) bool { return len(f.Instructions) == 0 } -func getFlowKey(fm *openflow15.FlowMod) string { +func getFlowModKey(fm *openflow15.FlowMod) string { return binding.FlowModMatchString(fm) } +func getFlowDumpKey(fm *openflow15.FlowMod) string { + return binding.FlowModMatchString(fm, "priority") +} + type flowMessageCache map[string]*openflow15.FlowMod type flowCategoryCache struct { diff --git a/pkg/agent/openflow/testing/mock_openflow.go b/pkg/agent/openflow/testing/mock_openflow.go index 99c21e47119..96dd46af1ea 100644 --- a/pkg/agent/openflow/testing/mock_openflow.go +++ b/pkg/agent/openflow/testing/mock_openflow.go @@ -178,17 +178,17 @@ func (mr *MockClientMockRecorder) GetFlowTableStatus() *gomock.Call { } // GetNetworkPolicyFlowKeys mocks base method. -func (m *MockClient) GetNetworkPolicyFlowKeys(arg0, arg1 string) []string { +func (m *MockClient) GetNetworkPolicyFlowKeys(arg0, arg1 string, arg2 v1beta2.NetworkPolicyType) []string { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetNetworkPolicyFlowKeys", arg0, arg1) + ret := m.ctrl.Call(m, "GetNetworkPolicyFlowKeys", arg0, arg1, arg2) ret0, _ := ret[0].([]string) return ret0 } // GetNetworkPolicyFlowKeys indicates an expected call of GetNetworkPolicyFlowKeys. -func (mr *MockClientMockRecorder) GetNetworkPolicyFlowKeys(arg0, arg1 any) *gomock.Call { +func (mr *MockClientMockRecorder) GetNetworkPolicyFlowKeys(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetworkPolicyFlowKeys", reflect.TypeOf((*MockClient)(nil).GetNetworkPolicyFlowKeys), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetworkPolicyFlowKeys", reflect.TypeOf((*MockClient)(nil).GetNetworkPolicyFlowKeys), arg0, arg1, arg2) } // GetPodFlowKeys mocks base method. diff --git a/pkg/antctl/antctl.go b/pkg/antctl/antctl.go index 946dbb3d53e..ddd91e6a6ab 100644 --- a/pkg/antctl/antctl.go +++ b/pkg/antctl/antctl.go @@ -215,9 +215,10 @@ $ antctl get podmulticaststats pod -n namespace`, shorthand: "p", }, { - name: "type", - usage: "Get NetworkPolicies with specific type. Type means the type of its source NetworkPolicy: K8sNP, ACNP, ANNP", - shorthand: "T", + name: "type", + usage: "Get NetworkPolicies with specific type. Type refers to the type of its source NetworkPolicy: K8sNP, ACNP, ANNP, BANP or ANP", + shorthand: "T", + supportedValues: []string{"K8sNP", "ACNP", "ANNP", "BANP", "ANP"}, }, }, getSortByFlag()), outputType: multiple, @@ -371,7 +372,7 @@ $ antctl get podmulticaststats pod -n namespace`, Dump OVS flows of a Service $ antctl get ovsflows -S svc1 -n ns1 Dump OVS flows of a NetworkPolicy - $ antctl get ovsflows -N np1 -n ns1 + $ antctl get ovsflows -N np1 -n ns1 --type K8sNP Dump OVS flows of a flow Table $ antctl get ovsflows -T IngressRule Dump OVS groups @@ -399,9 +400,14 @@ $ antctl get podmulticaststats pod -n namespace`, }, { name: "networkpolicy", - usage: "NetworkPolicy name. If present, Namespace must be provided.", + usage: "NetworkPolicy name. Namespace must be provided for non-cluster-scoped policy types if a type is specified.", shorthand: "N", }, + { + name: "type", + usage: "NetworkPolicy type. Valid types are K8sNP, ACNP, ANNP, BANP or ANP.", + supportedValues: []string{"K8sNP", "ACNP", "ANNP", "BANP", "ANP"}, + }, { name: "table", usage: "Comma separated Antrea OVS flow table names or numbers", diff --git a/pkg/ovs/openflow/utils.go b/pkg/ovs/openflow/utils.go index 1fc53beda5c..f0df875dcd5 100644 --- a/pkg/ovs/openflow/utils.go +++ b/pkg/ovs/openflow/utils.go @@ -17,6 +17,7 @@ package openflow import ( "fmt" "net" + "slices" "strings" "antrea.io/libOpenflow/openflow15" @@ -1222,8 +1223,22 @@ func FlowModToString(flowMod *openflow15.FlowMod) string { return fmt.Sprintf("%s, %s %s", getFlowModBaseString(flowMod), getFlowModMatch(flowMod), getFlowModAction(flowMod)) } -func FlowModMatchString(flowMod *openflow15.FlowMod) string { - return fmt.Sprintf("table=%d,%s", flowMod.TableId, getFlowModMatch(flowMod)) +func FlowModMatchString(flowMod *openflow15.FlowMod, omitFields ...string) string { + flowModMatch := getFlowModMatch(flowMod) + if len(omitFields) == 0 { + return fmt.Sprintf("table=%d,%s", flowMod.TableId, flowModMatch) + } + var flowDumpMatch []string + for _, m := range strings.Split(flowModMatch, ",") { + // Omit specific fields if needed. For example, the priority match field is not supported + // for the ovs-ofctl dump-flows command, and should be removed. + if !slices.ContainsFunc(omitFields, func(field string) bool { + return strings.HasPrefix(m, field) + }) { + flowDumpMatch = append(flowDumpMatch, m) + } + } + return fmt.Sprintf("table=%d,%s", flowMod.TableId, strings.Join(flowDumpMatch, ",")) } func GroupModToString(groupMod *openflow15.GroupMod) string { diff --git a/pkg/ovs/openflow/utils_test.go b/pkg/ovs/openflow/utils_test.go index bf88f902eee..e0d52e13610 100644 --- a/pkg/ovs/openflow/utils_test.go +++ b/pkg/ovs/openflow/utils_test.go @@ -143,7 +143,7 @@ func TestFlowModToString(t *testing.T) { newFb := *(basicFB.CopyToBuilder(basicFB.FlowPriority(), false).(*ofFlowBuilder)) f := tt.flowFunc(&newFb) messages, err := f.GetBundleMessages(AddMessage) - assert.NoError(t, err) + require.NoError(t, err) require.Equal(t, 1, len(messages)) fm, ok := messages[0].GetMessage().(*openflow15.FlowMod) assert.True(t, ok) @@ -303,7 +303,7 @@ func TestFlowModMatchString(t *testing.T) { newFb := *(basicFB.CopyToBuilder(basicFB.FlowPriority(), false).(*ofFlowBuilder)) f := tt.flowFunc(&newFb) messages, err := f.GetBundleMessages(AddMessage) - assert.NoError(t, err) + require.NoError(t, err) require.Equal(t, 1, len(messages)) fm, ok := messages[0].GetMessage().(*openflow15.FlowMod) assert.True(t, ok) @@ -313,6 +313,81 @@ func TestFlowModMatchString(t *testing.T) { } } +func TestFlowModStringForDumpCommand(t *testing.T) { + rm := &RegMark{field: NewRegField(0, 0, 3), value: 2} + table := &ofTable{Table: &ofctrl.Table{TableId: 1}} + basicFB := table.BuildFlow(100).(*ofFlowBuilder) + basicFB.Cookie(0x12345678).MatchInPort(3) + for _, tt := range []struct { + name string + flowFunc func(fb *ofFlowBuilder) Flow + expectedMatch string + }{ + { + name: "reg mark flow", + flowFunc: func(fb *ofFlowBuilder) Flow { + return fb.MatchRegMark(rm). + MatchXXReg(1, ip6Dst). + Done() + }, + expectedMatch: "table=1,reg0=0x2/0xf,xxreg1=0xfe000000000000000000000ca80a03,in_port=3", + }, + { + name: "IPv4 flow", + flowFunc: func(fb *ofFlowBuilder) Flow { + return fb.MatchDstMAC(ethDst).MatchSrcMAC(ethSrc).MatchDstIP(ipDst).MatchSrcIP(ipSrc).Done() + }, + expectedMatch: "table=1,in_port=3,dl_src=10:1a:1b:1c:1d:1f,dl_dst=20:2a:2b:2c:2d:2f,nw_src=192.168.10.2,nw_dst=192.168.20.3", + }, + { + name: "IPv4 net flow", + flowFunc: func(fb *ofFlowBuilder) Flow { + return fb.MatchSrcIPNet(*ipSrcNet).MatchDstIPNet(*ipDstNet).Done() + }, + expectedMatch: "table=1,in_port=3,nw_src=192.168.10.0/24,nw_dst=192.168.20.0/24", + }, + { + name: "IPv6 flow", + flowFunc: func(fb *ofFlowBuilder) Flow { + return fb.MatchDstIP(ip6Dst).MatchSrcIP(ip6Src).Done() + }, + expectedMatch: "table=1,in_port=3,ipv6_src=fe::ca8:a02,ipv6_dst=fe::ca8:a03", + }, + { + name: "IPv4 UDP flow", + flowFunc: func(fb *ofFlowBuilder) Flow { + return fb.MatchIPProtocolValue(false, 17).MatchDstPort(8080, nil).Done() + }, + expectedMatch: "table=1,udp,in_port=3,tp_dst=8080", + }, { + name: "IPv4 ICMP flow", + flowFunc: func(fb *ofFlowBuilder) Flow { + return fb.MatchProtocol(ProtocolICMP).MatchICMPCode(0).MatchICMPType(8).Done() + }, + expectedMatch: "table=1,icmp,in_port=3,icmp_type=8,icmp_code=0", + }, + { + name: "conjunction flow", + flowFunc: func(fb *ofFlowBuilder) Flow { + return fb.MatchProtocol(ProtocolIP).MatchConjID(101).Done() + }, + expectedMatch: "table=1,conj_id=101,ip,in_port=3", + }, + } { + t.Run(tt.name, func(t *testing.T) { + newFb := *(basicFB.CopyToBuilder(basicFB.FlowPriority(), false).(*ofFlowBuilder)) + f := tt.flowFunc(&newFb) + messages, err := f.GetBundleMessages(AddMessage) + require.NoError(t, err) + require.Equal(t, 1, len(messages)) + fm, ok := messages[0].GetMessage().(*openflow15.FlowMod) + assert.True(t, ok) + fmStr := FlowModMatchString(fm, "priority") + assert.Equal(t, tt.expectedMatch, fmStr) + }) + } +} + func TestGroupModToString(t *testing.T) { rf := NewRegField(1, 0, 31) for _, tt := range []struct { diff --git a/pkg/querier/querier.go b/pkg/querier/querier.go index 1ebf4fa0f06..886ec8f3ab5 100644 --- a/pkg/querier/querier.go +++ b/pkg/querier/querier.go @@ -17,6 +17,7 @@ package querier import ( v1 "k8s.io/api/core/v1" apitypes "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/sets" "antrea.io/antrea/pkg/agent/apis" "antrea.io/antrea/pkg/agent/interfacestore" @@ -108,10 +109,29 @@ type NetworkPolicyQueryFilter struct { SourceName string // The namespace of the original Namespace that the internal NetworkPolicy is created for. Namespace string - // The type of the original NetworkPolicy that the internal NetworkPolicy is created for.(K8sNP, ACNP, ANNP) + // The type of the original NetworkPolicy that the internal NetworkPolicy is created for.(K8sNP, ACNP, ANNP, ANP and BANP) SourceType cpv1beta.NetworkPolicyType } +// From user shorthand input to cpv1beta1.NetworkPolicyType +var NetworkPolicyTypeMap = map[string]cpv1beta.NetworkPolicyType{ + "K8SNP": cpv1beta.K8sNetworkPolicy, + "ACNP": cpv1beta.AntreaClusterNetworkPolicy, + "ANNP": cpv1beta.AntreaNetworkPolicy, + "ANP": cpv1beta.AdminNetworkPolicy, + "BANP": cpv1beta.BaselineAdminNetworkPolicy, +} + +func GetNetworkPolicyTypeShorthands() []string { + validTypes := make([]string, 0, len(NetworkPolicyTypeMap)) + for k := range NetworkPolicyTypeMap { + validTypes = append(validTypes, k) + } + return validTypes +} + +var NamespaceScopedPolicyTypes = sets.New[string]("ANNP", "K8SNP") + // ServiceExternalIPStatusQuerier queries the Service external IP status for debugging purposes. // Ideally, every Node should have consistent results eventually. This should only be used when // ServiceExternalIP feature is enabled.