Skip to content

Commit

Permalink
Uniform DNS Interception
Browse files Browse the repository at this point in the history
1. Change to use ct_state to match the DNS responses that need
further interception.
2. Add OF meter for DNS interception.

Signed-off-by: graysonwu <[email protected]>
  • Loading branch information
GraysonWu committed Aug 16, 2023
1 parent 5d848ba commit 48051c3
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 60 deletions.
67 changes: 42 additions & 25 deletions pkg/agent/openflow/network_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,17 @@ var (
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")
// Match value of MatchCTStatePositive will be used as positive CT state match(+).
// The value should follow the format "state1,state2".
// For example, if the match value is "rpl,trk", it will be translated to ct_state=+rpl+trk.
MatchCTStatePositive = types.NewMatchKey(binding.ProtocolIP, types.CTStateAddr, "ct_state")
Unsupported = types.NewMatchKey(binding.ProtocolIP, types.UnSupported, "unknown")

// metricFlowIdentifier is used to identify metric flows in metric table.
// There could be other flows like default flow and Traceflow flows in the table. Only metric flows are supposed to
// have normal priority.
metricFlowIdentifier = fmt.Sprintf("priority=%d,", priorityNormal)

protocolUDP = v1beta2.ProtocolUDP
protocolTCP = v1beta2.ProtocolTCP
dnsPort = int32(53)
)
Expand Down Expand Up @@ -706,53 +709,67 @@ 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)
}
udpService := v1beta2.Service{
Protocol: &protocolUDP,
SrcPort: &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, false)

