mirror of
https://github.com/vishvananda/netlink
synced 2025-03-21 18:46:42 +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,18 +318,25 @@ func parseRawData(data []byte) *ConntrackFlow {
|
||||
// --mask-src ip Source 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
|
||||
type ConntrackFilterType uint8
|
||||
|
||||
const (
|
||||
ConntrackOrigSrcIP = iota // -orig-src ip Source address from original direction
|
||||
ConntrackOrigDstIP // -orig-dst ip Destination address from original direction
|
||||
ConntrackReplySrcIP // --reply-src ip Reply Source IP
|
||||
ConntrackReplyDstIP // --reply-dst ip Reply Destination IP
|
||||
ConntrackReplyAnyIP // Match source or destination reply IP
|
||||
ConntrackNatSrcIP = ConntrackReplySrcIP // deprecated use instead ConntrackReplySrcIP
|
||||
ConntrackNatDstIP = ConntrackReplyDstIP // deprecated use instead ConntrackReplyDstIP
|
||||
ConntrackNatAnyIP = ConntrackReplyAnyIP // deprecated use instaed ConntrackReplyAnyIP
|
||||
ConntrackOrigSrcIP = iota // -orig-src ip Source address from original direction
|
||||
ConntrackOrigDstIP // -orig-dst ip Destination address from original direction
|
||||
ConntrackReplySrcIP // --reply-src ip Reply Source IP
|
||||
ConntrackReplyDstIP // --reply-dst ip Reply Destination 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
|
||||
ConntrackNatDstIP = ConntrackReplyDstIP // deprecated use instead ConntrackReplyDstIP
|
||||
ConntrackNatAnyIP = ConntrackReplyAnyIP // deprecated use instead ConntrackReplyAnyIP
|
||||
)
|
||||
|
||||
type CustomConntrackFilter interface {
|
||||
@ -339,7 +346,9 @@ type CustomConntrackFilter interface {
|
||||
}
|
||||
|
||||
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
|
||||
@ -354,38 +363,89 @@ func (f *ConntrackFilter) AddIP(tp ConntrackFilterType, ip net.IP) error {
|
||||
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
|
||||
// false otherwise
|
||||
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
|
||||
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
|
||||
// -orig-src ip Source address from original direction
|
||||
if elem, found := f.ipFilter[ConntrackOrigSrcIP]; found {
|
||||
match = match && elem.Equal(flow.Forward.SrcIP)
|
||||
|
||||
// IP conntrack filter
|
||||
if len(f.ipFilter) > 0 {
|
||||
// -orig-src ip Source address from original direction
|
||||
if elem, found := f.ipFilter[ConntrackOrigSrcIP]; found {
|
||||
match = match && elem.Equal(flow.Forward.SrcIP)
|
||||
}
|
||||
|
||||
// -orig-dst ip Destination address from original direction
|
||||
if elem, found := f.ipFilter[ConntrackOrigDstIP]; match && found {
|
||||
match = match && elem.Equal(flow.Forward.DstIP)
|
||||
}
|
||||
|
||||
// -src-nat ip Source NAT ip
|
||||
if elem, found := f.ipFilter[ConntrackReplySrcIP]; match && found {
|
||||
match = match && elem.Equal(flow.Reverse.SrcIP)
|
||||
}
|
||||
|
||||
// -dst-nat ip Destination NAT ip
|
||||
if elem, found := f.ipFilter[ConntrackReplyDstIP]; match && found {
|
||||
match = match && elem.Equal(flow.Reverse.DstIP)
|
||||
}
|
||||
|
||||
// Match source or destination reply IP
|
||||
if elem, found := f.ipFilter[ConntrackReplyAnyIP]; match && found {
|
||||
match = match && (elem.Equal(flow.Reverse.SrcIP) || elem.Equal(flow.Reverse.DstIP))
|
||||
}
|
||||
}
|
||||
|
||||
// -orig-dst ip Destination address from original direction
|
||||
if elem, found := f.ipFilter[ConntrackOrigDstIP]; match && found {
|
||||
match = match && elem.Equal(flow.Forward.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
|
||||
}
|
||||
|
||||
// -src-nat ip Source NAT ip
|
||||
if elem, found := f.ipFilter[ConntrackReplySrcIP]; match && found {
|
||||
match = match && elem.Equal(flow.Reverse.SrcIP)
|
||||
}
|
||||
|
||||
// -dst-nat ip Destination NAT ip
|
||||
if elem, found := f.ipFilter[ConntrackReplyDstIP]; match && found {
|
||||
match = match && elem.Equal(flow.Reverse.DstIP)
|
||||
}
|
||||
|
||||
// Match source or destination reply IP
|
||||
if elem, found := f.ipFilter[ConntrackReplyAnyIP]; match && found {
|
||||
match = match && (elem.Equal(flow.Reverse.SrcIP) || elem.Equal(flow.Reverse.DstIP))
|
||||
// -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
|
||||
|
@ -92,10 +92,18 @@ func TestConntrackSocket(t *testing.T) {
|
||||
// Creates some flows and checks that they are correctly fetched from the conntrack table
|
||||
func TestConntrackTableList(t *testing.T) {
|
||||
skipUnlessRoot(t)
|
||||
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_ipv6")
|
||||
}
|
||||
setUpNetlinkTestWithKModule(t, "nf_conntrack")
|
||||
setUpNetlinkTestWithKModule(t, "nf_conntrack_netlink")
|
||||
setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv4")
|
||||
setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv6")
|
||||
|
||||
// Creates a new namespace and bring up the loopback interface
|
||||
origns, ns, h := nsCreateAndEnter(t)
|
||||
@ -107,7 +115,7 @@ func TestConntrackTableList(t *testing.T) {
|
||||
setUpF(t, "/proc/sys/net/netfilter/nf_conntrack_acct", "1")
|
||||
|
||||
// Flush the table to start fresh
|
||||
err := h.ConntrackTableFlush(ConntrackTable)
|
||||
err = h.ConntrackTableFlush(ConntrackTable)
|
||||
CheckErrorFail(t, err)
|
||||
|
||||
// Create 5 udp
|
||||
@ -149,8 +157,16 @@ func TestConntrackTableFlush(t *testing.T) {
|
||||
skipUnlessRoot(t)
|
||||
setUpNetlinkTestWithKModule(t, "nf_conntrack")
|
||||
setUpNetlinkTestWithKModule(t, "nf_conntrack_netlink")
|
||||
setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv4")
|
||||
|
||||
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")
|
||||
// Creates a new namespace and bring up the loopback interface
|
||||
origns, ns, h := nsCreateAndEnter(t)
|
||||
defer netns.Set(*origns)
|
||||
@ -211,7 +227,15 @@ func TestConntrackTableDelete(t *testing.T) {
|
||||
skipUnlessRoot(t)
|
||||
setUpNetlinkTestWithKModule(t, "nf_conntrack")
|
||||
setUpNetlinkTestWithKModule(t, "nf_conntrack_netlink")
|
||||
setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv4")
|
||||
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")
|
||||
}
|
||||
|
||||
// Creates a new namespace and bring up the loopback interface
|
||||
origns, ns, h := nsCreateAndEnter(t)
|
||||
@ -252,6 +276,8 @@ func TestConntrackTableDelete(t *testing.T) {
|
||||
// Create a filter to erase groupB flows
|
||||
filter := &ConntrackFilter{}
|
||||
filter.AddIP(ConntrackOrigDstIP, net.ParseIP("127.0.0.20"))
|
||||
filter.AddProtocol(17)
|
||||
filter.AddPort(ConntrackOrigDstPort, 8000)
|
||||
|
||||
// Flush entries of groupB
|
||||
var deleted uint
|
||||
@ -296,46 +322,52 @@ func TestConntrackFilter(t *testing.T) {
|
||||
flowList = append(flowList, ConntrackFlow{
|
||||
FamilyType: unix.AF_INET,
|
||||
Forward: ipTuple{
|
||||
SrcIP: net.ParseIP("10.0.0.1"),
|
||||
DstIP: net.ParseIP("20.0.0.1"),
|
||||
SrcPort: 1000,
|
||||
DstPort: 2000,
|
||||
SrcIP: net.ParseIP("10.0.0.1"),
|
||||
DstIP: net.ParseIP("20.0.0.1"),
|
||||
SrcPort: 1000,
|
||||
DstPort: 2000,
|
||||
Protocol: 17,
|
||||
},
|
||||
Reverse: ipTuple{
|
||||
SrcIP: net.ParseIP("20.0.0.1"),
|
||||
DstIP: net.ParseIP("192.168.1.1"),
|
||||
SrcPort: 2000,
|
||||
DstPort: 1000,
|
||||
SrcIP: net.ParseIP("20.0.0.1"),
|
||||
DstIP: net.ParseIP("192.168.1.1"),
|
||||
SrcPort: 2000,
|
||||
DstPort: 1000,
|
||||
Protocol: 17,
|
||||
},
|
||||
},
|
||||
ConntrackFlow{
|
||||
FamilyType: unix.AF_INET,
|
||||
Forward: ipTuple{
|
||||
SrcIP: net.ParseIP("10.0.0.2"),
|
||||
DstIP: net.ParseIP("20.0.0.2"),
|
||||
SrcPort: 5000,
|
||||
DstPort: 6000,
|
||||
SrcIP: net.ParseIP("10.0.0.2"),
|
||||
DstIP: net.ParseIP("20.0.0.2"),
|
||||
SrcPort: 5000,
|
||||
DstPort: 6000,
|
||||
Protocol: 6,
|
||||
},
|
||||
Reverse: ipTuple{
|
||||
SrcIP: net.ParseIP("20.0.0.2"),
|
||||
DstIP: net.ParseIP("192.168.1.1"),
|
||||
SrcPort: 6000,
|
||||
DstPort: 5000,
|
||||
SrcIP: net.ParseIP("20.0.0.2"),
|
||||
DstIP: net.ParseIP("192.168.1.1"),
|
||||
SrcPort: 6000,
|
||||
DstPort: 5000,
|
||||
Protocol: 6,
|
||||
},
|
||||
},
|
||||
ConntrackFlow{
|
||||
FamilyType: unix.AF_INET6,
|
||||
Forward: ipTuple{
|
||||
SrcIP: net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"),
|
||||
DstIP: net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"),
|
||||
SrcPort: 1000,
|
||||
DstPort: 2000,
|
||||
SrcIP: net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"),
|
||||
DstIP: net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"),
|
||||
SrcPort: 1000,
|
||||
DstPort: 2000,
|
||||
Protocol: 132,
|
||||
},
|
||||
Reverse: ipTuple{
|
||||
SrcIP: net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"),
|
||||
DstIP: net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"),
|
||||
SrcPort: 2000,
|
||||
DstPort: 1000,
|
||||
SrcIP: net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"),
|
||||
DstIP: net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"),
|
||||
SrcPort: 2000,
|
||||
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)
|
||||
}
|
||||
|
||||
// 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.AddIP(ConntrackOrigSrcIP, net.ParseIP("10.0.0.1"))
|
||||
filterV4.AddProtocol(6)
|
||||
|
||||
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"))
|
||||
|
||||
v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
|
||||
@ -404,4 +478,32 @@ func TestConntrackFilter(t *testing.T) {
|
||||
if v4Match != 2 || v6Match != 1 {
|
||||
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