Skip to content

Commit

Permalink
Add FQDN TCP DNS support
Browse files Browse the repository at this point in the history
Signed-off-by: graysonwu <wgrayson@vmware.com>
  • Loading branch information
GraysonWu committed Mar 1, 2023
1 parent dfbfa41 commit dbf63b3
Show file tree
Hide file tree
Showing 18 changed files with 392 additions and 39 deletions.
7 changes: 7 additions & 0 deletions build/images/ovs/apply-patches.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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_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.
Expand Down
89 changes: 75 additions & 14 deletions pkg/agent/controller/networkpolicy/fqdn.go
Original file line number Diff line number Diff line change
Expand Up @@ -746,8 +746,7 @@ 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
Expand All @@ -762,14 +761,34 @@ func (f *fqdnController) handlePacketIn(pktIn *ofctrl.PacketIn) error {
}
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 {
return
}
dnsData, err := binding.GetTCPDataWithoutOptions(tcpPkt)
if err != 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 {
return
}
handleDNSData(tcpPkt.Data)
}
}
}()
Expand Down Expand Up @@ -803,18 +822,24 @@ func (f *fqdnController) sendDNSPacketout(pktIn *ofctrl.PacketIn) error {
dstIP = ipPkt.NWDst.String()
prot = ipPkt.Protocol
isIPv6 = false
switch dnsPkt := ipPkt.Data.(type) {
case *protocol.UDP:
packetData = dnsPkt.Data
switch prot {
case protocol.Type_UDP:
packetData = ipPkt.Data.(*protocol.UDP).Data
case protocol.Type_TCP:
tcpPkt, _ := binding.GetTCPPacketFromIPMessage(ipPkt)
packetData = tcpPkt.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
switch prot {
case protocol.Type_UDP:
packetData = ipPkt.Data.(*protocol.UDP).Data
case protocol.Type_TCP:
tcpPkt, _ := binding.GetTCPPacketFromIPMessage(ipPkt)
packetData = tcpPkt.Data
}
}
if prot == protocol.Type_UDP {
Expand Down Expand Up @@ -848,6 +873,42 @@ func (f *fqdnController) sendDNSPacketout(pktIn *ofctrl.PacketIn) error {
udpDstPort,
packetData,
mutatePacketOut)
} else if prot == protocol.Type_TCP {
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 purpose.
matches := pktIn.GetMatches()
inPortField := matches.GetMatchByName("OXM_OF_IN_PORT")
if inPortField != nil {
inPort = inPortField.GetValue().(uint32)
}
}
tcpSrcPort, tcpDstPort, tcpSeqNum, tcpAckNum, tcpHdrLen, tcpFlag, tcpWinSize, err := binding.GetTCPHeaderData(ethernetPkt.Data)
if err != nil {
klog.ErrorS(err, "Failed to get TCP header data")
return err
}
mutatePacketOut := func(packetOutBuilder binding.PacketOutBuilder) binding.PacketOutBuilder {
return packetOutBuilder.AddLoadRegMark(openflow.CustomReasonDNSRegMark)
}
return f.ofClient.SendTCPPacketOut(
ethernetPkt.HWSrc.String(),
ethernetPkt.HWDst.String(),
srcIP,
dstIP,
inPort,
0,
isIPv6,
tcpSrcPort,
tcpDstPort,
tcpSeqNum,
tcpAckNum,
tcpHdrLen,
tcpFlag,
tcpWinSize,
packetData,
mutatePacketOut)
}
return nil
}
6 changes: 5 additions & 1 deletion pkg/agent/controller/networkpolicy/reject.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,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
}
Expand All @@ -186,8 +186,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.
Expand Down
21 changes: 17 additions & 4 deletions pkg/agent/openflow/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,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(
Expand Down Expand Up @@ -1122,8 +1126,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)
Expand All @@ -1137,10 +1145,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)
Expand Down
20 changes: 20 additions & 0 deletions pkg/agent/openflow/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1561,8 +1561,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
Expand All @@ -1572,17 +1576,25 @@ 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",
protocol: binding.ProtocolTCPv6,
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",
Expand Down Expand Up @@ -1681,8 +1693,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(),
Expand All @@ -1692,8 +1708,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)
Expand Down
39 changes: 36 additions & 3 deletions pkg/agent/openflow/network_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ 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")
MatchTCPFlag = types.NewMatchKey(binding.ProtocolTCP, types.TCPFlagAddr, "tcp_flags")
Unsupported = types.NewMatchKey(binding.ProtocolIP, types.UnSupported, "unknown")

