conntrack

package module
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 16, 2023 License: MIT Imports: 12 Imported by: 17

README

conntrack GoDoc Tests Status Coverage Status Go Report Card

Package conntrack implements the Conntrack subsystem of the Netfilter (Netlink) protocol family. The package is intended to be clear, user-friendly, thoroughly tested and easy to understand.

It is purely written in Go, without any dependency on Cgo or any C library, kernel headers or userspace tools. It uses a native Netlink implementation (https://github.com/mdlayher/netlink) and does not parse or scrape any output of the conntrack command.

It is designed in a way that makes the user acquainted with the structure of the protocol, with a clean separation between the Conntrack types/attributes and the Netfilter layer (implemented in https://github.com/ti-mo/netfilter).

All Conntrack attributes known to the kernel up until version 4.17 are implemented. There is experimental support for manipulating Conntrack 'expectations', beside listening and dumping. The original focus of the package was receiving Conntrack events over Netlink multicast sockets, but was since expanded to be a full implementation supporting queries.

Features

With this library, the user can:

  • Interact with conntrack connections and expectations through Flow and Expect types respectively
  • Create, get, update and delete Flows in an idiomatic way (and Expects, to an extent)
  • Listen for create/update/destroy events
  • Flush (empty) and dump (display) the whole conntrack table, optionally filtering on specific connection marks

There are many usage examples in the godoc.

Contributing

Contributions are absolutely welcome! Before starting work on large changes, please create an issue first, or join #networking on Gophers Slack to discuss the design.

If you encounter a problem implementing the library, please open a GitHub issue for help.

Documentation

Overview

Package conntrack implements the Conntrack subsystem of the Netfilter (Netlink) protocol family. The package is intended to be clear, user-friendly, thoroughly tested and easy to understand.

It is purely written in Go, without any dependency on Cgo or any C library, kernel headers or userspace tools. It uses a native Netlink implementation (https://github.com/mdlayher/netlink) and does not parse or scrape any output of the `conntrack` command.

It is designed in a way that makes the user acquainted with the structure of the protocol, with a clean separation between the Conntrack types/attributes and the Netfilter layer (implemented in https://github.com/ti-mo/netfilter).

All Conntrack attributes known to the kernel up until version 4.17 are implemented. There is experimental support for manipulating Conntrack 'expectations', beside listening and dumping. The original focus of the package was receiving Conntrack events over Netlink multicast sockets, but was since expanded to be a full implementation supporting queries.

Index

Examples

Constants

View Source
const (
	EventUnknown eventType = iota
	EventNew
	EventUpdate
	EventDestroy
	EventExpNew
	EventExpDestroy
)

List of all types of Conntrack events. This is an internal representation unrelated to any message types in the kernel source.

Variables

This section is empty.

Functions

This section is empty.

Types

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

Conn represents a Netlink connection to the Netfilter subsystem and implements all Conntrack actions.

Example (CreateUpdateFlow)
package main

import (
	"log"
	"net/netip"

	"github.com/ti-mo/conntrack"
)

func main() {
	// Open a Conntrack connection.
	c, err := conntrack.Dial(nil)
	if err != nil {
		log.Fatal(err)
	}

	// Set up a new Flow object using a given set of attributes.
	f := conntrack.NewFlow(
		17, 0,
		netip.MustParseAddr("2a00:1450:400e:804::200e"),
		netip.MustParseAddr("2a00:1450:400e:804::200f"),
		1234, 80, 120, 0,
	)

	// Send the Flow to the kernel.
	err = c.Create(f)
	if err != nil {
		log.Fatal(err)
	}

	f.Timeout = 240

	// Update the Flow's timeout to 240 seconds.
	err = c.Update(f)
	if err != nil {
		log.Fatal(err)
	}

	// Query the kernel based on the Flow's source/destination tuples.
	// Returns a new Flow object with its connection ID assigned by the kernel.
	qf, err := c.Get(f)
	if err != nil {
		log.Fatal(err)
	}

	// Print the result. The Flow has a timeout greater than 120 seconds.
	log.Print(qf)
}
Output:

Example (Delete)
package main

import (
	"log"
	"net/netip"

	"github.com/ti-mo/conntrack"
)

func main() {
	// Open a Conntrack connection.
	c, err := conntrack.Dial(nil)
	if err != nil {
		log.Fatal(err)
	}

	f := conntrack.NewFlow(
		6, 0, netip.MustParseAddr("1.2.3.4"), netip.MustParseAddr("5.6.7.8"),
		1234, 80, 120, 0,
	)

	// Create the Flow, will return err if unsuccessful.
	err = c.Create(f)
	if err != nil {
		log.Fatal(err)
	}

	// Delete the Flow based on its IP/port tuple, will return err if unsuccessful.
	err = c.Delete(f)
	if err != nil {
		log.Fatal(err)
	}
}
Output:

Example (DumpFilter)
package main

import (
	"log"
	"net/netip"

	"github.com/ti-mo/conntrack"
)

func main() {
	// Open a Conntrack connection.
	c, err := conntrack.Dial(nil)
	if err != nil {
		log.Fatal(err)
	}

	f1 := conntrack.NewFlow(
		6, 0, netip.MustParseAddr("1.2.3.4"), netip.MustParseAddr("5.6.7.8"),
		1234, 80, 120, 0x00ff, // Set a connection mark
	)

	f2 := conntrack.NewFlow(
		17, 0, netip.MustParseAddr("2a00:1450:400e:804::200e"), netip.MustParseAddr("2a00:1450:400e:804::200f"),
		1234, 80, 120, 0xff00, // Set a connection mark
	)

	_ = c.Create(f1)
	_ = c.Create(f2)

	// Dump all records in the Conntrack table that match the filter's mark/mask.
	df, err := c.DumpFilter(conntrack.Filter{Mark: 0xff00, Mask: 0xff00}, nil)
	if err != nil {
		log.Fatal(err)
	}

	// Print the result. Only f2 is displayed.
	log.Print(df)
}
Output:

Example (Flush)
package main

import (
	"log"

	"github.com/ti-mo/conntrack"
)

func main() {
	// Open a Conntrack connection.
	c, err := conntrack.Dial(nil)
	if err != nil {
		log.Fatal(err)
	}

	// Evict all entries from the conntrack table in the current network namespace.
	err = c.Flush()
	if err != nil {
		log.Fatal(err)
	}
}
Output:

Example (FlushFilter)
package main

import (
	"log"
	"net/netip"

	"github.com/ti-mo/conntrack"
)

func main() {
	// Open a Conntrack connection.
	c, err := conntrack.Dial(nil)
	if err != nil {
		log.Fatal(err)
	}

	f1 := conntrack.NewFlow(
		6, 0, netip.MustParseAddr("1.2.3.4"), netip.MustParseAddr("5.6.7.8"),
		1234, 80, 120, 0x00ff, // Set a connection mark
	)

	f2 := conntrack.NewFlow(
		17, 0, netip.MustParseAddr("2a00:1450:400e:804::200e"), netip.MustParseAddr("2a00:1450:400e:804::200f"),
		1234, 80, 120, 0xff00, // Set a connection mark
	)

	_ = c.Create(f1)
	_ = c.Create(f2)

	// Flush only the second flow matching the filter's mark/mask.
	err = c.FlushFilter(conntrack.Filter{Mark: 0xff00, Mask: 0xff00})
	if err != nil {
		log.Fatal(err)
	}

	// Getting f1 succeeds.
	_, err = c.Get(f1)
	if err != nil {
		log.Fatal(err)
	}

	// Getting f2 will fail, since it was flushed.
	_, err = c.Get(f2)
	if err != nil {
		log.Println("Flow f2 missing, as expected", err)
	}
}
Output:

Example (Listen)
package main

import (
	"fmt"
	"log"

	"github.com/mdlayher/netlink"

	"github.com/ti-mo/conntrack"
	"github.com/ti-mo/netfilter"
)

func main() {
	// Open a Conntrack connection.
	c, err := conntrack.Dial(nil)
	if err != nil {
		log.Fatal(err)
	}

	// Make a buffered channel to receive event updates on.
	evCh := make(chan conntrack.Event, 1024)

	// Listen for all Conntrack and Conntrack-Expect events with 4 decoder goroutines.
	// All errors caught in the decoders are passed on channel errCh.
	errCh, err := c.Listen(evCh, 4, append(netfilter.GroupsCT, netfilter.GroupsCTExp...))
	if err != nil {
		log.Fatal(err)
	}

	// Listen to Conntrack events from all network namespaces on the system.
	err = c.SetOption(netlink.ListenAllNSID, true)
	if err != nil {
		log.Fatal(err)
	}

	// Start a goroutine to print all incoming messages on the event channel.
	go func() {
		for {
			fmt.Println(<-evCh)
		}
	}()

	// Stop the program as soon as an error is caught in a decoder goroutine.
	log.Print(<-errCh)
}
Output:

func Dial

func Dial(config *netlink.Config) (*Conn, error)

Dial opens a new Netfilter Netlink connection and returns it wrapped in a Conn structure that implements the Conntrack API.

func (*Conn) Close

func (c *Conn) Close() error

Close closes a Conn.

If any workers were started using Conn.Listen, blocks until all have terminated.

func (*Conn) Create

func (c *Conn) Create(f Flow) error

Create creates a new Conntrack entry.

func (*Conn) CreateExpect

func (c *Conn) CreateExpect(ex Expect) error

CreateExpect creates a new Conntrack Expect entry. Warning: Experimental, haven't got this to create an Expect correctly. Best-effort implementation based on kernel source.

func (*Conn) Delete

func (c *Conn) Delete(f Flow) error

Delete removes a Conntrack entry given a Flow. Flows are looked up in the conntrack table based on the original and reply tuple. When the Flow's ID field is filled, it must match the ID on the connection returned from the tuple lookup, or the delete will fail.

func (*Conn) Dump

func (c *Conn) Dump(opts *DumpOptions) ([]Flow, error)

Dump gets all Conntrack connections from the kernel in the form of a list of Flow objects.

func (*Conn) DumpExpect

func (c *Conn) DumpExpect() ([]Expect, error)

DumpExpect gets all expected Conntrack expectations from the kernel in the form of a list of Expect objects.

func (*Conn) DumpFilter

func (c *Conn) DumpFilter(f Filter, opts *DumpOptions) ([]Flow, error)

DumpFilter gets all Conntrack connections from the kernel in the form of a list of Flow objects, but only returns Flows matching the connmark specified in the Filter parameter.

func (*Conn) Flush

func (c *Conn) Flush() error

Flush empties the Conntrack table. Deletes all IPv4 and IPv6 entries.

func (*Conn) FlushFilter

func (c *Conn) FlushFilter(f Filter) error

FlushFilter deletes all entries from the Conntrack table matching a given Filter. Both IPv4 and IPv6 entries are considered for deletion.

func (*Conn) Get

func (c *Conn) Get(f Flow) (Flow, error)

Get queries the conntrack table for a connection matching some attributes of a given Flow. The following attributes are considered in the query: TupleOrig or TupleReply, in that order, and Zone. One of TupleOrig or TupleReply is required for a successful query.

func (*Conn) Listen

func (c *Conn) Listen(evChan chan<- Event, numWorkers uint8, groups []netfilter.NetlinkGroup) (chan error, error)

Listen joins the Netfilter connection to a multicast group and starts a given amount of Flow decoders from the Conn to the Flow channel. Returns an error channel the workers will return any errors on. Any error during Flow decoding is fatal and will halt the worker it occurs on. When numWorkers amount of errors have been received on the error channel, no more events will be produced on evChan.

The Conn will be marked as having listeners active, which will prevent Listen from being called again. For listening on other groups, open another socket.

evChan consumers need to be able to keep up with the Event producers. When the channel is full, messages will pile up in the Netlink socket's buffer, putting the socket at risk of being closed by the kernel when it eventually fills up.

Closing the Conn makes all workers terminate silently.

func (*Conn) SetOption

func (c *Conn) SetOption(option netlink.ConnOption, enable bool) error

SetOption enables or disables a netlink socket option for the Conn.

func (*Conn) SetReadBuffer

func (c *Conn) SetReadBuffer(bytes int) error

SetReadBuffer sets the size of the operating system's receive buffer associated with the Conn.

The default read buffer size of a socket is configured with `sysctl net.core.rmem_default`. The maximum buffer size that can be set without elevated privileges is `sysctl net.core.rmem_max`.

func (*Conn) SetWriteBuffer

func (c *Conn) SetWriteBuffer(bytes int) error

SetWriteBuffer sets the size of the operating system's transmit buffer associated with the Conn.

The default write buffer size of a socket is configured with `sysctl net.core.wmem_default`. The maximum buffer size that can be set without elevated privileges is `sysctl net.core.wmem_max`.

func (*Conn) Stats

func (c *Conn) Stats() ([]Stats, error)

Stats returns a list of Stats structures, one per CPU present in the machine. Each Stats structure contains performance counters of all Conntrack actions performed on that specific CPU.

func (*Conn) StatsExpect

func (c *Conn) StatsExpect() ([]StatsExpect, error)

StatsExpect returns a list of StatsExpect structures, one per CPU present in the machine. Each StatsExpect structure indicates how many Expect entries were initialized, created or deleted on each CPU.

func (*Conn) StatsGlobal

func (c *Conn) StatsGlobal() (StatsGlobal, error)

StatsGlobal queries Conntrack for an internal global counter that describes the total amount of Flow entries currently in the Conntrack table. Only the main Conntrack table has this fast query available. To get the amount of Expect entries, execute DumpExpect() and count the amount of entries returned.

Starting from kernels 4.18 and higher, MaxEntries is returned, describing the maximum size of the Conntrack table.

func (*Conn) Update

func (c *Conn) Update(f Flow) error

Update updates a Conntrack entry. Only the following attributes are considered when sending a Flow update: Helper, Timeout, Status, ProtoInfo, Mark, SeqAdj (orig/reply), SynProxy, Labels. All other attributes are immutable past the point of creation. See the ctnetlink_change_conntrack() kernel function for exact behaviour.

type Counter

type Counter struct {

	// true means it's a reply counter,
	// false is the original direction
	Direction bool

	Packets uint64
	Bytes   uint64
}

A Counter holds a pair of counters that represent packets and bytes sent over a Conntrack connection. Direction is true when it's a reply counter. This attribute cannot be changed on a connection and thus cannot be marshaled.

func (Counter) String

func (ctr Counter) String() string

type DumpOptions added in v0.4.1

type DumpOptions struct {
	// ZeroCounters resets all flows' counters to zero after the dump operation.
	ZeroCounters bool
}

DumpOptions is passed as an option to `Dump`-related methods to modify their behaviour.

type Event

type Event struct {
	Type eventType

	Flow   *Flow
	Expect *Expect
}

Event holds information about a Conntrack event.

func (Event) String

func (e Event) String() string

type Expect

type Expect struct {
	ID, Timeout uint32

	TupleMaster, Tuple, Mask Tuple

	Zone uint16

	HelpName, Function string

	Flags, Class uint32

	NAT ExpectNAT
}

Expect represents an 'expected' connection, created by Conntrack/IPTables helpers. Active connections created by helpers are shown by the conntrack tooling as 'RELATED'.

type ExpectNAT

type ExpectNAT struct {
	Direction bool
	Tuple     Tuple
}

ExpectNAT holds NAT information about an expected connection.

type Filter

type Filter struct {
	Mark, Mask uint32
}

Filter is a structure used in dump operations to filter the response based on a given connmark and mask. The mask is applied to the Mark field of all flows in the conntrack table, the result is compared to the filter's Mark. Each flow that matches will be returned by the kernel.

type Flow

type Flow struct {
	ID        uint32
	Timeout   uint32
	Timestamp Timestamp

	Status    Status
	ProtoInfo ProtoInfo
	Helper    Helper

	Zone uint16

	CountersOrig, CountersReply Counter

	SecurityContext Security

	TupleOrig, TupleReply, TupleMaster Tuple

	SeqAdjOrig, SeqAdjReply SequenceAdjust

	Labels, LabelsMask []byte

	Mark, Use uint32

	SynProxy SynProxy
}

Flow represents a snapshot of a Conntrack connection.

func NewFlow

func NewFlow(proto uint8, status StatusFlag, srcAddr, destAddr netip.Addr,
	srcPort, destPort uint16, timeout, mark uint32) Flow

NewFlow returns a new Flow object with the minimum necessary attributes to create a Conntrack entry. Writes values into the Status, Timeout, TupleOrig and TupleReply fields of the Flow.

proto is the layer 4 protocol number of the connection. status is a StatusFlag value, or an ORed combination thereof. srcAddr and dstAddr are the source and destination addresses. srcPort and dstPort are the source and destination ports. timeout is the non-zero time-to-live of a connection in seconds.

type Helper

type Helper struct {
	Name string
	Info []byte
}

A Helper holds the name and info the helper that creates a related connection.

type IPTuple

type IPTuple struct {
	SourceAddress      netip.Addr
	DestinationAddress netip.Addr
}

An IPTuple encodes a source and destination address.

func (IPTuple) IsIPv6

func (ipt IPTuple) IsIPv6() bool

IsIPv6 returns true if the IPTuple contains source and destination addresses that are both IPv6.

type ProtoInfo

type ProtoInfo struct {
	TCP  *ProtoInfoTCP
	DCCP *ProtoInfoDCCP
	SCTP *ProtoInfoSCTP
}

The ProtoInfo structure holds a pointer to one of ProtoInfoTCP, ProtoInfoDCCP or ProtoInfoSCTP.

type ProtoInfoDCCP

type ProtoInfoDCCP struct {
	State, Role  uint8
	HandshakeSeq uint64
}

ProtoInfoDCCP describes the state of a DCCP connection.

type ProtoInfoSCTP

type ProtoInfoSCTP struct {
	State                   uint8
	VTagOriginal, VTagReply uint32
}

ProtoInfoSCTP describes the state of an SCTP connection.

type ProtoInfoTCP

type ProtoInfoTCP struct {
	State               uint8
	OriginalWindowScale uint8
	ReplyWindowScale    uint8
	OriginalFlags       uint16
	ReplyFlags          uint16
}

A ProtoInfoTCP describes the state of a TCP session in both directions. It contains state, window scale and TCP flags.

type ProtoTuple

type ProtoTuple struct {
	Protocol        uint8
	SourcePort      uint16
	DestinationPort uint16

	ICMPv4 bool
	ICMPv6 bool

	ICMPID   uint16
	ICMPType uint8
	ICMPCode uint8
}

A ProtoTuple encodes a protocol number, source port and destination port.

type Security

type Security string

A Security structure holds the security info belonging to a connection. Kernel uses this to store and match SELinux context name. This attribute cannot be changed on a connection and thus cannot be marshaled.

type SequenceAdjust

type SequenceAdjust struct {
	// true means it's a reply adjustment,
	// false is the original direction
	Direction bool

	Position     uint32
	OffsetBefore uint32
	OffsetAfter  uint32
}

SequenceAdjust represents a TCP sequence number adjustment event. Direction is true when it's a reply adjustment.

func (SequenceAdjust) String

func (seq SequenceAdjust) String() string

type Stats

type Stats struct {
	CPUID         uint16
	Found         uint32
	Invalid       uint32
	Ignore        uint32
	Insert        uint32
	InsertFailed  uint32
	Drop          uint32
	EarlyDrop     uint32
	Error         uint32
	SearchRestart uint32
}

Stats represents the Conntrack performance counters of a single CPU (core). It indicates which and how many Flow operations took place on each CPU.

func (Stats) String

func (s Stats) String() string

type StatsExpect

type StatsExpect struct {
	CPUID               uint16
	New, Create, Delete uint32
}

StatsExpect represents the Conntrack Expect performance counters of a single CPU (core). It indicates how many Expect entries were initialized, created or deleted on each CPU.

type StatsGlobal

type StatsGlobal struct {
	Entries, MaxEntries uint32
}

StatsGlobal represents global statistics about the conntrack subsystem.

type Status

type Status struct {
	Value StatusFlag
}

Status represents a snapshot of a conntrack connection's state.

func (Status) Assured

func (s Status) Assured() bool

Assured is set when eg. three-way handshake is completed on a TCP flow.

func (Status) Confirmed

func (s Status) Confirmed() bool

Confirmed is set when the original packet has left the box.

func (Status) DstNAT

func (s Status) DstNAT() bool

DstNAT means the connection needs destination NAT in the original direction.

func (Status) DstNATDone

func (s Status) DstNATDone() bool

DstNATDone is set when destination NAT was applied onto the connection.

func (Status) Dying

func (s Status) Dying() bool

Dying means the connection has concluded and needs to be cleaned up by GC.

func (Status) Expected

func (s Status) Expected() bool

Expected indicates that this connection is an expected connection, created by Conntrack helpers based on the state of another, related connection.

func (Status) FixedTimeout

func (s Status) FixedTimeout() bool

FixedTimeout means the connection's timeout value cannot be changed.

func (Status) Helper

func (s Status) Helper() bool

Helper is set when a helper was explicitly attached using a Conntrack target.

func (Status) Offload

func (s Status) Offload() bool

Offload is set when the connection was offloaded to flow table.

func (Status) SeenReply

func (s Status) SeenReply() bool

SeenReply is set when the flow has seen traffic both ways.

func (Status) SeqAdjust

func (s Status) SeqAdjust() bool

SeqAdjust means the connection needs its TCP sequence to be adjusted.

func (Status) SrcNAT

func (s Status) SrcNAT() bool

SrcNAT means the connection needs source NAT in the original direction.

func (Status) SrcNATDone

func (s Status) SrcNATDone() bool

SrcNATDone is set when source NAT was applied onto the connection.

func (Status) String

func (s Status) String() string

func (Status) Template

func (s Status) Template() bool

Template indicates if the connection is a template.

type StatusFlag

type StatusFlag uint32

StatusFlag describes a status bit in a Status structure.

const (
	StatusExpected  StatusFlag = 1      // IPS_EXPECTED
	StatusSeenReply StatusFlag = 1 << 1 // IPS_SEEN_REPLY
	StatusAssured   StatusFlag = 1 << 2 // IPS_ASSURED
	StatusConfirmed StatusFlag = 1 << 3 // IPS_CONFIRMED
	StatusSrcNAT    StatusFlag = 1 << 4 // IPS_SRC_NAT
	StatusDstNAT    StatusFlag = 1 << 5 // IPS_DST_NAT

	StatusNATMask = StatusDstNAT | StatusSrcNAT // IPS_NAT_MASK

	StatusSeqAdjust  StatusFlag = 1 << 6 // IPS_SEQ_ADJUST
	StatusSrcNATDone StatusFlag = 1 << 7 // IPS_SRC_NAT_DONE
	StatusDstNATDone StatusFlag = 1 << 8 // IPS_DST_NAT_DONE

	StatusNATDoneMask = StatusDstNATDone | StatusSrcNATDone // IPS_NAT_DONE_MASK

	StatusDying        StatusFlag = 1 << 9
	StatusFixedTimeout StatusFlag = 1 << 10 // IPS_FIXED_TIMEOUT
	StatusTemplate     StatusFlag = 1 << 11 // IPS_TEMPLATE
	StatusUntracked    StatusFlag = 1 << 12 // IPS_UNTRACKED
	StatusHelper       StatusFlag = 1 << 13 // IPS_HELPER
	StatusOffload      StatusFlag = 1 << 14 // IPS_OFFLOAD
)

Conntrack connection's status flags, from enum ip_conntrack_status. uapi/linux/netfilter/nf_conntrack_common.h

type SynProxy

type SynProxy struct {
	ISN   uint32
	ITS   uint32
	TSOff uint32
}

SynProxy represents the SYN proxy parameters of a Conntrack flow.

type Timestamp

type Timestamp struct {
	Start time.Time
	Stop  time.Time
}

A Timestamp represents the start and end time of a flow. The timer resolution in the kernel is in nanosecond-epoch. This attribute cannot be changed on a connection and thus cannot be marshaled.

type Tuple

type Tuple struct {
	IP    IPTuple
	Proto ProtoTuple
	Zone  uint16
}

A Tuple holds an IPTuple, ProtoTuple and a Zone.

func (Tuple) String

func (t Tuple) String() string

String returns a string representation of a Tuple.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL