Skip to content

Commit

Permalink
conntrack: prevent potential memory leak
Browse files Browse the repository at this point in the history
Currently, the ConntrackDeleteFilters captures all flow entries
it fails to delete and reports them as errors. This behavior
can potentially lead to memory leaks in high-traffic systems,
where thousands of conntrack flow entries are cleared in a single
batch. With this commit, instead of returning all the un-deleted
flow entries, we now return a single error message for all of them.

Signed-off-by: Daman Arora <[email protected]>
  • Loading branch information
aroradaman authored and aboch committed Feb 6, 2025
1 parent 7c2350b commit 62fb240
Showing 1 changed file with 10 additions and 8 deletions.
18 changes: 10 additions & 8 deletions conntrack_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"encoding/binary"
"errors"
"fmt"
"io/fs"
"net"
"strings"
"time"

"github.com/vishvananda/netlink/nl"
Expand Down Expand Up @@ -159,17 +159,18 @@ func (h *Handle) ConntrackDeleteFilter(table ConntrackTableType, family InetFami
// ConntrackDeleteFilters deletes entries on the specified table matching any of the specified filters using the netlink handle passed
// conntrack -D [table] parameters Delete conntrack or expectation
func (h *Handle) ConntrackDeleteFilters(table ConntrackTableType, family InetFamily, filters ...CustomConntrackFilter) (uint, error) {
var errMsgs []string
var finalErr error
res, err := h.dumpConntrackTable(table, family)
if err != nil {
if !errors.Is(err, ErrDumpInterrupted) {
return 0, err
}
// This allows us to at least do a best effort to try to clean the
// entries matching the filter.
errMsgs = append(errMsgs, err.Error())
finalErr = err
}

var totalFilterErrors int
var matched uint
for _, dataRaw := range res {
flow := parseRawData(dataRaw)
Expand All @@ -178,19 +179,20 @@ func (h *Handle) ConntrackDeleteFilters(table ConntrackTableType, family InetFam
req2 := h.newConntrackRequest(table, family, nl.IPCTNL_MSG_CT_DELETE, unix.NLM_F_ACK)
// skip the first 4 byte that are the netfilter header, the newConntrackRequest is adding it already
req2.AddRawData(dataRaw[4:])
if _, err = req2.Execute(unix.NETLINK_NETFILTER, 0); err == nil {
if _, err = req2.Execute(unix.NETLINK_NETFILTER, 0); err == nil || errors.Is(err, fs.ErrNotExist) {
matched++
// flow is already deleted, no need to match on other filters and continue to the next flow.
break
} else {
totalFilterErrors++
}
errMsgs = append(errMsgs, fmt.Sprintf("failed to delete conntrack flow '%s': %s", flow.String(), err.Error()))
}
}
}
if len(errMsgs) > 0 {
return matched, fmt.Errorf(strings.Join(errMsgs, "; "))
if totalFilterErrors > 0 {
finalErr = errors.Join(finalErr, fmt.Errorf("failed to delete %d conntrack flows with %d filters", totalFilterErrors, len(filters)))
}
return matched, nil
return matched, finalErr
}

func (h *Handle) newConntrackRequest(table ConntrackTableType, family InetFamily, operation, flags int) *nl.NetlinkRequest {
Expand Down

0 comments on commit 62fb240

Please sign in to comment.