mirror of
https://github.com/vishvananda/netlink
synced 2025-03-23 19:46:50 +00:00
conntrack filter by port and protocol
Add a new method to the ConntrackFilter to be able to filter conntrack entries by Layer 4 protocol and source and destination port. Signed-off-by: Antonio Ojea <aojea@redhat.com>
This commit is contained in:
parent
cf66001890
commit
bca67dfc82
@ -318,6 +318,11 @@ func parseRawData(data []byte) *ConntrackFlow {
|
|||||||
// --mask-src ip Source mask address
|
// --mask-src ip Source mask address
|
||||||
// --mask-dst ip Destination mask address
|
// --mask-dst ip Destination mask address
|
||||||
|
|
||||||
|
// Layer 4 Protocol common parameters and options:
|
||||||
|
// TCP, UDP, SCTP, UDPLite and DCCP
|
||||||
|
// --sport, --orig-port-src port Source port in original direction
|
||||||
|
// --dport, --orig-port-dst port Destination port in original direction
|
||||||
|
|
||||||
// Filter types
|
// Filter types
|
||||||
type ConntrackFilterType uint8
|
type ConntrackFilterType uint8
|
||||||
|
|
||||||
@ -327,9 +332,11 @@ const (
|
|||||||
ConntrackReplySrcIP // --reply-src ip Reply Source IP
|
ConntrackReplySrcIP // --reply-src ip Reply Source IP
|
||||||
ConntrackReplyDstIP // --reply-dst ip Reply Destination IP
|
ConntrackReplyDstIP // --reply-dst ip Reply Destination IP
|
||||||
ConntrackReplyAnyIP // Match source or destination reply IP
|
ConntrackReplyAnyIP // Match source or destination reply IP
|
||||||
|
ConntrackOrigSrcPort // --orig-port-src port Source port in original direction
|
||||||
|
ConntrackOrigDstPort // --orig-port-dst port Destination port in original direction
|
||||||
ConntrackNatSrcIP = ConntrackReplySrcIP // deprecated use instead ConntrackReplySrcIP
|
ConntrackNatSrcIP = ConntrackReplySrcIP // deprecated use instead ConntrackReplySrcIP
|
||||||
ConntrackNatDstIP = ConntrackReplyDstIP // deprecated use instead ConntrackReplyDstIP
|
ConntrackNatDstIP = ConntrackReplyDstIP // deprecated use instead ConntrackReplyDstIP
|
||||||
ConntrackNatAnyIP = ConntrackReplyAnyIP // deprecated use instaed ConntrackReplyAnyIP
|
ConntrackNatAnyIP = ConntrackReplyAnyIP // deprecated use instead ConntrackReplyAnyIP
|
||||||
)
|
)
|
||||||
|
|
||||||
type CustomConntrackFilter interface {
|
type CustomConntrackFilter interface {
|
||||||
@ -340,6 +347,8 @@ type CustomConntrackFilter interface {
|
|||||||
|
|
||||||
type ConntrackFilter struct {
|
type ConntrackFilter struct {
|
||||||
ipFilter map[ConntrackFilterType]net.IP
|
ipFilter map[ConntrackFilterType]net.IP
|
||||||
|
portFilter map[ConntrackFilterType]uint16
|
||||||
|
protoFilter uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddIP adds an IP to the conntrack filter
|
// AddIP adds an IP to the conntrack filter
|
||||||
@ -354,15 +363,52 @@ func (f *ConntrackFilter) AddIP(tp ConntrackFilterType, ip net.IP) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddPort adds a Port to the conntrack filter if the Layer 4 protocol allows it
|
||||||
|
func (f *ConntrackFilter) AddPort(tp ConntrackFilterType, port uint16) error {
|
||||||
|
switch f.protoFilter {
|
||||||
|
// TCP, UDP, DCCP, SCTP, UDPLite
|
||||||
|
case 6, 17, 33, 132, 136:
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Filter attribute not available without a valid Layer 4 protocol: %d", f.protoFilter)
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.portFilter == nil {
|
||||||
|
f.portFilter = make(map[ConntrackFilterType]uint16)
|
||||||
|
}
|
||||||
|
if _, ok := f.portFilter[tp]; ok {
|
||||||
|
return errors.New("Filter attribute already present")
|
||||||
|
}
|
||||||
|
f.portFilter[tp] = port
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddProtocol adds the Layer 4 protocol to the conntrack filter
|
||||||
|
func (f *ConntrackFilter) AddProtocol(proto uint8) error {
|
||||||
|
if f.protoFilter != 0 {
|
||||||
|
return errors.New("Filter attribute already present")
|
||||||
|
}
|
||||||
|
f.protoFilter = proto
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// MatchConntrackFlow applies the filter to the flow and returns true if the flow matches the filter
|
// MatchConntrackFlow applies the filter to the flow and returns true if the flow matches the filter
|
||||||
// false otherwise
|
// false otherwise
|
||||||
func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool {
|
func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool {
|
||||||
if len(f.ipFilter) == 0 {
|
if len(f.ipFilter) == 0 && len(f.portFilter) == 0 && f.protoFilter == 0 {
|
||||||
// empty filter always not match
|
// empty filter always not match
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -p, --protonum proto Layer 4 Protocol, eg. 'tcp'
|
||||||
|
if f.protoFilter != 0 && flow.Forward.Protocol != f.protoFilter {
|
||||||
|
// different Layer 4 protocol always not match
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
match := true
|
match := true
|
||||||
|
|
||||||
|
// IP conntrack filter
|
||||||
|
if len(f.ipFilter) > 0 {
|
||||||
// -orig-src ip Source address from original direction
|
// -orig-src ip Source address from original direction
|
||||||
if elem, found := f.ipFilter[ConntrackOrigSrcIP]; found {
|
if elem, found := f.ipFilter[ConntrackOrigSrcIP]; found {
|
||||||
match = match && elem.Equal(flow.Forward.SrcIP)
|
match = match && elem.Equal(flow.Forward.SrcIP)
|
||||||
@ -387,6 +433,20 @@ func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool {
|
|||||||
if elem, found := f.ipFilter[ConntrackReplyAnyIP]; match && found {
|
if elem, found := f.ipFilter[ConntrackReplyAnyIP]; match && found {
|
||||||
match = match && (elem.Equal(flow.Reverse.SrcIP) || elem.Equal(flow.Reverse.DstIP))
|
match = match && (elem.Equal(flow.Reverse.SrcIP) || elem.Equal(flow.Reverse.DstIP))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Layer 4 Port filter
|
||||||
|
if len(f.portFilter) > 0 {
|
||||||
|
// -orig-port-src port Source port from original direction
|
||||||
|
if elem, found := f.portFilter[ConntrackOrigSrcPort]; match && found {
|
||||||
|
match = match && elem == flow.Forward.SrcPort
|
||||||
|
}
|
||||||
|
|
||||||
|
// -orig-port-dst port Destination port from original direction
|
||||||
|
if elem, found := f.portFilter[ConntrackOrigDstPort]; match && found {
|
||||||
|
match = match && elem == flow.Forward.DstPort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return match
|
return match
|
||||||
}
|
}
|
||||||
|
@ -92,10 +92,18 @@ func TestConntrackSocket(t *testing.T) {
|
|||||||
// Creates some flows and checks that they are correctly fetched from the conntrack table
|
// Creates some flows and checks that they are correctly fetched from the conntrack table
|
||||||
func TestConntrackTableList(t *testing.T) {
|
func TestConntrackTableList(t *testing.T) {
|
||||||
skipUnlessRoot(t)
|
skipUnlessRoot(t)
|
||||||
setUpNetlinkTestWithKModule(t, "nf_conntrack")
|
k, m, err := KernelVersion()
|
||||||
setUpNetlinkTestWithKModule(t, "nf_conntrack_netlink")
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// conntrack l3proto was unified since 4.19
|
||||||
|
// https://github.com/torvalds/linux/commit/a0ae2562c6c4b2721d9fddba63b7286c13517d9f
|
||||||
|
if k < 4 || k == 4 && m < 19 {
|
||||||
setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv4")
|
setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv4")
|
||||||
setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv6")
|
setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv6")
|
||||||
|
}
|
||||||
|
setUpNetlinkTestWithKModule(t, "nf_conntrack")
|
||||||
|
setUpNetlinkTestWithKModule(t, "nf_conntrack_netlink")
|
||||||
|
|
||||||
// Creates a new namespace and bring up the loopback interface
|
// Creates a new namespace and bring up the loopback interface
|
||||||
origns, ns, h := nsCreateAndEnter(t)
|
origns, ns, h := nsCreateAndEnter(t)
|
||||||
@ -107,7 +115,7 @@ func TestConntrackTableList(t *testing.T) {
|
|||||||
setUpF(t, "/proc/sys/net/netfilter/nf_conntrack_acct", "1")
|
setUpF(t, "/proc/sys/net/netfilter/nf_conntrack_acct", "1")
|
||||||
|
|
||||||
// Flush the table to start fresh
|
// Flush the table to start fresh
|
||||||
err := h.ConntrackTableFlush(ConntrackTable)
|
err = h.ConntrackTableFlush(ConntrackTable)
|
||||||
CheckErrorFail(t, err)
|
CheckErrorFail(t, err)
|
||||||
|
|
||||||
// Create 5 udp
|
// Create 5 udp
|
||||||
@ -149,8 +157,16 @@ func TestConntrackTableFlush(t *testing.T) {
|
|||||||
skipUnlessRoot(t)
|
skipUnlessRoot(t)
|
||||||
setUpNetlinkTestWithKModule(t, "nf_conntrack")
|
setUpNetlinkTestWithKModule(t, "nf_conntrack")
|
||||||
setUpNetlinkTestWithKModule(t, "nf_conntrack_netlink")
|
setUpNetlinkTestWithKModule(t, "nf_conntrack_netlink")
|
||||||
|
k, m, err := KernelVersion()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// conntrack l3proto was unified since 4.19
|
||||||
|
// https://github.com/torvalds/linux/commit/a0ae2562c6c4b2721d9fddba63b7286c13517d9f
|
||||||
|
if k < 4 || k == 4 && m < 19 {
|
||||||
setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv4")
|
setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv4")
|
||||||
|
}
|
||||||
|
setUpNetlinkTestWithKModule(t, "nf_conntrack")
|
||||||
// Creates a new namespace and bring up the loopback interface
|
// Creates a new namespace and bring up the loopback interface
|
||||||
origns, ns, h := nsCreateAndEnter(t)
|
origns, ns, h := nsCreateAndEnter(t)
|
||||||
defer netns.Set(*origns)
|
defer netns.Set(*origns)
|
||||||
@ -211,7 +227,15 @@ func TestConntrackTableDelete(t *testing.T) {
|
|||||||
skipUnlessRoot(t)
|
skipUnlessRoot(t)
|
||||||
setUpNetlinkTestWithKModule(t, "nf_conntrack")
|
setUpNetlinkTestWithKModule(t, "nf_conntrack")
|
||||||
setUpNetlinkTestWithKModule(t, "nf_conntrack_netlink")
|
setUpNetlinkTestWithKModule(t, "nf_conntrack_netlink")
|
||||||
|
k, m, err := KernelVersion()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// conntrack l3proto was unified since 4.19
|
||||||
|
// https://github.com/torvalds/linux/commit/a0ae2562c6c4b2721d9fddba63b7286c13517d9f
|
||||||
|
if k < 4 || k == 4 && m < 19 {
|
||||||
setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv4")
|
setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv4")
|
||||||
|
}
|
||||||
|
|
||||||
// Creates a new namespace and bring up the loopback interface
|
// Creates a new namespace and bring up the loopback interface
|
||||||
origns, ns, h := nsCreateAndEnter(t)
|
origns, ns, h := nsCreateAndEnter(t)
|
||||||
@ -252,6 +276,8 @@ func TestConntrackTableDelete(t *testing.T) {
|
|||||||
// Create a filter to erase groupB flows
|
// Create a filter to erase groupB flows
|
||||||
filter := &ConntrackFilter{}
|
filter := &ConntrackFilter{}
|
||||||
filter.AddIP(ConntrackOrigDstIP, net.ParseIP("127.0.0.20"))
|
filter.AddIP(ConntrackOrigDstIP, net.ParseIP("127.0.0.20"))
|
||||||
|
filter.AddProtocol(17)
|
||||||
|
filter.AddPort(ConntrackOrigDstPort, 8000)
|
||||||
|
|
||||||
// Flush entries of groupB
|
// Flush entries of groupB
|
||||||
var deleted uint
|
var deleted uint
|
||||||
@ -300,12 +326,14 @@ func TestConntrackFilter(t *testing.T) {
|
|||||||
DstIP: net.ParseIP("20.0.0.1"),
|
DstIP: net.ParseIP("20.0.0.1"),
|
||||||
SrcPort: 1000,
|
SrcPort: 1000,
|
||||||
DstPort: 2000,
|
DstPort: 2000,
|
||||||
|
Protocol: 17,
|
||||||
},
|
},
|
||||||
Reverse: ipTuple{
|
Reverse: ipTuple{
|
||||||
SrcIP: net.ParseIP("20.0.0.1"),
|
SrcIP: net.ParseIP("20.0.0.1"),
|
||||||
DstIP: net.ParseIP("192.168.1.1"),
|
DstIP: net.ParseIP("192.168.1.1"),
|
||||||
SrcPort: 2000,
|
SrcPort: 2000,
|
||||||
DstPort: 1000,
|
DstPort: 1000,
|
||||||
|
Protocol: 17,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ConntrackFlow{
|
ConntrackFlow{
|
||||||
@ -315,12 +343,14 @@ func TestConntrackFilter(t *testing.T) {
|
|||||||
DstIP: net.ParseIP("20.0.0.2"),
|
DstIP: net.ParseIP("20.0.0.2"),
|
||||||
SrcPort: 5000,
|
SrcPort: 5000,
|
||||||
DstPort: 6000,
|
DstPort: 6000,
|
||||||
|
Protocol: 6,
|
||||||
},
|
},
|
||||||
Reverse: ipTuple{
|
Reverse: ipTuple{
|
||||||
SrcIP: net.ParseIP("20.0.0.2"),
|
SrcIP: net.ParseIP("20.0.0.2"),
|
||||||
DstIP: net.ParseIP("192.168.1.1"),
|
DstIP: net.ParseIP("192.168.1.1"),
|
||||||
SrcPort: 6000,
|
SrcPort: 6000,
|
||||||
DstPort: 5000,
|
DstPort: 5000,
|
||||||
|
Protocol: 6,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ConntrackFlow{
|
ConntrackFlow{
|
||||||
@ -330,12 +360,14 @@ func TestConntrackFilter(t *testing.T) {
|
|||||||
DstIP: net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"),
|
DstIP: net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"),
|
||||||
SrcPort: 1000,
|
SrcPort: 1000,
|
||||||
DstPort: 2000,
|
DstPort: 2000,
|
||||||
|
Protocol: 132,
|
||||||
},
|
},
|
||||||
Reverse: ipTuple{
|
Reverse: ipTuple{
|
||||||
SrcIP: net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"),
|
SrcIP: net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"),
|
||||||
DstIP: net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"),
|
DstIP: net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"),
|
||||||
SrcPort: 2000,
|
SrcPort: 2000,
|
||||||
DstPort: 1000,
|
DstPort: 1000,
|
||||||
|
Protocol: 132,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -345,11 +377,53 @@ func TestConntrackFilter(t *testing.T) {
|
|||||||
t.Fatalf("Error, empty filter cannot match, v4:%d, v6:%d", v4Match, v6Match)
|
t.Fatalf("Error, empty filter cannot match, v4:%d, v6:%d", v4Match, v6Match)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SrcIP filter
|
// Filter errors
|
||||||
|
|
||||||
|
// Adding same attribute should fail
|
||||||
|
filter := &ConntrackFilter{}
|
||||||
|
filter.AddIP(ConntrackOrigSrcIP, net.ParseIP("10.0.0.1"))
|
||||||
|
if err := filter.AddIP(ConntrackOrigSrcIP, net.ParseIP("10.0.0.1")); err == nil {
|
||||||
|
t.Fatalf("Error, it should fail adding same attribute to the filter")
|
||||||
|
}
|
||||||
|
filter.AddProtocol(6)
|
||||||
|
if err := filter.AddProtocol(17); err == nil {
|
||||||
|
t.Fatalf("Error, it should fail adding same attribute to the filter")
|
||||||
|
}
|
||||||
|
filter.AddPort(ConntrackOrigSrcPort, 80)
|
||||||
|
if err := filter.AddPort(ConntrackOrigSrcPort, 80); err == nil {
|
||||||
|
t.Fatalf("Error, it should fail adding same attribute to the filter")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can not add a Port filter without Layer 4 protocol
|
||||||
|
filter = &ConntrackFilter{}
|
||||||
|
if err := filter.AddPort(ConntrackOrigSrcPort, 80); err == nil {
|
||||||
|
t.Fatalf("Error, it should fail adding a port filter without a protocol")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can not add a Port filter if the Layer 4 protocol does not support it
|
||||||
|
filter = &ConntrackFilter{}
|
||||||
|
filter.AddProtocol(47)
|
||||||
|
if err := filter.AddPort(ConntrackOrigSrcPort, 80); err == nil {
|
||||||
|
t.Fatalf("Error, it should fail adding a port filter with a wrong protocol")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proto filter
|
||||||
filterV4 := &ConntrackFilter{}
|
filterV4 := &ConntrackFilter{}
|
||||||
filterV4.AddIP(ConntrackOrigSrcIP, net.ParseIP("10.0.0.1"))
|
filterV4.AddProtocol(6)
|
||||||
|
|
||||||
filterV6 := &ConntrackFilter{}
|
filterV6 := &ConntrackFilter{}
|
||||||
|
filterV6.AddProtocol(132)
|
||||||
|
|
||||||
|
v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
|
||||||
|
if v4Match != 1 || v6Match != 1 {
|
||||||
|
t.Fatalf("Error, there should be only 1 match for TCP:%d, UDP:%d", v4Match, v6Match)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SrcIP filter
|
||||||
|
filterV4 = &ConntrackFilter{}
|
||||||
|
filterV4.AddIP(ConntrackOrigSrcIP, net.ParseIP("10.0.0.1"))
|
||||||
|
|
||||||
|
filterV6 = &ConntrackFilter{}
|
||||||
filterV6.AddIP(ConntrackOrigSrcIP, net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"))
|
filterV6.AddIP(ConntrackOrigSrcIP, net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"))
|
||||||
|
|
||||||
v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
|
v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
|
||||||
@ -404,4 +478,32 @@ func TestConntrackFilter(t *testing.T) {
|
|||||||
if v4Match != 2 || v6Match != 1 {
|
if v4Match != 2 || v6Match != 1 {
|
||||||
t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match)
|
t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SrcPort filter
|
||||||
|
filterV4 = &ConntrackFilter{}
|
||||||
|
filterV4.AddProtocol(6)
|
||||||
|
filterV4.AddPort(ConntrackOrigSrcPort, 5000)
|
||||||
|
|
||||||
|
filterV6 = &ConntrackFilter{}
|
||||||
|
filterV6.AddProtocol(132)
|
||||||
|
filterV6.AddPort(ConntrackOrigSrcPort, 1000)
|
||||||
|
|
||||||
|
v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
|
||||||
|
if v4Match != 1 || v6Match != 1 {
|
||||||
|
t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DstPort filter
|
||||||
|
filterV4 = &ConntrackFilter{}
|
||||||
|
filterV4.AddProtocol(6)
|
||||||
|
filterV4.AddPort(ConntrackOrigDstPort, 6000)
|
||||||
|
|
||||||
|
filterV6 = &ConntrackFilter{}
|
||||||
|
filterV6.AddProtocol(132)
|
||||||
|
filterV6.AddPort(ConntrackOrigDstPort, 2000)
|
||||||
|
|
||||||
|
v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
|
||||||
|
if v4Match != 1 || v6Match != 1 {
|
||||||
|
t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user