diff --git a/build/images/ovs/apply-patches.sh b/build/images/ovs/apply-patches.sh index aa62b5a53ed..a4ef831669d 100755 --- a/build/images/ovs/apply-patches.sh +++ b/build/images/ovs/apply-patches.sh @@ -99,6 +99,13 @@ if version_lt "$OVS_VERSION" "2.18.0" ; then apply_patch "78ff3961ca9fb012eaaca3d3af1e8186fe1827e7" fi +# This patch fixes the issue that TCP port matching and TCP flags matching can't +# take effect when using together. +# See https://github.com/openvswitch/ovs-issues/issues/272 +if version_get "$OVS_VERSION" "2.13.0" && version_let "$OVS_VERSION" "2.17.3" ; then + apply_patch "489553b1c21692063931a9f50b6849b23128443c" +fi + # OVS hardcodes the installation path to /usr/lib/python3.7/dist-packages/ but this location # does not seem to be in the Python path in Ubuntu 22.04. There may be a better way to do this, # but this seems like an acceptable workaround. diff --git a/go.mod b/go.mod index 1d265b1276c..c8c0dc411e2 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module antrea.io/antrea go 1.19 require ( - antrea.io/libOpenflow v0.9.1 - antrea.io/ofnet v0.6.5 + antrea.io/libOpenflow v0.9.2 + antrea.io/ofnet v0.6.9 github.com/ClickHouse/clickhouse-go v1.5.4 github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/Mellanox/sriovnet v1.1.0 @@ -210,5 +210,3 @@ require ( sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect ) - -replace antrea.io/ofnet v0.6.0 => github.com/wenyingd/ofnet v0.0.0-20220817031400-cb451467adc1 diff --git a/go.sum b/go.sum index 3f88a082091..bc79dd5fc27 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,7 @@ -antrea.io/libOpenflow v0.8.1/go.mod h1:CzEJZxDNAupiGxeL5VOw92PsxfyvehEAvE3PiC6gr8o= -antrea.io/libOpenflow v0.9.1 h1:nrw7EpGuSgi932xriAHdMYGYdLnnjrj91qXGt/bzUUw= -antrea.io/libOpenflow v0.9.1/go.mod h1:IM9mUfHh5hUNciRRcWYIaWZTlv1TI6QBEHlml7ALdS4= -antrea.io/ofnet v0.6.5 h1:jMnrU2Iva+jn/j2tyHJ1bSmC7HXtMDYVCJb7pq8L37I= -antrea.io/ofnet v0.6.5/go.mod h1:/gjpTqhUpyn8uZnef+ytdCCAeY5oGG1jCr/szPUqVXU= +antrea.io/libOpenflow v0.9.2 h1:9W++nzaxxwY4NxyHHow/4bfum2UPIBJKmEOVTAG+x3o= +antrea.io/libOpenflow v0.9.2/go.mod h1:IM9mUfHh5hUNciRRcWYIaWZTlv1TI6QBEHlml7ALdS4= +antrea.io/ofnet v0.6.9 h1:ACoDhFhSHfNtuBKffvptspZDwKe+EQ5i35PuDUZ8svk= +antrea.io/ofnet v0.6.9/go.mod h1:CB/Pkt+U0Yi1sM7DZ7iS215xGL+dhRRAM0EV0LTDLnY= bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= diff --git a/pkg/agent/controller/networkpolicy/fqdn.go b/pkg/agent/controller/networkpolicy/fqdn.go index 48a7eb3b70e..7cbaa2e50c0 100644 --- a/pkg/agent/controller/networkpolicy/fqdn.go +++ b/pkg/agent/controller/networkpolicy/fqdn.go @@ -169,7 +169,7 @@ func newFQDNController(client openflow.Client, allocator *idAllocator, dnsServer gwPort: gwPort, } if controller.ofClient != nil { - if err := controller.ofClient.NewDNSpacketInConjunction(dnsInterceptRuleID); err != nil { + if err := controller.ofClient.NewDNSPacketInConjunction(dnsInterceptRuleID); err != nil { return nil, fmt.Errorf("failed to install flow for DNS response interception: %w", err) } } @@ -726,7 +726,7 @@ func (f *fqdnController) makeDNSRequest(ctx context.Context, fqdn string) error return nil } -// implements openflow.PacketInHandler +// HandlePacketIn implements openflow.PacketInHandler func (f *fqdnController) HandlePacketIn(pktIn *ofctrl.PacketIn) error { matches := pktIn.GetMatches() // Get custom reasons in this packet-in. @@ -746,11 +746,11 @@ func (f *fqdnController) HandlePacketIn(pktIn *ofctrl.PacketIn) error { func (f *fqdnController) handlePacketIn(pktIn *ofctrl.PacketIn) error { klog.V(4).InfoS("Received a packetIn for DNS response") waitCh := make(chan error, 1) - handleUDPData := func(dnsPkt *protocol.UDP) { - dnsData := dnsPkt.Data + handleDNSData := func(dnsData []byte) { dnsMsg := dns.Msg{} if err := dnsMsg.Unpack(dnsData); err != nil { - waitCh <- err + // A non-DNS response packet or a fragmented DNS response is received. Forward it to the Pod. + waitCh <- nil return } f.onDNSResponseMsg(&dnsMsg, time.Now(), waitCh) @@ -758,18 +758,52 @@ func (f *fqdnController) handlePacketIn(pktIn *ofctrl.PacketIn) error { go func() { ethernetPkt, err := getEthernetPacket(pktIn) if err != nil { + // Can't parse the packet. Forward it to the Pod. + waitCh <- nil return } switch ipPkt := ethernetPkt.Data.(type) { case *protocol.IPv4: - switch dnsPkt := ipPkt.Data.(type) { - case *protocol.UDP: - handleUDPData(dnsPkt) + proto := ipPkt.Protocol + switch proto { + case protocol.Type_UDP: + udpPkt := ipPkt.Data.(*protocol.UDP) + handleDNSData(udpPkt.Data) + case protocol.Type_TCP: + tcpPkt, err := binding.GetTCPPacketFromIPMessage(ipPkt) + if err != nil { + // Can't parse the packet. Forward it to the Pod. + waitCh <- nil + return + } + dnsData, err := binding.GetTCPDNSData(tcpPkt) + if err != nil { + // A non-DNS response packet is received or a fragmented DNS response is received. Forward it to the Pod. + waitCh <- nil + return + } + handleDNSData(dnsData) } case *protocol.IPv6: - switch dnsPkt := ipPkt.Data.(type) { - case *protocol.UDP: - handleUDPData(dnsPkt) + proto := ipPkt.NextHeader + switch proto { + case protocol.Type_UDP: + udpPkt := ipPkt.Data.(*protocol.UDP) + handleDNSData(udpPkt.Data) + case protocol.Type_TCP: + tcpPkt, err := binding.GetTCPPacketFromIPMessage(ipPkt) + if err != nil { + // Can't parse the packet. Forward it to the Pod. + waitCh <- nil + return + } + dnsData, err := binding.GetTCPDNSData(tcpPkt) + if err != nil { + // A non-DNS response packet is received or a fragmented DNS response is received. Forward it to the Pod. + waitCh <- nil + return + } + handleDNSData(dnsData) } } }() @@ -780,74 +814,29 @@ func (f *fqdnController) handlePacketIn(pktIn *ofctrl.PacketIn) error { if err != nil { return fmt.Errorf("error when syncing up rules for DNS reply, dropping packet: %v", err) } - klog.V(2).InfoS("Rule sync is successful or not needed, forwarding DNS response to Pod") + klog.V(2).InfoS("Rule sync is successful or not needed or a non-DNS response packet or a fragmented DNS response was received, forwarding the packet to Pod") return f.sendDNSPacketout(pktIn) } } // sendDNSPacketout forwards the DNS response packet to the original requesting client. func (f *fqdnController) sendDNSPacketout(pktIn *ofctrl.PacketIn) error { - var ( - packetData []byte - srcIP, dstIP string - prot uint8 - isIPv6 bool - ) ethernetPkt, err := getEthernetPacket(pktIn) if err != nil { return err } - switch ipPkt := ethernetPkt.Data.(type) { - case *protocol.IPv4: - srcIP = ipPkt.NWSrc.String() - dstIP = ipPkt.NWDst.String() - prot = ipPkt.Protocol - isIPv6 = false - switch dnsPkt := ipPkt.Data.(type) { - case *protocol.UDP: - packetData = dnsPkt.Data - } - case *protocol.IPv6: - srcIP = ipPkt.NWSrc.String() - dstIP = ipPkt.NWDst.String() - prot = ipPkt.NextHeader - isIPv6 = true - switch dnsPkt := ipPkt.Data.(type) { - case *protocol.UDP: - packetData = dnsPkt.Data - } - } - if prot == protocol.Type_UDP { - inPort := f.gwPort - if inPort == 0 { - // Use the original in_port number in the packetIn message to avoid an invalid input port number. Note that, - // this should not happen in container case as antrea-gw0 always exists. This check is for security. - matches := pktIn.GetMatches() - inPortField := matches.GetMatchByName("OXM_OF_IN_PORT") - if inPortField != nil { - inPort = inPortField.GetValue().(uint32) - } - } - udpSrcPort, udpDstPort, err := binding.GetUDPHeaderData(ethernetPkt.Data) - if err != nil { - klog.ErrorS(err, "Failed to get UDP header data") - return err + inPort := f.gwPort + if inPort == 0 { + // Use the original in_port number in the packetIn message to avoid an invalid input port number. Note that, + // this should not happen in container case as antrea-gw0 always exists. This check is for security. + matches := pktIn.GetMatches() + inPortField := matches.GetMatchByName("OXM_OF_IN_PORT") + if inPortField != nil { + inPort = inPortField.GetValue().(uint32) } - mutatePacketOut := func(packetOutBuilder binding.PacketOutBuilder) binding.PacketOutBuilder { - return packetOutBuilder.AddLoadRegMark(openflow.CustomReasonDNSRegMark) - } - return f.ofClient.SendUDPPacketOut( - ethernetPkt.HWSrc.String(), - ethernetPkt.HWDst.String(), - srcIP, - dstIP, - inPort, - 0, - isIPv6, - udpSrcPort, - udpDstPort, - packetData, - mutatePacketOut) } - return nil + mutatePacketOut := func(packetOutBuilder binding.PacketOutBuilder) binding.PacketOutBuilder { + return packetOutBuilder.AddLoadRegMark(openflow.CustomReasonDNSRegMark) + } + return f.ofClient.SendEthPacketOut(inPort, 0, ethernetPkt, mutatePacketOut) } diff --git a/pkg/agent/controller/networkpolicy/fqdn_test.go b/pkg/agent/controller/networkpolicy/fqdn_test.go index 85fdb0de35c..715ed3f7180 100644 --- a/pkg/agent/controller/networkpolicy/fqdn_test.go +++ b/pkg/agent/controller/networkpolicy/fqdn_test.go @@ -31,7 +31,7 @@ import ( func newMockFQDNController(t *testing.T, controller *gomock.Controller, dnsServer *string) (*fqdnController, *openflowtest.MockClient) { mockOFClient := openflowtest.NewMockClient(controller) - mockOFClient.EXPECT().NewDNSpacketInConjunction(gomock.Any()).Return(nil).AnyTimes() + mockOFClient.EXPECT().NewDNSPacketInConjunction(gomock.Any()).Return(nil).AnyTimes() dirtyRuleHandler := func(rule string) {} dnsServerAddr := "8.8.8.8:53" // dummy DNS server, will not be used since we don't send any request in these tests if dnsServer != nil { diff --git a/pkg/agent/controller/networkpolicy/reject.go b/pkg/agent/controller/networkpolicy/reject.go index ea43b6e209d..5581ac15790 100644 --- a/pkg/agent/controller/networkpolicy/reject.go +++ b/pkg/agent/controller/networkpolicy/reject.go @@ -193,7 +193,7 @@ func (c *Controller) rejectRequest(pktIn *ofctrl.PacketIn) error { if proto == protocol.Type_TCP { // Get TCP data. - oriTCPSrcPort, oriTCPDstPort, oriTCPSeqNum, _, _, err := binding.GetTCPHeaderData(ethernetPkt.Data) + oriTCPSrcPort, oriTCPDstPort, oriTCPSeqNum, _, _, _, _, err := binding.GetTCPHeaderData(ethernetPkt.Data) if err != nil { return err } @@ -209,8 +209,12 @@ func (c *Controller) rejectRequest(pktIn *ofctrl.PacketIn) error { isIPv6, oriTCPDstPort, oriTCPSrcPort, + 0, oriTCPSeqNum+1, + 0, TCPAck|TCPRst, + 0, + nil, mutateFunc) } // Use ICMP host administratively prohibited for ICMP, UDP, SCTP reject. diff --git a/pkg/agent/openflow/client.go b/pkg/agent/openflow/client.go index 092c12d55e5..a435cc2deb0 100644 --- a/pkg/agent/openflow/client.go +++ b/pkg/agent/openflow/client.go @@ -243,8 +243,12 @@ type Client interface { isIPv6 bool, tcpSrcPort uint16, tcpDstPort uint16, + tcpSeqNum uint32, tcpAckNum uint32, + tcpHdrLen uint8, tcpFlag uint8, + tcpWinSize uint16, + tcpData []byte, mutatePacketOut func(builder binding.PacketOutBuilder) binding.PacketOutBuilder) error // SendICMPPacketOut sends ICMP packet as a packet-out to OVS. SendICMPPacketOut( @@ -272,8 +276,10 @@ type Client interface { udpDstPort uint16, udpData []byte, mutatePacketOut func(builder binding.PacketOutBuilder) binding.PacketOutBuilder) error - // NewDNSpacketInConjunction creates a policyRuleConjunction for the dns response interception flows. - NewDNSpacketInConjunction(id uint32) error + // SendEthPacketOut sends ethernet packet as a packet-out to OVS. + SendEthPacketOut(inPort, outPort uint32, ethPkt *protocol.Ethernet, mutatePacketOut func(builder binding.PacketOutBuilder) binding.PacketOutBuilder) error + // NewDNSPacketInConjunction creates a policyRuleConjunction for the dns response interception flows. + NewDNSPacketInConjunction(id uint32) error // AddAddressToDNSConjunction adds addresses to the toAddresses of the dns packetIn conjunction, // so that dns response packets sent towards these addresses will be intercepted and parsed by // the fqdnController. @@ -1150,8 +1156,12 @@ func (c *client) SendTCPPacketOut( isIPv6 bool, tcpSrcPort uint16, tcpDstPort uint16, + tcpSeqNum uint32, tcpAckNum uint32, + tcpHdrLen uint8, tcpFlag uint8, + tcpWinSize uint16, + tcpData []byte, mutatePacketOut func(builder binding.PacketOutBuilder) binding.PacketOutBuilder) error { // Generate a base IP PacketOutBuilder. packetOutBuilder, err := setBasePacketOutBuilder(c.bridge.BuildPacketOut(), srcMAC, dstMAC, srcIP, dstIP, inPort, outPort) @@ -1165,10 +1175,15 @@ func (c *client) SendTCPPacketOut( packetOutBuilder = packetOutBuilder.SetIPProtocol(binding.ProtocolTCP) } // Set TCP header data. - packetOutBuilder = packetOutBuilder.SetTCPSrcPort(tcpSrcPort) - packetOutBuilder = packetOutBuilder.SetTCPDstPort(tcpDstPort) - packetOutBuilder = packetOutBuilder.SetTCPAckNum(tcpAckNum) - packetOutBuilder = packetOutBuilder.SetTCPFlags(tcpFlag) + packetOutBuilder = packetOutBuilder. + SetTCPSrcPort(tcpSrcPort). + SetTCPDstPort(tcpDstPort). + SetTCPSeqNum(tcpSeqNum). + SetTCPAckNum(tcpAckNum). + SetTCPHdrLen(tcpHdrLen). + SetTCPFlags(tcpFlag). + SetTCPWinSize(tcpWinSize). + SetTCPData(tcpData) if mutatePacketOut != nil { packetOutBuilder = mutatePacketOut(packetOutBuilder) @@ -1252,6 +1267,20 @@ func (c *client) SendUDPPacketOut( return c.bridge.SendPacketOut(packetOutObj) } +func (c *client) SendEthPacketOut(inPort, outPort uint32, ethPkt *protocol.Ethernet, mutatePacketOut func(builder binding.PacketOutBuilder) binding.PacketOutBuilder) error { + packetOutBuilder := c.bridge.BuildPacketOut() + packetOutBuilder = packetOutBuilder.SetInport(inPort) + if outPort != 0 { + packetOutBuilder = packetOutBuilder.SetOutport(outPort) + } + if mutatePacketOut != nil { + packetOutBuilder = mutatePacketOut(packetOutBuilder) + } + packetOutBuilder.SetEthPacket(ethPkt) + packetOutObj := packetOutBuilder.Done() + return c.bridge.SendPacketOut(packetOutObj) +} + func (c *client) InstallMulticastFlows(multicastIP net.IP, groupID binding.GroupIDType) error { flows := c.featureMulticast.localMulticastForwardFlows(multicastIP, groupID) cacheKey := fmt.Sprintf("multicast_%s", multicastIP.String()) diff --git a/pkg/agent/openflow/client_test.go b/pkg/agent/openflow/client_test.go index 98d8e41ee75..05e0a1ce17c 100644 --- a/pkg/agent/openflow/client_test.go +++ b/pkg/agent/openflow/client_test.go @@ -1592,8 +1592,12 @@ func Test_client_SendPacketOut(t *testing.T) { igmp util.Message tcpSrcPort uint16 tcpDstPort uint16 + tcpSeqNum uint32 tcpAckNum uint32 + tcpHdrLen uint8 tcpFlag uint8 + tcpWinSize uint16 + tcpData []byte udpSrcPort uint16 udpDstPort uint16 udpData []byte @@ -1603,8 +1607,12 @@ func Test_client_SendPacketOut(t *testing.T) { protocol: binding.ProtocolTCP, tcpSrcPort: uint16(50000), tcpDstPort: uint16(80), + tcpSeqNum: uint32(7654321), tcpAckNum: uint32(1234567), + tcpHdrLen: uint8(5), tcpFlag: uint8(0b000100), + tcpWinSize: uint16(123), + tcpData: []byte{1, 2, 3}, }, { name: "SendTCPPacketOut IPv6", @@ -1612,8 +1620,12 @@ func Test_client_SendPacketOut(t *testing.T) { isIPv6: true, tcpSrcPort: uint16(50000), tcpDstPort: uint16(443), + tcpSeqNum: uint32(7654321), tcpAckNum: uint32(1234567), + tcpHdrLen: uint8(8), tcpFlag: uint8(0b000100), + tcpWinSize: uint16(123), + tcpData: []byte{1, 2, 3}, }, { name: "SendUDPPacketOut IPv4", @@ -1712,8 +1724,12 @@ func Test_client_SendPacketOut(t *testing.T) { case binding.ProtocolTCP, binding.ProtocolTCPv6: mockPacketOutBuilder.EXPECT().SetTCPSrcPort(tc.tcpSrcPort).Return(mockPacketOutBuilder) mockPacketOutBuilder.EXPECT().SetTCPDstPort(tc.tcpDstPort).Return(mockPacketOutBuilder) + mockPacketOutBuilder.EXPECT().SetTCPSeqNum(tc.tcpSeqNum).Return(mockPacketOutBuilder) mockPacketOutBuilder.EXPECT().SetTCPAckNum(tc.tcpAckNum).Return(mockPacketOutBuilder) + mockPacketOutBuilder.EXPECT().SetTCPHdrLen(tc.tcpHdrLen).Return(mockPacketOutBuilder) mockPacketOutBuilder.EXPECT().SetTCPFlags(tc.tcpFlag).Return(mockPacketOutBuilder) + mockPacketOutBuilder.EXPECT().SetTCPWinSize(tc.tcpWinSize).Return(mockPacketOutBuilder) + mockPacketOutBuilder.EXPECT().SetTCPData(tc.tcpData).Return(mockPacketOutBuilder) assert.NoError(t, fc.SendTCPPacketOut(srcMAC.String(), dstMAC.String(), srcIP.String(), @@ -1723,8 +1739,12 @@ func Test_client_SendPacketOut(t *testing.T) { tc.isIPv6, tc.tcpSrcPort, tc.tcpDstPort, + tc.tcpSeqNum, tc.tcpAckNum, + tc.tcpHdrLen, tc.tcpFlag, + tc.tcpWinSize, + tc.tcpData, nil)) case binding.ProtocolUDP, binding.ProtocolUDPv6: mockPacketOutBuilder.EXPECT().SetUDPSrcPort(tc.udpSrcPort).Return(mockPacketOutBuilder) diff --git a/pkg/agent/openflow/network_policy.go b/pkg/agent/openflow/network_policy.go index 4a72cc9fb00..dbd5bf33c84 100644 --- a/pkg/agent/openflow/network_policy.go +++ b/pkg/agent/openflow/network_policy.go @@ -71,6 +71,8 @@ var ( MatchServiceGroupID = types.NewMatchKey(binding.ProtocolIP, types.ServiceGroupIDAddr, "reg7[0..31]") MatchIGMPProtocol = types.NewMatchKey(binding.ProtocolIGMP, types.IGMPAddr, "igmp") MatchLabelID = types.NewMatchKey(binding.ProtocolIP, types.LabelIDAddr, "tun_id") + MatchTCPFlags = types.NewMatchKey(binding.ProtocolTCP, types.TCPFlagsAddr, "tcp_flags") + MatchTCPv6Flags = types.NewMatchKey(binding.ProtocolTCPv6, types.TCPFlagsAddr, "tcp_flags") Unsupported = types.NewMatchKey(binding.ProtocolIP, types.UnSupported, "unknown") // metricFlowIdentifier is used to identify metric flows in metric table. @@ -79,9 +81,15 @@ var ( metricFlowIdentifier = fmt.Sprintf("priority=%d,", priorityNormal) protocolUDP = v1beta2.ProtocolUDP + protocolTCP = v1beta2.ProtocolTCP dnsPort = intstr.FromInt(53) ) +type TCPFlags struct { + Flag uint16 + Mask uint16 +} + // IP address calculated from Pod's address. type IPAddress net.IP @@ -682,7 +690,7 @@ type clause struct { dropTable binding.Table } -func (c *client) NewDNSpacketInConjunction(id uint32) error { +func (c *client) NewDNSPacketInConjunction(id uint32) error { existingConj := c.featureNetworkPolicy.getPolicyRuleConjunction(id) if existingConj != nil { klog.InfoS("DNS Conjunction has already been added to cache", "id", id) @@ -703,13 +711,40 @@ func (c *client) NewDNSpacketInConjunction(id uint32) error { Protocol: &protocolUDP, Port: &dnsPort, } + tcpService := v1beta2.Service{ + Protocol: &protocolTCP, + Port: &dnsPort, + } dnsPriority := priorityDNSIntercept conj.serviceClause = conj.newClause(1, 2, getTableByID(conj.ruleTableID), nil) conj.toClause = conj.newClause(2, 2, getTableByID(conj.ruleTableID), nil) - c.featureNetworkPolicy.conjMatchFlowLock.Lock() defer c.featureNetworkPolicy.conjMatchFlowLock.Unlock() ctxChanges := conj.serviceClause.addServiceFlows(c.featureNetworkPolicy, []v1beta2.Service{udpService}, &dnsPriority, true, false) + dnsTCPMatchPairs := getServiceMatchPairs(tcpService, c.featureNetworkPolicy.ipProtocols, true) + for _, dnsTCPMatchPair := range dnsTCPMatchPairs { + tcpFlagsMatchPair := matchPair{ + matchKey: MatchTCPFlags, + matchValue: TCPFlags{ + // URG|ACK|PSH|RST|SYN|FIN| + Flag: 0b011000, + Mask: 0b011000, + }, + } + if dnsTCPMatchPair[0].matchKey.GetOFProtocol() == binding.ProtocolTCPv6 { + tcpFlagsMatchPair.matchKey = MatchTCPv6Flags + } + tcpServiceMatch := &conjunctiveMatch{ + tableID: conj.serviceClause.ruleTable.GetID(), + matchPairs: []matchPair{ + dnsTCPMatchPair[0], + tcpFlagsMatchPair, + }, + priority: &dnsPriority, + } + ctxChange := conj.serviceClause.addConjunctiveMatchFlow(c.featureNetworkPolicy, tcpServiceMatch, false, false) + ctxChanges = append(ctxChanges, ctxChange) + } if err := c.featureNetworkPolicy.applyConjunctiveMatchFlows(ctxChanges); err != nil { return err } diff --git a/pkg/agent/openflow/network_policy_test.go b/pkg/agent/openflow/network_policy_test.go index 284e7e416ed..b629aa42a14 100644 --- a/pkg/agent/openflow/network_policy_test.go +++ b/pkg/agent/openflow/network_policy_test.go @@ -65,7 +65,6 @@ var ( actionAllow = crdv1alpha1.RuleActionAllow actionDrop = crdv1alpha1.RuleActionDrop port8080 = intstr.FromInt(8080) - protocolTCP = v1beta2.ProtocolTCP protocolICMP = v1beta2.ProtocolICMP priority100 = uint16(100) priority200 = uint16(200) @@ -174,7 +173,7 @@ func TestInstallPolicyRuleFlows(t *testing.T) { // Create a policyRuleConjunction for the dns response interception flows // to ensure nil NetworkPolicyReference is handled correctly by GetNetworkPolicyFlowKeys. dnsID := uint32(1) - require.NoError(t, c.NewDNSpacketInConjunction(dnsID)) + require.NoError(t, c.NewDNSPacketInConjunction(dnsID)) ruleID1 := uint32(101) rule1 := &types.PolicyRule{ diff --git a/pkg/agent/openflow/pipeline.go b/pkg/agent/openflow/pipeline.go index ae707f20301..0098792d20a 100644 --- a/pkg/agent/openflow/pipeline.go +++ b/pkg/agent/openflow/pipeline.go @@ -2010,6 +2010,12 @@ func (f *featureNetworkPolicy) addFlowMatch(fb binding.FlowBuilder, matchKey *ty fb = fb.MatchProtocol(matchKey.GetOFProtocol()) case MatchLabelID: fb = fb.MatchTunnelID(uint64(matchValue.(uint32))) + case MatchTCPFlags: + fallthrough + case MatchTCPv6Flags: + fb = fb.MatchProtocol(matchKey.GetOFProtocol()) + tcpFlag := matchValue.(TCPFlags) + fb = fb.MatchTCPFlags(tcpFlag.Flag, tcpFlag.Mask) } return fb } diff --git a/pkg/agent/openflow/testing/mock_openflow.go b/pkg/agent/openflow/testing/mock_openflow.go index 31549ad080b..8d2cd26d97f 100644 --- a/pkg/agent/openflow/testing/mock_openflow.go +++ b/pkg/agent/openflow/testing/mock_openflow.go @@ -26,6 +26,7 @@ import ( openflow "antrea.io/antrea/pkg/ovs/openflow" ip "antrea.io/antrea/pkg/util/ip" proxy "antrea.io/antrea/third_party/proxy" + protocol "antrea.io/libOpenflow/protocol" util "antrea.io/libOpenflow/util" gomock "github.com/golang/mock/gomock" v1 "k8s.io/api/core/v1" @@ -619,18 +620,18 @@ func (mr *MockClientMockRecorder) NetworkPolicyMetrics() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkPolicyMetrics", reflect.TypeOf((*MockClient)(nil).NetworkPolicyMetrics)) } -// NewDNSpacketInConjunction mocks base method -func (m *MockClient) NewDNSpacketInConjunction(arg0 uint32) error { +// NewDNSPacketInConjunction mocks base method +func (m *MockClient) NewDNSPacketInConjunction(arg0 uint32) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NewDNSpacketInConjunction", arg0) + ret := m.ctrl.Call(m, "NewDNSPacketInConjunction", arg0) ret0, _ := ret[0].(error) return ret0 } -// NewDNSpacketInConjunction indicates an expected call of NewDNSpacketInConjunction -func (mr *MockClientMockRecorder) NewDNSpacketInConjunction(arg0 interface{}) *gomock.Call { +// NewDNSPacketInConjunction indicates an expected call of NewDNSPacketInConjunction +func (mr *MockClientMockRecorder) NewDNSPacketInConjunction(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewDNSpacketInConjunction", reflect.TypeOf((*MockClient)(nil).NewDNSpacketInConjunction), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewDNSPacketInConjunction", reflect.TypeOf((*MockClient)(nil).NewDNSPacketInConjunction), arg0) } // ReassignFlowPriorities mocks base method @@ -671,6 +672,20 @@ func (mr *MockClientMockRecorder) ReplayFlows() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplayFlows", reflect.TypeOf((*MockClient)(nil).ReplayFlows)) } +// SendEthPacketOut mocks base method +func (m *MockClient) SendEthPacketOut(arg0, arg1 uint32, arg2 *protocol.Ethernet, arg3 func(openflow.PacketOutBuilder) openflow.PacketOutBuilder) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendEthPacketOut", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendEthPacketOut indicates an expected call of SendEthPacketOut +func (mr *MockClientMockRecorder) SendEthPacketOut(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendEthPacketOut", reflect.TypeOf((*MockClient)(nil).SendEthPacketOut), arg0, arg1, arg2, arg3) +} + // SendICMPPacketOut mocks base method func (m *MockClient) SendICMPPacketOut(arg0, arg1, arg2, arg3 string, arg4, arg5 uint32, arg6 bool, arg7, arg8 byte, arg9 []byte, arg10 func(openflow.PacketOutBuilder) openflow.PacketOutBuilder) error { m.ctrl.T.Helper() @@ -714,17 +729,17 @@ func (mr *MockClientMockRecorder) SendIGMPRemoteReportPacketOut(arg0, arg1, arg2 } // SendTCPPacketOut mocks base method -func (m *MockClient) SendTCPPacketOut(arg0, arg1, arg2, arg3 string, arg4, arg5 uint32, arg6 bool, arg7, arg8 uint16, arg9 uint32, arg10 byte, arg11 func(openflow.PacketOutBuilder) openflow.PacketOutBuilder) error { +func (m *MockClient) SendTCPPacketOut(arg0, arg1, arg2, arg3 string, arg4, arg5 uint32, arg6 bool, arg7, arg8 uint16, arg9, arg10 uint32, arg11, arg12 byte, arg13 uint16, arg14 []byte, arg15 func(openflow.PacketOutBuilder) openflow.PacketOutBuilder) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SendTCPPacketOut", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11) + ret := m.ctrl.Call(m, "SendTCPPacketOut", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15) ret0, _ := ret[0].(error) return ret0 } // SendTCPPacketOut indicates an expected call of SendTCPPacketOut -func (mr *MockClientMockRecorder) SendTCPPacketOut(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11 interface{}) *gomock.Call { +func (mr *MockClientMockRecorder) SendTCPPacketOut(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendTCPPacketOut", reflect.TypeOf((*MockClient)(nil).SendTCPPacketOut), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendTCPPacketOut", reflect.TypeOf((*MockClient)(nil).SendTCPPacketOut), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15) } // SendTraceflowPacket mocks base method diff --git a/pkg/agent/types/networkpolicy.go b/pkg/agent/types/networkpolicy.go index 90bcc6a275f..66db9c09ff2 100644 --- a/pkg/agent/types/networkpolicy.go +++ b/pkg/agent/types/networkpolicy.go @@ -57,6 +57,7 @@ const ( ServiceGroupIDAddr IGMPAddr LabelIDAddr + TCPFlagsAddr UnSupported ) diff --git a/pkg/ovs/openflow/interfaces.go b/pkg/ovs/openflow/interfaces.go index 8bcc41a33d6..f9656162a0a 100644 --- a/pkg/ovs/openflow/interfaces.go +++ b/pkg/ovs/openflow/interfaces.go @@ -18,6 +18,7 @@ import ( "net" "time" + "antrea.io/libOpenflow/protocol" "antrea.io/libOpenflow/util" "antrea.io/ofnet/ofctrl" ) @@ -281,6 +282,7 @@ type FlowBuilder interface { MatchConjID(value uint32) FlowBuilder MatchDstPort(port uint16, portMask *uint16) FlowBuilder MatchSrcPort(port uint16, portMask *uint16) FlowBuilder + MatchTCPFlags(flag, mask uint16) FlowBuilder MatchICMPType(icmpType byte) FlowBuilder MatchICMPCode(icmpCode byte) FlowBuilder MatchICMPv6Type(icmp6Type byte) FlowBuilder @@ -402,6 +404,9 @@ type PacketOutBuilder interface { SetTCPFlags(flags uint8) PacketOutBuilder SetTCPSeqNum(seqNum uint32) PacketOutBuilder SetTCPAckNum(ackNum uint32) PacketOutBuilder + SetTCPHdrLen(hdrLen uint8) PacketOutBuilder + SetTCPWinSize(winSize uint16) PacketOutBuilder + SetTCPData(data []byte) PacketOutBuilder SetUDPSrcPort(port uint16) PacketOutBuilder SetUDPDstPort(port uint16) PacketOutBuilder SetUDPData(data []byte) PacketOutBuilder @@ -418,6 +423,7 @@ type PacketOutBuilder interface { AddLoadRegMark(mark *RegMark) PacketOutBuilder AddResubmitAction(inPort *uint16, table *uint8) PacketOutBuilder SetL4Packet(packet util.Message) PacketOutBuilder + SetEthPacket(packet *protocol.Ethernet) PacketOutBuilder Done() *ofctrl.PacketOut } diff --git a/pkg/ovs/openflow/ofctrl_builder.go b/pkg/ovs/openflow/ofctrl_builder.go index 456873d17b1..8d9bd573d5e 100644 --- a/pkg/ovs/openflow/ofctrl_builder.go +++ b/pkg/ovs/openflow/ofctrl_builder.go @@ -598,6 +598,13 @@ func (b *ofFlowBuilder) MatchSrcPort(port uint16, portMask *uint16) FlowBuilder return b } +func (b *ofFlowBuilder) MatchTCPFlags(flag, mask uint16) FlowBuilder { + b.matchers = append(b.matchers, fmt.Sprintf("tcp_flags=%b/%b", uint8(flag), uint8(mask))) + b.Match.TcpFlags = &flag + b.Match.TcpFlagsMask = &mask + return b +} + // MatchCTSrcIP matches the source IPv4 address of the connection tracker original direction tuple. This match requires // a match to valid connection tracking state as a prerequisite, and valid connection tracking state matches include // "+new", "+est", "+rel" and "+trk-inv". diff --git a/pkg/ovs/openflow/ofctrl_packetin.go b/pkg/ovs/openflow/ofctrl_packetin.go index 61ae8199b0a..1c6406def45 100644 --- a/pkg/ovs/openflow/ofctrl_packetin.go +++ b/pkg/ovs/openflow/ofctrl_packetin.go @@ -17,6 +17,9 @@ package openflow import ( "encoding/binary" "errors" + "fmt" + + "k8s.io/klog/v2" "antrea.io/libOpenflow/protocol" "antrea.io/libOpenflow/util" @@ -26,10 +29,20 @@ import ( const ( icmpEchoRequestType uint8 = 8 icmp6EchoRequestType uint8 = 128 + // tcpStandardHdrLen is the TCP header length without options. + tcpStandardHdrLen uint8 = 5 ) -// GetTCPHeaderData gets TCP header data from IP packet. -func GetTCPHeaderData(ipPkt util.Message) (tcpSrcPort, tcpDstPort uint16, tcpSeqNum, tcpAckNum uint32, tcpFlags uint8, err error) { +func GetTCPHeaderData(ipPkt util.Message) (tcpSrcPort, tcpDstPort uint16, tcpSeqNum, tcpAckNum uint32, tcpHdrLen uint8, tcpFlags uint8, tcpWinSize uint16, err error) { + tcpIn, err := GetTCPPacketFromIPMessage(ipPkt) + if err != nil { + return 0, 0, 0, 0, 0, 0, 0, err + } + return tcpIn.PortSrc, tcpIn.PortDst, tcpIn.SeqNum, tcpIn.AckNum, tcpIn.HdrLen, tcpIn.Code, tcpIn.WinSize, nil +} + +// GetTCPPacketFromIPMessage gets a TCP struct from an IP message. +func GetTCPPacketFromIPMessage(ipPkt util.Message) (tcpPkt *protocol.TCP, err error) { var tcpBytes []byte // Transfer Buffer to TCP @@ -40,15 +53,36 @@ func GetTCPHeaderData(ipPkt util.Message) (tcpSrcPort, tcpDstPort uint16, tcpSeq tcpBytes, err = typedIPPkt.Data.(*util.Buffer).MarshalBinary() } if err != nil { - return 0, 0, 0, 0, 0, err + return nil, err } - tcpIn := new(protocol.TCP) - err = tcpIn.UnmarshalBinary(tcpBytes) + tcpPkt = new(protocol.TCP) + err = tcpPkt.UnmarshalBinary(tcpBytes) if err != nil { - return 0, 0, 0, 0, 0, err + return nil, err } - return tcpIn.PortSrc, tcpIn.PortDst, tcpIn.SeqNum, tcpIn.AckNum, tcpIn.Code, nil + return tcpPkt, nil +} + +func GetTCPDNSData(tcpPkt *protocol.TCP) (data []byte, err error) { + // TCP.HdrLen is 4-octet unit indicating the length of TCP header including options. + tcpOptionsLen := (tcpPkt.HdrLen - tcpStandardHdrLen) * 4 + // Move two more octet. + // From RFC 7766: + // DNS clients and servers SHOULD pass the two-octet length field, and + // the message described by that length field, to the TCP layer at the + // same time (e.g., in a single "write" system call) to make it more + // likely that all the data will be transmitted in a single TCP segment. + if int(tcpOptionsLen+2) > len(tcpPkt.Data) { + return nil, fmt.Errorf("no DNS data in TCP data") + } + dnsDataLen := binary.BigEndian.Uint16(tcpPkt.Data[tcpOptionsLen : tcpOptionsLen+2]) + dnsData := tcpPkt.Data[tcpOptionsLen+2:] + if int(dnsDataLen) > len(dnsData) { + klog.Info("There is a non-DNS response or a fragmented DNS response in TCP payload") + return nil, fmt.Errorf("there is a non-DNS response or a fragmented DNS response in TCP payload") + } + return dnsData, nil } func GetUDPHeaderData(ipPkt util.Message) (udpSrcPort, udpDstPort uint16, err error) { @@ -122,7 +156,7 @@ func ParsePacketIn(pktIn *ofctrl.PacketIn) (*Packet, error) { var err error if packet.IPProto == protocol.Type_TCP { - packet.SourcePort, packet.DestinationPort, _, _, packet.TCPFlags, err = GetTCPHeaderData(ethernetData.Data) + packet.SourcePort, packet.DestinationPort, _, _, _, packet.TCPFlags, _, err = GetTCPHeaderData(ethernetData.Data) } else if packet.IPProto == protocol.Type_UDP { packet.SourcePort, packet.DestinationPort, err = GetUDPHeaderData(ethernetData.Data) } else if packet.IPProto == protocol.Type_ICMP || packet.IPProto == protocol.Type_IPv6ICMP { diff --git a/pkg/ovs/openflow/ofctrl_packetin_test.go b/pkg/ovs/openflow/ofctrl_packetin_test.go index 536f0fb8174..6c5a554b480 100644 --- a/pkg/ovs/openflow/ofctrl_packetin_test.go +++ b/pkg/ovs/openflow/ofctrl_packetin_test.go @@ -15,6 +15,7 @@ package openflow import ( + "fmt" "net" "testing" @@ -33,7 +34,9 @@ func TestGetTCPHeaderData(t *testing.T) { expectTCPDstPort uint16 expectTCPSeqNum uint32 expectTCPAckNum uint32 + expectTCPHdrLen uint8 expectTCPCode uint8 + expectTCPWinSize uint16 } tests := []struct { name string @@ -47,13 +50,17 @@ func TestGetTCPHeaderData(t *testing.T) { PortDst: 80, SeqNum: 0, AckNum: 0, + HdrLen: 5, Code: 2, + WinSize: 0, }, expectTCPSrcPort: 1080, expectTCPDstPort: 80, expectTCPSeqNum: 0, expectTCPAckNum: 0, + expectTCPHdrLen: 5, expectTCPCode: 2, + expectTCPWinSize: 0, }, }, } @@ -66,13 +73,15 @@ func TestGetTCPHeaderData(t *testing.T) { bf.UnmarshalBinary(bytes) pktIn.Data = bf - tcpSrcPort, tcpDstPort, tcpSeqNum, tcpAckNum, tcpCode, err := GetTCPHeaderData(pktIn) + tcpSrcPort, tcpDstPort, tcpSeqNum, tcpAckNum, tcpHdrLen, tcpCode, tcpWinSize, err := GetTCPHeaderData(pktIn) require.NoError(t, err, "GetTCPHeaderData() returned an error") assert.Equal(t, tt.args.expectTCPSrcPort, tcpSrcPort) assert.Equal(t, tt.args.expectTCPDstPort, tcpDstPort) assert.Equal(t, tt.args.expectTCPSeqNum, tcpSeqNum) assert.Equal(t, tt.args.expectTCPAckNum, tcpAckNum) + assert.Equal(t, tt.args.expectTCPHdrLen, tcpHdrLen) assert.Equal(t, tt.args.expectTCPCode, tcpCode) + assert.Equal(t, tt.args.expectTCPWinSize, tcpWinSize) }) } } @@ -219,3 +228,57 @@ func TestParsePacketIn(t *testing.T) { }) } } + +func TestGetTCPDNSData(t *testing.T) { + type args struct { + tcp protocol.TCP + expectErr error + expectData []byte + } + tests := []struct { + name string + args args + }{ + { + name: "GetTCPDNSDataNoDNS", + args: args{ + tcp: protocol.TCP{ + HdrLen: 6, + Data: []byte{1, 2, 3, 4, 0}, + }, + expectErr: fmt.Errorf("no DNS data in TCP data"), + expectData: nil, + }, + }, + { + name: "GetTCPDNSDataFragmented", + args: args{ + tcp: protocol.TCP{ + HdrLen: 6, + Data: []byte{1, 2, 3, 4, 0, 2, 5}, + }, + expectErr: fmt.Errorf("there is a non-DNS response or a fragmented DNS response in TCP payload"), + expectData: nil, + }, + }, + { + name: "GetTCPDNSDataSuccess", + args: args{ + tcp: protocol.TCP{ + HdrLen: 6, + Data: []byte{1, 2, 3, 4, 0, 1, 5}, + }, + expectErr: nil, + expectData: []byte{5}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tcp := tt.args.tcp + tcpData, err := GetTCPDNSData(&tcp) + assert.Equal(t, tt.args.expectErr, err) + assert.Equal(t, tt.args.expectData, tcpData) + }) + } +} diff --git a/pkg/ovs/openflow/ofctrl_packetout.go b/pkg/ovs/openflow/ofctrl_packetout.go index d119667261e..5af8e1a37e6 100644 --- a/pkg/ovs/openflow/ofctrl_packetout.go +++ b/pkg/ovs/openflow/ofctrl_packetout.go @@ -211,6 +211,30 @@ func (b *ofPacketOutBuilder) SetTCPAckNum(ackNum uint32) PacketOutBuilder { return b } +func (b *ofPacketOutBuilder) SetTCPHdrLen(hdrLen uint8) PacketOutBuilder { + if b.pktOut.TCPHeader == nil { + b.pktOut.TCPHeader = new(protocol.TCP) + } + b.pktOut.TCPHeader.HdrLen = hdrLen + return b +} + +func (b *ofPacketOutBuilder) SetTCPWinSize(winSize uint16) PacketOutBuilder { + if b.pktOut.TCPHeader == nil { + b.pktOut.TCPHeader = new(protocol.TCP) + } + b.pktOut.TCPHeader.WinSize = winSize + return b +} + +func (b *ofPacketOutBuilder) SetTCPData(data []byte) PacketOutBuilder { + if b.pktOut.TCPHeader == nil { + b.pktOut.TCPHeader = new(protocol.TCP) + } + b.pktOut.TCPHeader.Data = data + return b +} + // SetUDPSrcPort sets the source port in the packet's UDP header. func (b *ofPacketOutBuilder) SetUDPSrcPort(port uint16) PacketOutBuilder { if b.pktOut.UDPHeader == nil { @@ -301,6 +325,11 @@ func (b *ofPacketOutBuilder) SetL4Packet(packet util.Message) PacketOutBuilder { return b } +func (b *ofPacketOutBuilder) SetEthPacket(packet *protocol.Ethernet) PacketOutBuilder { + b.pktOut.EthernetPacket = packet + return b +} + // AddSetIPToSAction sets the IP_TOS field in the packet-out message. The action clears the two ECN bits as 0, // and only 2-7 bits of the DSCP field in IP header is set. func (b *ofPacketOutBuilder) AddSetIPTOSAction(data uint8) PacketOutBuilder { @@ -332,6 +361,10 @@ func (b *ofPacketOutBuilder) AddResubmitAction(inPort *uint16, table *uint8) Pac } func (b *ofPacketOutBuilder) Done() *ofctrl.PacketOut { + if b.pktOut.EthernetPacket != nil { + // Entire ethernet packet is provided. No need to fill L3/L4 header. + return b.pktOut + } if b.pktOut.IPHeader != nil && b.pktOut.IPv6Header != nil { klog.Errorf("Invalid PacketOutBuilder: IP header and IPv6 header are not allowed to exist at the same time") return nil @@ -344,7 +377,9 @@ func (b *ofPacketOutBuilder) Done() *ofctrl.PacketOut { b.pktOut.ICMPHeader.Checksum = b.icmpHeaderChecksum() b.pktOut.IPHeader.Length = 20 + b.pktOut.ICMPHeader.Len() } else if b.pktOut.TCPHeader != nil { - b.pktOut.TCPHeader.HdrLen = 5 + if b.pktOut.TCPHeader.HdrLen == 0 { + b.pktOut.TCPHeader.HdrLen = 5 + } if b.pktOut.TCPHeader.SeqNum == 0 { // #nosec G404: random number generator not used for security purposes b.pktOut.TCPHeader.SeqNum = pktRand.Uint32() @@ -387,7 +422,9 @@ func (b *ofPacketOutBuilder) Done() *ofctrl.PacketOut { b.pktOut.ICMPHeader.Checksum = b.icmpHeaderChecksum() b.pktOut.IPv6Header.Length = b.pktOut.ICMPHeader.Len() } else if b.pktOut.TCPHeader != nil { - b.pktOut.TCPHeader.HdrLen = 5 + if b.pktOut.TCPHeader.HdrLen == 0 { + b.pktOut.TCPHeader.HdrLen = 5 + } if b.pktOut.TCPHeader.SeqNum == 0 { // #nosec G404: random number generator not used for security purposes b.pktOut.TCPHeader.SeqNum = pktRand.Uint32() diff --git a/pkg/ovs/openflow/ofctrl_packetout_test.go b/pkg/ovs/openflow/ofctrl_packetout_test.go index a5d93d5e310..63ed129901d 100644 --- a/pkg/ovs/openflow/ofctrl_packetout_test.go +++ b/pkg/ovs/openflow/ofctrl_packetout_test.go @@ -603,6 +603,135 @@ func Test_ofPacketOutBuilder_SetIPFlags(t *testing.T) { } } +func Test_ofPacketOutBuilder_SetTCPHdrLen(t *testing.T) { + tests := []struct { + name string + pktOutBuilder *ofPacketOutBuilder + tcpHdrLen uint8 + want PacketOutBuilder + }{ + { + name: "New TCP header", + pktOutBuilder: &ofPacketOutBuilder{pktOut: new(ofctrl.PacketOut)}, + tcpHdrLen: 5, + want: &ofPacketOutBuilder{ + pktOut: &ofctrl.PacketOut{ + TCPHeader: &protocol.TCP{ + HdrLen: 5, + }, + }, + }, + }, + { + name: "Existing TCP header", + pktOutBuilder: &ofPacketOutBuilder{pktOut: &ofctrl.PacketOut{ + TCPHeader: &protocol.TCP{}, + }}, + tcpHdrLen: 5, + want: &ofPacketOutBuilder{ + pktOut: &ofctrl.PacketOut{ + TCPHeader: &protocol.TCP{ + HdrLen: 5, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.pktOutBuilder.SetTCPHdrLen(tt.tcpHdrLen); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SetTCPHdrLen() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_ofPacketOutBuilder_SetTCPWinSize(t *testing.T) { + tests := []struct { + name string + pktOutBuilder *ofPacketOutBuilder + tcpWinSize uint16 + want PacketOutBuilder + }{ + { + name: "New TCP header", + pktOutBuilder: &ofPacketOutBuilder{pktOut: new(ofctrl.PacketOut)}, + tcpWinSize: 1, + want: &ofPacketOutBuilder{ + pktOut: &ofctrl.PacketOut{ + TCPHeader: &protocol.TCP{ + WinSize: 1, + }, + }, + }, + }, + { + name: "Existing TCP header", + pktOutBuilder: &ofPacketOutBuilder{pktOut: &ofctrl.PacketOut{ + TCPHeader: &protocol.TCP{}, + }}, + tcpWinSize: 1, + want: &ofPacketOutBuilder{ + pktOut: &ofctrl.PacketOut{ + TCPHeader: &protocol.TCP{ + WinSize: 1, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.pktOutBuilder.SetTCPWinSize(tt.tcpWinSize); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SetTCPWinSize() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_ofPacketOutBuilder_SetTCPData(t *testing.T) { + tests := []struct { + name string + pktOutBuilder *ofPacketOutBuilder + tcpData []byte + want PacketOutBuilder + }{ + { + name: "New TCP header", + pktOutBuilder: &ofPacketOutBuilder{pktOut: new(ofctrl.PacketOut)}, + tcpData: []byte{1}, + want: &ofPacketOutBuilder{ + pktOut: &ofctrl.PacketOut{ + TCPHeader: &protocol.TCP{ + Data: []byte{1}, + }, + }, + }, + }, + { + name: "Existing TCP header", + pktOutBuilder: &ofPacketOutBuilder{pktOut: &ofctrl.PacketOut{ + TCPHeader: &protocol.TCP{}, + }}, + tcpData: []byte{1}, + want: &ofPacketOutBuilder{ + pktOut: &ofctrl.PacketOut{ + TCPHeader: &protocol.TCP{ + Data: []byte{1}, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.pktOutBuilder.SetTCPData(tt.tcpData); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SetTCPData() = %v, want %v", got, tt.want) + } + }) + } +} + func Test_ofPacketOutBuilder_Done(t *testing.T) { type fields struct { pktOut *ofctrl.PacketOut diff --git a/pkg/ovs/openflow/testing/mock_openflow.go b/pkg/ovs/openflow/testing/mock_openflow.go index ce28d5953de..b0e78e64c2f 100644 --- a/pkg/ovs/openflow/testing/mock_openflow.go +++ b/pkg/ovs/openflow/testing/mock_openflow.go @@ -21,6 +21,7 @@ package testing import ( openflow "antrea.io/antrea/pkg/ovs/openflow" + protocol "antrea.io/libOpenflow/protocol" util "antrea.io/libOpenflow/util" ofctrl "antrea.io/ofnet/ofctrl" gomock "github.com/golang/mock/gomock" @@ -2058,6 +2059,20 @@ func (mr *MockFlowBuilderMockRecorder) MatchSrcPort(arg0, arg1 interface{}) *gom return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MatchSrcPort", reflect.TypeOf((*MockFlowBuilder)(nil).MatchSrcPort), arg0, arg1) } +// MatchTCPFlags mocks base method +func (m *MockFlowBuilder) MatchTCPFlags(arg0, arg1 uint16) openflow.FlowBuilder { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MatchTCPFlags", arg0, arg1) + ret0, _ := ret[0].(openflow.FlowBuilder) + return ret0 +} + +// MatchTCPFlags indicates an expected call of MatchTCPFlags +func (mr *MockFlowBuilderMockRecorder) MatchTCPFlags(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MatchTCPFlags", reflect.TypeOf((*MockFlowBuilder)(nil).MatchTCPFlags), arg0, arg1) +} + // MatchTunMetadata mocks base method func (m *MockFlowBuilder) MatchTunMetadata(arg0 int, arg1 uint32) openflow.FlowBuilder { m.ctrl.T.Helper() @@ -2546,6 +2561,20 @@ func (mr *MockPacketOutBuilderMockRecorder) SetDstMAC(arg0 interface{}) *gomock. return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDstMAC", reflect.TypeOf((*MockPacketOutBuilder)(nil).SetDstMAC), arg0) } +// SetEthPacket mocks base method +func (m *MockPacketOutBuilder) SetEthPacket(arg0 *protocol.Ethernet) openflow.PacketOutBuilder { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetEthPacket", arg0) + ret0, _ := ret[0].(openflow.PacketOutBuilder) + return ret0 +} + +// SetEthPacket indicates an expected call of SetEthPacket +func (mr *MockPacketOutBuilderMockRecorder) SetEthPacket(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetEthPacket", reflect.TypeOf((*MockPacketOutBuilder)(nil).SetEthPacket), arg0) +} + // SetICMPCode mocks base method func (m *MockPacketOutBuilder) SetICMPCode(arg0 byte) openflow.PacketOutBuilder { m.ctrl.T.Helper() @@ -2756,6 +2785,20 @@ func (mr *MockPacketOutBuilderMockRecorder) SetTCPAckNum(arg0 interface{}) *gomo return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTCPAckNum", reflect.TypeOf((*MockPacketOutBuilder)(nil).SetTCPAckNum), arg0) } +// SetTCPData mocks base method +func (m *MockPacketOutBuilder) SetTCPData(arg0 []byte) openflow.PacketOutBuilder { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetTCPData", arg0) + ret0, _ := ret[0].(openflow.PacketOutBuilder) + return ret0 +} + +// SetTCPData indicates an expected call of SetTCPData +func (mr *MockPacketOutBuilderMockRecorder) SetTCPData(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTCPData", reflect.TypeOf((*MockPacketOutBuilder)(nil).SetTCPData), arg0) +} + // SetTCPDstPort mocks base method func (m *MockPacketOutBuilder) SetTCPDstPort(arg0 uint16) openflow.PacketOutBuilder { m.ctrl.T.Helper() @@ -2784,6 +2827,20 @@ func (mr *MockPacketOutBuilderMockRecorder) SetTCPFlags(arg0 interface{}) *gomoc return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTCPFlags", reflect.TypeOf((*MockPacketOutBuilder)(nil).SetTCPFlags), arg0) } +// SetTCPHdrLen mocks base method +func (m *MockPacketOutBuilder) SetTCPHdrLen(arg0 byte) openflow.PacketOutBuilder { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetTCPHdrLen", arg0) + ret0, _ := ret[0].(openflow.PacketOutBuilder) + return ret0 +} + +// SetTCPHdrLen indicates an expected call of SetTCPHdrLen +func (mr *MockPacketOutBuilderMockRecorder) SetTCPHdrLen(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTCPHdrLen", reflect.TypeOf((*MockPacketOutBuilder)(nil).SetTCPHdrLen), arg0) +} + // SetTCPSeqNum mocks base method func (m *MockPacketOutBuilder) SetTCPSeqNum(arg0 uint32) openflow.PacketOutBuilder { m.ctrl.T.Helper() @@ -2812,6 +2869,20 @@ func (mr *MockPacketOutBuilderMockRecorder) SetTCPSrcPort(arg0 interface{}) *gom return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTCPSrcPort", reflect.TypeOf((*MockPacketOutBuilder)(nil).SetTCPSrcPort), arg0) } +// SetTCPWinSize mocks base method +func (m *MockPacketOutBuilder) SetTCPWinSize(arg0 uint16) openflow.PacketOutBuilder { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetTCPWinSize", arg0) + ret0, _ := ret[0].(openflow.PacketOutBuilder) + return ret0 +} + +// SetTCPWinSize indicates an expected call of SetTCPWinSize +func (mr *MockPacketOutBuilderMockRecorder) SetTCPWinSize(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTCPWinSize", reflect.TypeOf((*MockPacketOutBuilder)(nil).SetTCPWinSize), arg0) +} + // SetTTL mocks base method func (m *MockPacketOutBuilder) SetTTL(arg0 byte) openflow.PacketOutBuilder { m.ctrl.T.Helper() diff --git a/test/e2e/antreapolicy_test.go b/test/e2e/antreapolicy_test.go index 9df53764c78..ecad4bd4b3a 100644 --- a/test/e2e/antreapolicy_test.go +++ b/test/e2e/antreapolicy_test.go @@ -1554,7 +1554,7 @@ func testANPGroupServiceRefDelete(t *testing.T) { k8sUtils.Validate(allPods, reachability, []int32{80}, ProtocolTCP) _, wrong, _ := reachability.Summary() if wrong != 0 { - t.Errorf("failure -- %d wrong results", wrong) + t.Errorf("Failure -- %d wrong results", wrong) reachability.PrintSummary(true, true, true) } // Delete services, pods should be connected. @@ -1565,7 +1565,7 @@ func testANPGroupServiceRefDelete(t *testing.T) { k8sUtils.Validate(allPods, reachability2, []int32{80}, ProtocolTCP) _, wrong, _ = reachability2.Summary() if wrong != 0 { - t.Errorf("failure -- %d wrong results", wrong) + t.Errorf("Failure -- %d wrong results", wrong) reachability2.PrintSummary(true, true, true) } // Cleanup test resources. @@ -2262,10 +2262,10 @@ func testRejectServiceTraffic(t *testing.T, data *TestData, clientNamespace, ser log.Tracef("Probing: %s -> %s:%d", tc.clientPod.PodName(), tc.destAddr, tc.destPort) connectivity, err := k8sUtils.ProbeAddr(tc.clientPod.Namespace(), "antrea-e2e", tc.clientPod.PodName(), tc.destAddr, tc.destPort, ProtocolTCP) if err != nil { - t.Errorf("failure -- could not complete probe: %v", err) + t.Errorf("Failure -- could not complete probe: %v", err) } if connectivity != tc.expectedConnectivity { - t.Errorf("failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", + t.Errorf("Failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", tc.clientPod.Namespace(), tc.clientPod.PodName(), tc.destAddr, tc.destPort, connectivity, tc.expectedConnectivity) } } @@ -2287,10 +2287,10 @@ func testRejectServiceTraffic(t *testing.T, data *TestData, clientNamespace, ser log.Tracef("Probing: %s -> %s:%d", tc.clientPod.PodName(), tc.destAddr, tc.destPort) connectivity, err := k8sUtils.ProbeAddr(tc.clientPod.Namespace(), "antrea-e2e", tc.clientPod.PodName(), tc.destAddr, tc.destPort, ProtocolTCP) if err != nil { - t.Errorf("failure -- could not complete probe: %v", err) + t.Errorf("Failure -- could not complete probe: %v", err) } if connectivity != tc.expectedConnectivity { - t.Errorf("failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", + t.Errorf("Failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", tc.clientPod.Namespace(), tc.clientPod.PodName(), tc.destAddr, tc.destPort, connectivity, tc.expectedConnectivity) } } @@ -2353,10 +2353,10 @@ func testRejectNoInfiniteLoop(t *testing.T, data *TestData, clientNamespace, ser log.Tracef("Probing: %s -> %s:%d", tc.clientPod.PodName(), tc.destAddr, tc.destPort) connectivity, err := k8sUtils.ProbeAddr(tc.clientPod.Namespace(), "antrea-e2e", tc.clientPod.PodName(), tc.destAddr, tc.destPort, ProtocolTCP) if err != nil { - t.Errorf("failure -- could not complete probe: %v", err) + t.Errorf("Failure -- could not complete probe: %v", err) } if connectivity != tc.expectedConnectivity { - t.Errorf("failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", + t.Errorf("Failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", tc.clientPod.Namespace(), tc.clientPod.PodName(), tc.destAddr, tc.destPort, connectivity, tc.expectedConnectivity) } } @@ -2515,7 +2515,7 @@ func testANPMultipleAppliedTo(t *testing.T, data *TestData, singleRule bool) { k8sUtils.Validate(allPods, reachability, []int32{80}, ProtocolTCP) _, wrong, _ := reachability.Summary() if wrong != 0 { - t.Errorf("failure -- %d wrong results", wrong) + t.Errorf("Failure -- %d wrong results", wrong) reachability.PrintSummary(true, true, true) } @@ -2534,7 +2534,7 @@ func testANPMultipleAppliedTo(t *testing.T, data *TestData, singleRule bool) { k8sUtils.Validate(allPods, reachability, []int32{80}, ProtocolTCP) _, wrong, _ = reachability.Summary() if wrong != 0 { - t.Errorf("failure -- %d wrong results", wrong) + t.Errorf("Failure -- %d wrong results", wrong) reachability.PrintSummary(true, true, true) } @@ -2548,7 +2548,7 @@ func testANPMultipleAppliedTo(t *testing.T, data *TestData, singleRule bool) { k8sUtils.Validate(allPods, reachability, []int32{80}, ProtocolTCP) _, wrong, _ = reachability.Summary() if wrong != 0 { - t.Errorf("failure -- %d wrong results", wrong) + t.Errorf("Failure -- %d wrong results", wrong) reachability.PrintSummary(true, true, true) } @@ -3215,10 +3215,10 @@ func testFQDNPolicy(t *testing.T) { log.Tracef("Probing: %s -> %s", tc.clientPod.PodName(), tc.destAddr) connectivity, err := k8sUtils.ProbeAddr(tc.clientPod.Namespace(), "pod", tc.clientPod.PodName(), tc.destAddr, tc.destPort, ProtocolTCP) if err != nil { - t.Errorf("failure -- could not complete probe: %v", err) + t.Errorf("Failure -- could not complete probe: %v", err) } if connectivity != tc.expectedConnectivity { - t.Errorf("failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", + t.Errorf("Failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", tc.clientPod.Namespace(), tc.clientPod.PodName(), tc.destAddr, tc.destPort, connectivity, tc.expectedConnectivity) } } @@ -3306,10 +3306,10 @@ func testFQDNPolicyInClusterService(t *testing.T) { log.Tracef("Probing: %s -> %s", tc.clientPod.PodName(), tc.destAddr) connectivity, err := k8sUtils.ProbeAddr(tc.clientPod.Namespace(), "pod", tc.clientPod.PodName(), tc.destAddr, tc.destPort, ProtocolTCP) if err != nil { - t.Errorf("failure -- could not complete probe: %v", err) + t.Errorf("Failure -- could not complete probe: %v", err) } if connectivity != tc.expectedConnectivity { - t.Errorf("failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", + t.Errorf("Failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", tc.clientPod.Namespace(), tc.clientPod.PodName(), tc.destAddr, tc.destPort, connectivity, tc.expectedConnectivity) } } @@ -3320,6 +3320,51 @@ func testFQDNPolicyInClusterService(t *testing.T) { failOnError(k8sUtils.DeleteACNP(builder.Name), t) } +// testFQDNPolicyTCP +func testFQDNPolicyTCP(t *testing.T) { + // The ipv6-only test env doesn't have IPv6 access to the web. + skipIfNotIPv4Cluster(t) + // It is convenient to have higher log verbosity for FQDNtests for troubleshooting failures. + logLevel := log.GetLevel() + log.SetLevel(log.TraceLevel) + defer log.SetLevel(logLevel) + builder := &ClusterNetworkPolicySpecBuilder{} + builder = builder.SetName("test-acnp-fqdn-tcp"). + SetTier("application"). + SetPriority(1.0). + SetAppliedToGroup([]ACNPAppliedToSpec{{NSSelector: map[string]string{}}}) + builder.AddFQDNRule("github.com", ProtocolTCP, nil, nil, nil, "", nil, crdv1alpha1.RuleActionDrop) + testcases := []podToAddrTestStep{ + { + Pod(namespaces["y"] + "/a"), + "github.com", + 80, + Dropped, + }, + } + acnp, err := k8sUtils.CreateOrUpdateACNP(builder.Get()) + failOnError(err, t) + failOnError(waitForResourceReady(t, timeout, acnp), t) + for _, tc := range testcases { + destIP, err := k8sUtils.digDNS(tc.clientPod.PodName(), tc.clientPod.Namespace(), tc.destAddr, true) + if err != nil { + t.Errorf("Failure -- could not complete dig: %v", err) + continue + } + log.Tracef("Probing: %s -> %s(%s)", tc.clientPod.PodName(), tc.destAddr, destIP) + connectivity, err := k8sUtils.ProbeAddr(tc.clientPod.Namespace(), "pod", tc.clientPod.PodName(), destIP, tc.destPort, ProtocolTCP) + if err != nil { + t.Errorf("Failure -- could not complete probe: %v", err) + } + if connectivity != tc.expectedConnectivity { + t.Errorf("Failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", + tc.clientPod.Namespace(), tc.clientPod.PodName(), tc.destAddr, tc.destPort, connectivity, tc.expectedConnectivity) + } + } + // cleanup test resources + failOnError(k8sUtils.DeleteACNP(builder.Name), t) +} + func testToServices(t *testing.T) { skipIfProxyDisabled(t) var services []*v1.Service @@ -3380,10 +3425,10 @@ func testToServices(t *testing.T) { log.Tracef("Probing: %s -> %s", tc.clientPod.PodName(), tc.destAddr) connectivity, err := k8sUtils.ProbeAddr(tc.clientPod.Namespace(), "pod", tc.clientPod.PodName(), tc.destAddr, tc.destPort, ProtocolTCP) if err != nil { - t.Errorf("failure -- could not complete probe: %v", err) + t.Errorf("Failure -- could not complete probe: %v", err) } if connectivity != tc.expectedConnectivity { - t.Errorf("failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", + t.Errorf("Failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", tc.clientPod.Namespace(), tc.clientPod.PodName(), tc.destAddr, tc.destPort, connectivity, tc.expectedConnectivity) } } @@ -3467,10 +3512,10 @@ func testServiceAccountSelector(t *testing.T, data *TestData) { log.Tracef("Probing: %s -> %s:%d", tc.clientPod.PodName(), tc.destAddr, tc.destPort) connectivity, err := k8sUtils.ProbeAddr(tc.clientPod.Namespace(), "antrea-e2e", tc.clientPod.PodName(), tc.destAddr, tc.destPort, ProtocolTCP) if err != nil { - t.Errorf("failure -- could not complete probe: %v", err) + t.Errorf("Failure -- could not complete probe: %v", err) } if connectivity != tc.expectedConnectivity { - t.Errorf("failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", + t.Errorf("Failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", tc.clientPod.Namespace(), tc.clientPod.PodName(), tc.destAddr, tc.destPort, connectivity, tc.expectedConnectivity) } } @@ -3529,10 +3574,10 @@ func testACNPNodeSelectorEgress(t *testing.T) { log.Tracef("Probing: %s -> %s", tc.clientPod.PodName(), tc.destAddr) connectivity, err := k8sUtils.ProbeAddr(tc.clientPod.Namespace(), "pod", tc.clientPod.PodName(), tc.destAddr, tc.destPort, ProtocolTCP) if err != nil { - t.Errorf("failure -- could not complete probe: %v", err) + t.Errorf("Failure -- could not complete probe: %v", err) } if connectivity != tc.expectedConnectivity { - t.Errorf("failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", + t.Errorf("Failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", tc.clientPod.Namespace(), tc.clientPod.PodName(), tc.destAddr, tc.destPort, connectivity, tc.expectedConnectivity) } } @@ -3604,10 +3649,10 @@ func testACNPNodeSelectorIngress(t *testing.T, data *TestData) { log.Tracef("Probing: %s -> %s", tc.clientPod.PodName(), tc.destAddr) connectivity, err := k8sUtils.ProbeAddr(tc.clientPod.Namespace(), "antrea-e2e", tc.clientPod.PodName(), tc.destAddr, tc.destPort, ProtocolTCP) if err != nil { - t.Errorf("failure -- could not complete probe: %v", err) + t.Errorf("Failure -- could not complete probe: %v", err) } if connectivity != tc.expectedConnectivity { - t.Errorf("failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", + t.Errorf("Failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", tc.clientPod.Namespace(), tc.clientPod.PodName(), tc.destAddr, tc.destPort, connectivity, tc.expectedConnectivity) } } @@ -3676,10 +3721,10 @@ func testACNPICMPSupport(t *testing.T, data *TestData) { log.Tracef("Probing: %s -> %s", tc.clientPod.PodName(), tc.destAddr) connectivity, err := k8sUtils.ProbeAddr(tc.clientPod.Namespace(), "antrea-e2e", tc.clientPod.PodName(), tc.destAddr, tc.destPort, ProtocolICMP) if err != nil { - t.Errorf("failure -- could not complete probe: %v", err) + t.Errorf("Failure -- could not complete probe: %v", err) } if connectivity != tc.expectedConnectivity { - t.Errorf("failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", + t.Errorf("Failure -- wrong results for probe: Source %s/%s --> Dest %s:%d connectivity: %v, expected: %v", tc.clientPod.Namespace(), tc.clientPod.PodName(), tc.destAddr, tc.destPort, connectivity, tc.expectedConnectivity) } } @@ -3766,7 +3811,7 @@ sleep 3600 connectivity = DecideProbeResult(stderr, 3) } if connectivity != Rejected { - t.Errorf("failure -- wrong results for probe: Source 1.1.1.1 --> Dest %s:%d connectivity: %v, expected: Rej", nodeIP(idx), nodePort, connectivity) + t.Errorf("Failure -- wrong results for probe: Source 1.1.1.1 --> Dest %s:%d connectivity: %v, expected: Rej", nodeIP(idx), nodePort, connectivity) } } failOnError(k8sUtils.DeleteACNP(builder.Name), t) @@ -3989,7 +4034,7 @@ func executeTestsWithData(t *testing.T, testList []*TestCase, data *TestData) { _, wrong, _ := step.Reachability.Summary() if wrong != 0 { - t.Errorf("failure -- %d wrong results", wrong) + t.Errorf("Failure -- %d wrong results", wrong) reachability.PrintSummary(true, true, true) } } @@ -4016,10 +4061,10 @@ func doProbe(t *testing.T, data *TestData, p *CustomProbe, protocol AntreaPolicy log.Tracef("Probing: %s -> %s", p.SourcePod.Pod.PodName(), p.DestPod.Pod.PodName()) connectivity, err := k8sUtils.Probe(p.SourcePod.Pod.Namespace(), p.SourcePod.Pod.PodName(), p.DestPod.Pod.Namespace(), p.DestPod.Pod.PodName(), p.Port, protocol, nil) if err != nil { - t.Errorf("failure -- could not complete probe: %v", err) + t.Errorf("Failure -- could not complete probe: %v", err) } if connectivity != p.ExpectConnectivity { - t.Errorf("failure -- wrong results for custom probe: Source %s/%s --> Dest %s/%s connectivity: %v, expected: %v", + t.Errorf("Failure -- wrong results for custom probe: Source %s/%s --> Dest %s/%s connectivity: %v, expected: %v", p.SourcePod.Pod.Namespace(), p.SourcePod.Pod.PodName(), p.DestPod.Pod.Namespace(), p.DestPod.Pod.PodName(), connectivity, p.ExpectConnectivity) } } @@ -4286,7 +4331,8 @@ func TestAntreaPolicy(t *testing.T) { t.Run("Case=ANPGroupRefRuleIPBlocks", func(t *testing.T) { testANPGroupRefRuleIPBlocks(t) }) t.Run("Case=ANPNestedGroup", func(t *testing.T) { testANPNestedGroupCreateAndUpdate(t, data) }) t.Run("Case=ACNPFQDNPolicy", func(t *testing.T) { testFQDNPolicy(t) }) - t.Run("Case=FQDNPolicyInCluster", func(t *testing.T) { testFQDNPolicyInClusterService(t) }) + t.Run("Case=ACNPFQDNPolicyInCluster", func(t *testing.T) { testFQDNPolicyInClusterService(t) }) + t.Run("Case=ACNPFQDNPolicyTCP", func(t *testing.T) { testFQDNPolicyTCP(t) }) t.Run("Case=ACNPToServices", func(t *testing.T) { testToServices(t) }) t.Run("Case=ACNPServiceAccountSelector", func(t *testing.T) { testServiceAccountSelector(t, data) }) t.Run("Case=ACNPNodeSelectorEgress", func(t *testing.T) { testACNPNodeSelectorEgress(t) }) diff --git a/test/e2e/k8s_util.go b/test/e2e/k8s_util.go index 2a616a45761..683a7f23a6f 100644 --- a/test/e2e/k8s_util.go +++ b/test/e2e/k8s_util.go @@ -274,6 +274,65 @@ func decidePingProbeResult(stdout string, probeNum int) PodConnectivityMark { return Error } +func (k *KubernetesUtils) digDNS( + podName string, + podNamespace string, + dstAddr string, + useTCP bool, +) (string, error) { + pod, err := k.GetPodByLabel(podNamespace, podName) + if err != nil { + return "", fmt.Errorf("Pod %s/%s dones't exist", podNamespace, podName) + } + digCmd := fmt.Sprintf("dig %s", dstAddr) + if useTCP { + digCmd += " +tcp" + } + cmd := []string{ + "/bin/sh", + "-c", + digCmd, + } + log.Tracef("Running: kubectl exec %s -c %s -n %s -- %s", pod.Name, pod.Spec.Containers[0].Name, pod.Namespace, strings.Join(cmd, " ")) + stdout, stderr, err := k.RunCommandFromPod(pod.Namespace, pod.Name, pod.Spec.Containers[0].Name, cmd) + log.Tracef("%s -> %s: error when running command: err - %v /// stdout - %s /// stderr - %s", podName, dstAddr, err, stdout, stderr) + //========DiG command stdout example======== + //; <<>> DiG 9.16.6 <<>> github.com +tcp + //;; global options: +cmd + //;; Got answer: + //;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21816 + //;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 + // + //;; OPT PSEUDOSECTION: + //; EDNS: version: 0, flags:; udp: 4096 + //; COOKIE: 2d7fe493ea37c430 (echoed) + //;; QUESTION SECTION: + //;github.com. IN A + // + //;; ANSWER SECTION: + //github.com. 6 IN A 140.82.113.3 + // + //;; Query time: 0 msec + //;; SERVER: 10.96.0.10#53(10.96.0.10) + //;; WHEN: Tue Feb 14 22:34:23 UTC 2023 + //;; MSG SIZE rcvd: 77 + //========================================== + answerMarkIdx := strings.Index(stdout, ";; ANSWER SECTION:") + if answerMarkIdx == -1 { + return "", fmt.Errorf("failed to parse dig response") + } + splitResp := strings.Split(stdout[answerMarkIdx:], "\n") + if len(splitResp) < 2 { + return "", fmt.Errorf("failed to parse dig response") + } + ipLine := splitResp[1] + lastTab := strings.LastIndex(ipLine, "\t") + if lastTab == -1 { + return "", fmt.Errorf("failed to parse dig response") + } + return ipLine[lastTab:], nil +} + // Probe execs into a Pod and checks its connectivity to another Pod. It assumes // that the target Pod is serving on the input port, and also that agnhost is // installed. The connectivity from source Pod to all IPs of the target Pod