conntrack linux,conntrack_linux.go

package netlink

import (

"bytes"

"encoding/binary"

"errors"

"fmt"

"net"

"github.com/vishvananda/netlink/nl"

"golang.org/x/sys/unix"

)

// ConntrackTableType Conntrack table for the netlink operation

type ConntrackTableType uint8

const (

// ConntrackTable Conntrack table

// https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink.h -> #define NFNL_SUBSYS_CTNETLINK 1

ConntrackTable = 1

// ConntrackExpectTable Conntrack expect table

// https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink.h -> #define NFNL_SUBSYS_CTNETLINK_EXP 2

ConntrackExpectTable = 2

)

const (

// backward compatibility with golang 1.6 which does not have io.SeekCurrent

seekCurrent = 1

)

// InetFamily Family type

type InetFamily uint8

// -L [table] [options] List conntrack or expectation table

// -G [table] parameters Get conntrack or expectation

// -I [table] parameters Create a conntrack or expectation

// -U [table] parameters Update a conntrack

// -E [table] [options] Show events

// -C [table] Show counter

// -S Show statistics

// ConntrackTableList returns the flow list of a table of a specific family

// conntrack -L [table] [options] List conntrack or expectation table

func ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {

return pkgHandle.ConntrackTableList(table, family)

}

// ConntrackTableFlush flushes all the flows of a specified table

// conntrack -F [table] Flush table

// The flush operation applies to all the family types

func ConntrackTableFlush(table ConntrackTableType) error {

return pkgHandle.ConntrackTableFlush(table)

}

// ConntrackDeleteFilter deletes entries on the specified table on the base of the filter

// conntrack -D [table] parameters Delete conntrack or expectation

func ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter CustomConntrackFilter) (uint, error) {

return pkgHandle.ConntrackDeleteFilter(table, family, filter)

}

// ConntrackTableList returns the flow list of a table of a specific family using the netlink handle passed

// conntrack -L [table] [options] List conntrack or expectation table

func (h *Handle) ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {

res, err := h.dumpConntrackTable(table, family)

if err != nil {

return nil, err

}

// Deserialize all the flows

var result []*ConntrackFlow

for _, dataRaw := range res {

result = append(result, parseRawData(dataRaw))

}

return result, nil

}

// ConntrackTableFlush flushes all the flows of a specified table using the netlink handle passed

// conntrack -F [table] Flush table

// The flush operation applies to all the family types

func (h *Handle) ConntrackTableFlush(table ConntrackTableType) error {

req := h.newConntrackRequest(table, unix.AF_INET, nl.IPCTNL_MSG_CT_DELETE, unix.NLM_F_ACK)

_, err := req.Execute(unix.NETLINK_NETFILTER, 0)

return err

}

// ConntrackDeleteFilter deletes entries on the specified table on the base of the filter using the netlink handle passed

// conntrack -D [table] parameters Delete conntrack or expectation

func (h *Handle) ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter CustomConntrackFilter) (uint, error) {

res, err := h.dumpConntrackTable(table, family)

if err != nil {

return 0, err

}

var matched uint

for _, dataRaw := range res {

flow := parseRawData(dataRaw)

if match := filter.MatchConntrackFlow(flow); match {

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:])

req2.Execute(unix.NETLINK_NETFILTER, 0)

matched++

}

}

return matched, nil

}

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

// Create the Netlink request object

req := h.newNetlinkRequest((int(table)<<8)|operation, flags)

// Add the netfilter header

msg := &nl.Nfgenmsg{

NfgenFamily: uint8(family),

Version: nl.NFNETLINK_V0,

ResId: 0,

}

req.AddData(msg)

return req

}

func (h *Handle) dumpConntrackTable(table ConntrackTableType, family InetFamily) ([][]byte, error) {

req := h.newConntrackRequest(table, family, nl.IPCTNL_MSG_CT_GET, unix.NLM_F_DUMP)

return req.Execute(unix.NETLINK_NETFILTER, 0)

}

// The full conntrack flow structure is very complicated and can be found in the file:

// http://git.netfilter.org/libnetfilter_conntrack/tree/include/internal/object.h

// For the time being, the structure below allows to parse and extract the base information of a flow

type ipTuple struct {

Bytes uint64

DstIP net.IP

DstPort uint16

Packets uint64

Protocol uint8

SrcIP net.IP

SrcPort uint16

}