// metricFlowIdentifier is used to identify metric flows in metric table.
Expand All @@ -79,9 +80,15 @@ var (
metricFlowIdentifier = fmt.Sprintf("priority=%d,", priorityNormal)

protocolUDP = v1beta2.ProtocolUDP
protocolTCP = v1beta2.ProtocolTCP
dnsPort = intstr.FromInt(53)
)

type TCPFlag struct {
Flag uint16
Mask uint16
}

// IP address calculated from Pod's address.
type IPAddress net.IP

Expand Down Expand Up @@ -699,17 +706,43 @@ func (c *client) NewDNSpacketInConjunction(id uint32) error {
if err := c.ofEntryOperations.AddAll(conj.actionFlows); err != nil {
return fmt.Errorf("error when adding action flows for the DNS conjunction: %w", err)
}
dnsPriority := priorityDNSIntercept
conj.serviceClause = conj.newClause(1, 2, getTableByID(conj.ruleTableID), nil)
conj.toClause = conj.newClause(2, 2, getTableByID(conj.ruleTableID), nil)
udpService := v1beta2.Service{
Protocol: &protocolUDP,
Port: &dnsPort,
}
dnsPriority := priorityDNSIntercept
conj.serviceClause = conj.newClause(1, 2, getTableByID(conj.ruleTableID), nil)
conj.toClause = conj.newClause(2, 2, getTableByID(conj.ruleTableID), nil)
tcpService := v1beta2.Service{
Protocol: &protocolTCP,
Port: &dnsPort,
}
tcpServiceMatch := &conjunctiveMatch{
tableID: conj.serviceClause.ruleTable.GetID(),
matchPairs: []matchPair{
getServiceMatchPairs(tcpService, c.featureNetworkPolicy.ipProtocols, true)[0][0],
{
matchKey: MatchTCPFlag,
matchValue: TCPFlag{
// URG|ACK|PSH|RST|SYN|FIN|
Flag: 0b011000,
Mask: 0b011000,
},
},
},
priority: &dnsPriority,
}

c.featureNetworkPolicy.conjMatchFlowLock.Lock()
defer c.featureNetworkPolicy.conjMatchFlowLock.Unlock()
ctxChanges := conj.serviceClause.addServiceFlows(c.featureNetworkPolicy, []v1beta2.Service{udpService}, &dnsPriority, true, false)
ctxChange := conj.serviceClause.addConjunctiveMatchFlow(c.featureNetworkPolicy, tcpServiceMatch, false, false)
ctxChanges = append(ctxChanges, ctxChange)
for _, change := range ctxChanges {
for _, pa := range change.context.matchPairs {
klog.Infof("%s:%s", pa.matchKey.GetKeyString(), pa.matchValue)
}
}
if err := c.featureNetworkPolicy.applyConjunctiveMatchFlows(ctxChanges); err != nil {
return err
}
Expand Down
1 change: 0 additions & 1 deletion pkg/agent/openflow/network_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions pkg/agent/openflow/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 MatchTCPFlag:
fb = fb.MatchProtocol(matchKey.GetOFProtocol())
if matchValue != nil {
tcpFlag := matchValue.(TCPFlag)
fb = fb.MatchTCPFlag(tcpFlag.Flag, tcpFlag.Mask)
}
}
return fb
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/agent/openflow/testing/mock_openflow.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pkg/agent/types/networkpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const (
ServiceGroupIDAddr
IGMPAddr
LabelIDAddr
TCPFlagAddr
UnSupported
)

Expand Down
Loading

0 comments on commit dbf63b3

Please sign in to comment.