tcpFlags := TCPFlags{
// URG|ACK|PSH|RST|SYN|FIN|
Flag: 0b011000,
Mask: 0b011000,
}
tcpDNSPort := types.BitRange{Value: uint16(dnsPort)}
var ctxChanges []*conjMatchFlowContextChange
for _, proto := range c.featureNetworkPolicy.ipProtocols {
tcpServiceMatch := &conjunctiveMatch{
tcpMatch := &conjunctiveMatch{
tableID: conj.serviceClause.ruleTable.GetID(),
priority: &dnsPriority,
}
udpMatch := &conjunctiveMatch{
tableID: conj.serviceClause.ruleTable.GetID(),
priority: &dnsPriority,
}
if proto == binding.ProtocolIP {
tcpServiceMatch.matchPairs = []matchPair{
tcpMatch.matchPairs = []matchPair{
{
matchKey: MatchTCPSrcPort,
matchValue: tcpDNSPort,
matchValue: types.BitRange{Value: uint16(dnsPort)},
},
{
matchKey: MatchTCPFlags,
matchValue: tcpFlags,
matchKey: MatchCTStatePositive,
matchValue: "rpl,trk",
},
}
udpMatch.matchPairs = []matchPair{
{
matchKey: MatchUDPSrcPort,
matchValue: types.BitRange{Value: uint16(dnsPort)},
},
{
matchKey: MatchCTStatePositive,
matchValue: "rpl,trk",
},
}
} else if proto == binding.ProtocolIPv6 {
tcpServiceMatch.matchPairs = []matchPair{
tcpMatch.matchPairs = []matchPair{
{
matchKey: MatchTCPv6SrcPort,
matchValue: tcpDNSPort,
matchValue: types.BitRange{Value: uint16(dnsPort)},
},
{
matchKey: MatchCTStatePositive,
matchValue: "rpl,trk",
},
}
udpMatch.matchPairs = []matchPair{
{
matchKey: MatchUDPv6SrcPort,
matchValue: types.BitRange{Value: uint16(dnsPort)},
},
{
matchKey: MatchTCPv6Flags,
matchValue: tcpFlags,
matchKey: MatchCTStatePositive,
matchValue: "rpl,trk",
},
}
}
ctxChange := conj.serviceClause.addConjunctiveMatchFlow(c.featureNetworkPolicy, tcpServiceMatch, false, false)
ctxChanges = append(ctxChanges, ctxChange)
tcpCtxChange := conj.serviceClause.addConjunctiveMatchFlow(c.featureNetworkPolicy, tcpMatch, false, false)
udpCtxChange := conj.serviceClause.addConjunctiveMatchFlow(c.featureNetworkPolicy, udpMatch, false, false)
ctxChanges = append(ctxChanges, tcpCtxChange, udpCtxChange)
}
if err := c.featureNetworkPolicy.applyConjunctiveMatchFlows(ctxChanges); err != nil {
return err
Expand Down
85 changes: 56 additions & 29 deletions pkg/agent/openflow/network_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1447,6 +1447,47 @@ func Test_featureNetworkPolicy_initFlows(t *testing.T) {
}

func Test_NewDNSPacketInConjunction(t *testing.T) {
ovsMetersSupported := ovsMetersAreSupported()
ipv4ExpFlows := []string{
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,conj_id=1 actions=controller(id=32776,reason=no_match,userdata=02,max_len=128),goto_table:IngressMetric",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,ct_state=+rpl+trk,udp,tp_src=53 actions=conjunction(1,1/2)",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,ct_state=+rpl+trk,tcp,tp_src=53 actions=conjunction(1,1/2)",
}
if ovsMetersSupported {
ipv4ExpFlows = []string{
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,conj_id=1 actions=meter:1,controller(id=32776,reason=no_match,userdata=02,max_len=128),goto_table:IngressMetric",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,ct_state=+rpl+trk,udp,tp_src=53 actions=conjunction(1,1/2)",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,ct_state=+rpl+trk,tcp,tp_src=53 actions=conjunction(1,1/2)",
}
}
ipv6ExpFlows := []string{
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,conj_id=1 actions=controller(id=32776,reason=no_match,userdata=02,max_len=128),goto_table:IngressMetric",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,ct_state=+rpl+trk,udp6,tp_src=53 actions=conjunction(1,1/2)",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,ct_state=+rpl+trk,tcp6,tp_src=53 actions=conjunction(1,1/2)",
}
if ovsMetersSupported {
ipv6ExpFlows = []string{
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,conj_id=1 actions=meter:1,controller(id=32776,reason=no_match,userdata=02,max_len=128),goto_table:IngressMetric",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,ct_state=+rpl+trk,udp6,tp_src=53 actions=conjunction(1,1/2)",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,ct_state=+rpl+trk,tcp6,tp_src=53 actions=conjunction(1,1/2)",
}
}
dsExpFlows := []string{
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,conj_id=1 actions=controller(id=32776,reason=no_match,userdata=02,max_len=128),goto_table:IngressMetric",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,ct_state=+rpl+trk,udp,tp_src=53 actions=conjunction(1,1/2)",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,ct_state=+rpl+trk,tcp,tp_src=53 actions=conjunction(1,1/2)",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,ct_state=+rpl+trk,udp6,tp_src=53 actions=conjunction(1,1/2)",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,ct_state=+rpl+trk,tcp6,tp_src=53 actions=conjunction(1,1/2)",
}
if ovsMetersSupported {
dsExpFlows = []string{
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,conj_id=1 actions=meter:1,controller(id=32776,reason=no_match,userdata=02,max_len=128),goto_table:IngressMetric",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,ct_state=+rpl+trk,udp,tp_src=53 actions=conjunction(1,1/2)",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,ct_state=+rpl+trk,tcp,tp_src=53 actions=conjunction(1,1/2)",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,ct_state=+rpl+trk,udp6,tp_src=53 actions=conjunction(1,1/2)",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,ct_state=+rpl+trk,tcp6,tp_src=53 actions=conjunction(1,1/2)",
}
}
for _, tc := range []struct {
name string
enableIPv4 bool
Expand All @@ -1455,39 +1496,25 @@ func Test_NewDNSPacketInConjunction(t *testing.T) {
expectedFlows []string
}{
{
name: "IPv4 only",
enableIPv4: true,
enableIPv6: false,
conjID: 1,
expectedFlows: []string{
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,conj_id=1 actions=controller(id=32776,reason=no_match,userdata=02,max_len=128),goto_table:IngressMetric",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,udp,tp_src=53 actions=conjunction(1,1/2)",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,tcp,tp_src=53,tcp_flags=+psh+ack actions=conjunction(1,1/2)",
},
name: "IPv4 only",
enableIPv4: true,
enableIPv6: false,
conjID: 1,
expectedFlows: ipv4ExpFlows,
},
{
name: "IPv6 only",
enableIPv4: false,
enableIPv6: true,
conjID: 1,
expectedFlows: []string{
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,conj_id=1 actions=controller(id=32776,reason=no_match,userdata=02,max_len=128),goto_table:IngressMetric",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,udp6,tp_src=53 actions=conjunction(1,1/2)",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,tcp6,tp_src=53,tcp_flags=+psh+ack actions=conjunction(1,1/2)",
},
name: "IPv6 only",
enableIPv4: false,
enableIPv6: true,
conjID: 1,
expectedFlows: ipv6ExpFlows,
},
{
name: "dual stack",
enableIPv4: true,
enableIPv6: true,
conjID: 1,
expectedFlows: []string{
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,conj_id=1 actions=controller(id=32776,reason=no_match,userdata=02,max_len=128),goto_table:IngressMetric",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,udp,tp_src=53 actions=conjunction(1,1/2)",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,tcp,tp_src=53,tcp_flags=+psh+ack actions=conjunction(1,1/2)",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,udp6,tp_src=53 actions=conjunction(1,1/2)",
"cookie=0x1020000000000, table=AntreaPolicyIngressRule, priority=64991,tcp6,tp_src=53,tcp_flags=+psh+ack actions=conjunction(1,1/2)",
},
name: "dual stack",
enableIPv4: true,
enableIPv6: true,
conjID: 1,
expectedFlows: dsExpFlows,
},
} {
t.Run(tc.name, func(t *testing.T) {
Expand Down
27 changes: 21 additions & 6 deletions pkg/agent/openflow/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"fmt"
"net"
"sort"
"strings"
"sync"
"time"

Expand Down Expand Up @@ -2094,6 +2095,17 @@ func (f *featureNetworkPolicy) addFlowMatch(fb binding.FlowBuilder, matchKey *ty
fb = fb.MatchProtocol(matchKey.GetOFProtocol())
tcpFlag := matchValue.(TCPFlags)
fb = fb.MatchTCPFlags(tcpFlag.Flag, tcpFlag.Mask)
case MatchCTStatePositive:
fb = fb.MatchProtocol(matchKey.GetOFProtocol())
states := matchValue.(string)
for _, state := range strings.Split(states, ",") {
switch state {
case "rpl":
fb = fb.MatchCTStateRpl(true)
case "trk":
fb = fb.MatchCTStateTrk(true)
}
}
}
return fb
}
Expand Down Expand Up @@ -2177,13 +2189,16 @@ func (f *featureNetworkPolicy) multiClusterNetworkPolicySecurityDropFlow(table b
// dnsPacketInFlow generates the flow to send dns response packets of fqdn policy selected Pods to the fqdnController for
// processing.
func (f *featureNetworkPolicy) dnsPacketInFlow(conjunctionID uint32) binding.Flow {
return AntreaPolicyIngressRuleTable.ofTable.BuildFlow(priorityDNSIntercept).
fb := AntreaPolicyIngressRuleTable.ofTable.BuildFlow(priorityDNSIntercept).
Cookie(f.cookieAllocator.Request(f.category).Raw()).
MatchConjID(conjunctionID).
// FQDN should pause DNS response packets and send them to the controller. After
// the controller processes DNS response packets, like creating related flows in
// the OVS or no operations are needed, the controller will resume those packets.
Action().SendToController([]byte{uint8(PacketInCategoryDNS)}, true).
MatchConjID(conjunctionID)
if f.ovsMetersAreSupported {
fb = fb.Action().Meter(PacketInMeterIDNP)
}
// FQDN should pause DNS response packets and send them to the controller. After
// the controller processes DNS response packets, like creating related flows in
// the OVS or no operations are needed, the controller will resume those packets.
return fb.Action().SendToController([]byte{uint8(PacketInCategoryDNS)}, true).
Action().GotoTable(IngressMetricTable.GetID()).
Done()
}
Expand Down
1 change: 1 addition & 0 deletions pkg/agent/types/networkpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const (
IGMPAddr
LabelIDAddr
TCPFlagsAddr
CTStateAddr
UnSupported
)

Expand Down

0 comments on commit 48051c3

Please sign in to comment.