type ConntrackFlow struct {

FamilyType uint8

Forward ipTuple

Reverse ipTuple

Mark uint32

}

func (s *ConntrackFlow) String() string {

// conntrack cmd output:

// udp 17 src=127.0.0.1 dst=127.0.0.1 sport=4001 dport=1234 packets=5 bytes=532 [UNREPLIED] src=127.0.0.1 dst=127.0.0.1 sport=1234 dport=4001 packets=10 bytes=1078 mark=0

return fmt.Sprintf("%s\t%d src=%s dst=%s sport=%d dport=%d packets=%d bytes=%d\tsrc=%s dst=%s sport=%d dport=%d packets=%d bytes=%d mark=%d",

nl.L4ProtoMap[s.Forward.Protocol], s.Forward.Protocol,

s.Forward.SrcIP.String(), s.Forward.DstIP.String(), s.Forward.SrcPort, s.Forward.DstPort, s.Forward.Packets, s.Forward.Bytes,

s.Reverse.SrcIP.String(), s.Reverse.DstIP.String(), s.Reverse.SrcPort, s.Reverse.DstPort, s.Reverse.Packets, s.Reverse.Bytes,

s.Mark)

}

// This method parse the ip tuple structure

// The message structure is the following:

//

//

//

//

//

func parseIpTuple(reader *bytes.Reader, tpl *ipTuple) uint8 {

for i := 0; i < 2; i++ {

_, t, _, v := parseNfAttrTLV(reader)

switch t {

case nl.CTA_IP_V4_SRC, nl.CTA_IP_V6_SRC:

tpl.SrcIP = v

case nl.CTA_IP_V4_DST, nl.CTA_IP_V6_DST:

tpl.DstIP = v

}

}

// Skip the next 4 bytes nl.NLA_F_NESTED|nl.CTA_TUPLE_PROTO

reader.Seek(4, seekCurrent)

_, t, _, v := parseNfAttrTLV(reader)

if t == nl.CTA_PROTO_NUM {

tpl.Protocol = uint8(v[0])

}

// Skip some padding 3 bytes

reader.Seek(3, seekCurrent)

for i := 0; i < 2; i++ {

_, t, _ := parseNfAttrTL(reader)

switch t {

case nl.CTA_PROTO_SRC_PORT:

parseBERaw16(reader, &tpl.SrcPort)

case nl.CTA_PROTO_DST_PORT:

parseBERaw16(reader, &tpl.DstPort)

}

// Skip some padding 2 byte

reader.Seek(2, seekCurrent)

}

return tpl.Protocol

}

func parseNfAttrTLV(r *bytes.Reader) (isNested bool, attrType, len uint16, value []byte) {

isNested, attrType, len = parseNfAttrTL(r)

value = make([]byte, len)

binary.Read(r, binary.BigEndian, &value)

return isNested, attrType, len, value

}

func parseNfAttrTL(r *bytes.Reader) (isNested bool, attrType, len uint16) {

binary.Read(r, nl.NativeEndian(), &len)

len -= nl.SizeofNfattr

binary.Read(r, nl.NativeEndian(), &attrType)

isNested = (attrType & nl.NLA_F_NESTED) == nl.NLA_F_NESTED

attrType = attrType & (nl.NLA_F_NESTED - 1)

return isNested, attrType, len

}

func parseBERaw16(r *bytes.Reader, v *uint16) {

binary.Read(r, binary.BigEndian, v)

}

func parseBERaw32(r *bytes.Reader, v *uint32) {

binary.Read(r, binary.BigEndian, v)

}

func parseBERaw64(r *bytes.Reader, v *uint64) {

binary.Read(r, binary.BigEndian, v)

}

func parseByteAndPacketCounters(r *bytes.Reader) (bytes, packets uint64) {

for i := 0; i < 2; i++ {

switch _, t, _ := parseNfAttrTL(r); t {

case nl.CTA_COUNTERS_BYTES:

parseBERaw64(r, &bytes)

case nl.CTA_COUNTERS_PACKETS:

parseBERaw64(r, &packets)

default:

return

}

}

return

}

func parseConnectionMark(r *bytes.Reader) (mark uint32) {

parseBERaw32(r, &mark)

return

}

func parseRawData(data []byte) *ConntrackFlow {

s := &ConntrackFlow{}

// First there is the Nfgenmsg header

// consume only the family field

reader := bytes.NewReader(data)

binary.Read(reader, nl.NativeEndian(), &s.FamilyType)

// skip rest of the Netfilter header

reader.Seek(3, seekCurrent)

// The message structure is the following:

// 4 bytes

// 4 bytes

// flow information of the forward flow

// 4 bytes

// 4 bytes

// flow information of the reverse flow

for reader.Len() > 0 {

if nested, t, l := parseNfAttrTL(reader); nested {

switch t {

case nl.CTA_TUPLE_ORIG:

if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP {

parseIpTuple(reader, &s.Forward)

}

case nl.CTA_TUPLE_REPLY:

if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP {

parseIpTuple(reader, &s.Reverse)

} else {

// Header not recognized skip it

reader.Seek(int64(l), seekCurrent)

}

case nl.CTA_COUNTERS_ORIG:

s.Forward.Bytes, s.Forward.Packets = parseByteAndPacketCounters(reader)

case nl.CTA_COUNTERS_REPLY:

s.Reverse.Bytes, s.Reverse.Packets = parseByteAndPacketCounters(reader)

}

} else {

switch t {

case nl.CTA_MARK:

s.Mark = parseConnectionMark(reader)

}

}

}

return s

}

// Conntrack parameters and options:

// -n, --src-nat ip source NAT ip

// -g, --dst-nat ip destination NAT ip

// -j, --any-nat ip source or destination NAT ip

// -m, --mark mark Set mark

// -c, --secmark secmark Set selinux secmark

// -e, --event-mask eventmask Event mask, eg. NEW,DESTROY

// -z, --zero Zero counters while listing

// -o, --output type[,...] Output format, eg. xml

// -l, --label label[,...] conntrack labels

// Common parameters and options:

// -s, --src, --orig-src ip Source address from original direction

// -d, --dst, --orig-dst ip Destination address from original direction

// -r, --reply-src ip Source address from reply direction

// -q, --reply-dst ip Destination address from reply direction

// -p, --protonum proto Layer 4 Protocol, eg. 'tcp'

// -f, --family proto Layer 3 Protocol, eg. 'ipv6'

// -t, --timeout timeout Set timeout

// -u, --status status Set status, eg. ASSURED

// -w, --zone value Set conntrack zone

// --orig-zone value Set zone for original direction

// --reply-zone value Set zone for reply direction

// -b, --buffer-size Netlink socket buffer size

// --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

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 {

// MatchConntrackFlow applies the filter to the flow and returns true if the flow matches

// the filter or false otherwise

MatchConntrackFlow(flow *ConntrackFlow) bool

}

type ConntrackFilter struct {

ipNetFilter map[ConntrackFilterType]*net.IPNet

portFilter map[ConntrackFilterType]uint16

protoFilter uint8

}

// 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.ipNetFilter[tp]; ok {

return errors.New("Filter attribute already present")

}

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")

}

return f.AddIPNet(tp, NewIPNet(ip))

}

// 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.ipNetFilter) == 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

// IP conntrack filter

if len(f.ipNetFilter) > 0 {

// -orig-src ip Source address from original direction

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.ipNetFilter[ConntrackOrigDstIP]; match && found {

match = match && elem.Contains(flow.Forward.DstIP)

}

// -src-nat ip Source NAT ip

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.ipNetFilter[ConntrackReplyDstIP]; match && found {

match = match && elem.Contains(flow.Reverse.DstIP)

}

// Match source or destination reply IP

if elem, found := f.ipNetFilter[ConntrackReplyAnyIP]; match && found {

match = match && (elem.Contains(flow.Reverse.SrcIP) || elem.Contains(flow.Reverse.DstIP))

}

}

// Layer 4 Port filter

if len(f.portFilter) > 0 {

// -orig-port-src portSource port from original direction

if elem, found := f.portFilter[ConntrackOrigSrcPort]; match && found {

match = match && elem == flow.Forward.SrcPort

}

// -orig-port-dst portDestination port from original direction

if elem, found := f.portFilter[ConntrackOrigDstPort]; match && found {

match = match && elem == flow.Forward.DstPort

}

}

return match

}

var _ CustomConntrackFilter = (*ConntrackFilter)(nil)

一键复制

编辑

Web IDE

原始数据

按行查看

历史

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值