Skip to content

Commit

Permalink
conntrack: allow to filter by subnet
Browse files Browse the repository at this point in the history
Add a new conntrack filter to be able to filter by subnet, in
addition to current IP address filter.

Signed-off-by: Antonio Ojea <aojea@redhat.com>
  • Loading branch information
Antonio Ojea committed Mar 27, 2021
1 parent 66fce01 commit e9f53c8
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 19 deletions.
60 changes: 41 additions & 19 deletions conntrack_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,23 +346,45 @@ type CustomConntrackFilter interface {
}

type ConntrackFilter struct {
ipFilter map[ConntrackFilterType]net.IP
ipNetFilter map[ConntrackFilterType]*net.IPNet
portFilter map[ConntrackFilterType]uint16
protoFilter uint8
}

// AddIP adds an IP to the conntrack filter
func (f *ConntrackFilter) AddIP(tp ConntrackFilterType, ip net.IP) error {
if f.ipFilter == nil {
f.ipFilter = make(map[ConntrackFilterType]net.IP)
// AddIPNet adds a IP subnet to the conntrack filter
func (f *ConntrackFilter) AddIPNet(tp ConntrackFilterType, ipNet *net.IPNet) error {
if ipNet == nil {
return fmt.Errorf("Filter attribute empty")
}
if f.ipNetFilter == nil {
f.ipNetFilter = make(map[ConntrackFilterType]*net.IPNet)
}
if _, ok := f.ipFilter[tp]; ok {
if _, ok := f.ipNetFilter[tp]; ok {
return errors.New("Filter attribute already present")
}
f.ipFilter[tp] = ip
f.ipNetFilter[tp] = ipNet
return nil
}

// AddIP adds an IP to the conntrack filter
func (f *ConntrackFilter) AddIP(tp ConntrackFilterType, ip net.IP) error {
if ip == nil {
return fmt.Errorf("Filter attribute empty")
}
// Convert IP to a subnet with a full mask
// and use the AddIPNet method.
// A subnet with full mask 192.168.1.1/32
// contains the IP subnet 192.168.1.1
fullMask := 32
// check if is an IPv6 address
if ip.To4() == nil && ip.To16() != nil {
fullMask = 128
}
m := net.CIDRMask(fullMask, fullMask)
ipNet := &net.IPNet{IP: ip.Mask(m), Mask: m}
return f.AddIPNet(tp, ipNet)
}

// 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 {
Expand Down Expand Up @@ -394,7 +416,7 @@ func (f *ConntrackFilter) AddProtocol(proto uint8) error {
// 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 && len(f.portFilter) == 0 && f.protoFilter == 0 {
if len(f.ipNetFilter) == 0 && len(f.portFilter) == 0 && f.protoFilter == 0 {
// empty filter always not match
return false
}
Expand All @@ -408,30 +430,30 @@ func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool {
match := true

// IP conntrack filter
if len(f.ipFilter) > 0 {
if len(f.ipNetFilter) > 0 {
// -orig-src ip Source address from original direction
if elem, found := f.ipFilter[ConntrackOrigSrcIP]; found {
match = match && elem.Equal(flow.Forward.SrcIP)
if elem, found := f.ipNetFilter[ConntrackOrigSrcIP]; found {
match = match && elem.Contains(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)
if elem, found := f.ipNetFilter[ConntrackOrigDstIP]; match && found {
match = match && elem.Contains(flow.Forward.DstIP)
}

// -src-nat ip Source NAT ip
if elem, found := f.ipFilter[ConntrackReplySrcIP]; match && found {
match = match && elem.Equal(flow.Reverse.SrcIP)
if elem, found := f.ipNetFilter[ConntrackReplySrcIP]; match && found {
match = match && elem.Contains(flow.Reverse.SrcIP)
}

// -dst-nat ip Destination NAT ip
if elem, found := f.ipFilter[ConntrackReplyDstIP]; match && found {
match = match && elem.Equal(flow.Reverse.DstIP)
if elem, found := f.ipNetFilter[ConntrackReplyDstIP]; match && found {
match = match && elem.Contains(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))
if elem, found := f.ipNetFilter[ConntrackReplyAnyIP]; match && found {
match = match && (elem.Contains(flow.Reverse.SrcIP) || elem.Contains(flow.Reverse.DstIP))
}
}

Expand Down
59 changes: 59 additions & 0 deletions conntrack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,65 @@ func TestConntrackFilter(t *testing.T) {
t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match)
}

// SrcIPNet filter
filterV4 = &ConntrackFilter{}
filterV4.AddIPNet(ConntrackOrigSrcIP, &net.IPNet{IP: net.ParseIP("10.0.0.1"), Mask: net.CIDRMask(12, 32)})

filterV6 = &ConntrackFilter{}
filterV6.AddIPNet(ConntrackOrigSrcIP, &net.IPNet{IP: net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"), Mask: net.CIDRMask(64, 128)})

v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
if v4Match != 2 || v6Match != 1 {
t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
}

// DstIpNet filter
filterV4 = &ConntrackFilter{}
filterV4.AddIPNet(ConntrackOrigDstIP, &net.IPNet{IP: net.ParseIP("20.0.0.1"), Mask: net.CIDRMask(12, 32)})

filterV6 = &ConntrackFilter{}
filterV6.AddIPNet(ConntrackOrigDstIP, &net.IPNet{IP: net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"), Mask: net.CIDRMask(64, 128)})

v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
if v4Match != 2 || v6Match != 1 {
t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
}

// SrcIPNet for NAT
filterV4 = &ConntrackFilter{}
filterV4.AddIPNet(ConntrackReplySrcIP, &net.IPNet{IP: net.ParseIP("20.0.0.1"), Mask: net.CIDRMask(12, 32)})

filterV6 = &ConntrackFilter{}
filterV6.AddIPNet(ConntrackReplySrcIP, &net.IPNet{IP: net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"), Mask: net.CIDRMask(64, 128)})

v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
if v4Match != 2 || v6Match != 1 {
t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
}

// DstIPNet for NAT
filterV4 = &ConntrackFilter{}
filterV4.AddIPNet(ConntrackReplyDstIP, &net.IPNet{IP: net.ParseIP("192.168.1.1"), Mask: net.CIDRMask(12, 32)})

filterV6 = &ConntrackFilter{}
filterV6.AddIPNet(ConntrackReplyDstIP, &net.IPNet{IP: net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"), Mask: net.CIDRMask(64, 128)})

v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
if v4Match != 2 || v6Match != 0 {
t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match)
}

// AnyIpNet for Nat
filterV4 = &ConntrackFilter{}
filterV4.AddIPNet(ConntrackReplyAnyIP, &net.IPNet{IP: net.ParseIP("192.168.1.1"), Mask: net.CIDRMask(12, 32)})

filterV6 = &ConntrackFilter{}
filterV6.AddIPNet(ConntrackReplyAnyIP, &net.IPNet{IP: net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"), Mask: net.CIDRMask(64, 128)})

v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
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)
Expand Down

0 comments on commit e9f53c8

Please sign in to comment.