Commit b9405bcc authored by Bryan Boreham's avatar Bryan Boreham
Browse files

Remove our own copy of the upstream library

parent 73f35fd6
Showing with 0 additions and 634 deletions
+0 -634
The MIT License (MIT)
Copyright (c) 2015 typetypetype
Copyright (c) 2017 josephglanville
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
package conntrack
import (
"encoding/binary"
"fmt"
"net"
"syscall"
"unsafe"
"golang.org/x/sys/unix"
)
const (
sizeofGenmsg = uint32(unsafe.Sizeof(unix.Nfgenmsg{})) // TODO
)
type ConntrackListReq struct {
Header syscall.NlMsghdr
Body unix.Nfgenmsg
}
func (c *ConntrackListReq) toWireFormat() []byte {
// adapted from syscall/NetlinkRouteRequest.toWireFormat
b := make([]byte, c.Header.Len)
*(*uint32)(unsafe.Pointer(&b[0:4][0])) = c.Header.Len
*(*uint16)(unsafe.Pointer(&b[4:6][0])) = c.Header.Type
*(*uint16)(unsafe.Pointer(&b[6:8][0])) = c.Header.Flags
*(*uint32)(unsafe.Pointer(&b[8:12][0])) = c.Header.Seq
*(*uint32)(unsafe.Pointer(&b[12:16][0])) = c.Header.Pid
b[16] = byte(c.Body.Nfgen_family)
b[17] = byte(c.Body.Version)
*(*uint16)(unsafe.Pointer(&b[18:20][0])) = c.Body.Res_id
return b
}
func connectNetfilter(bufferSize int, groups uint32) (int, *syscall.SockaddrNetlink, error) {
s, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_NETFILTER)
if err != nil {
return 0, nil, err
}
lsa := &syscall.SockaddrNetlink{
Family: syscall.AF_NETLINK,
Groups: groups,
}
if err := syscall.Bind(s, lsa); err != nil {
return 0, nil, err
}
if bufferSize > 0 {
if err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_RCVBUFFORCE, bufferSize); err != nil {
if err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_RCVBUF, bufferSize); err != nil {
return 0, nil, err
}
}
}
return s, lsa, nil
}
// Established lists all established TCP connections.
func Established(bufferSize int) ([]Flow, error) {
s, lsa, err := connectNetfilter(bufferSize, 0)
if err != nil {
return nil, err
}
defer syscall.Close(s)
var flows []Flow
msg := ConntrackListReq{
Header: syscall.NlMsghdr{
Len: syscall.NLMSG_HDRLEN + sizeofGenmsg,
Type: (NFNL_SUBSYS_CTNETLINK << 8) | uint16(IpctnlMsgCtGet),
Flags: syscall.NLM_F_REQUEST | syscall.NLM_F_DUMP,
Pid: 0,
Seq: 0,
},
Body: unix.Nfgenmsg{
Nfgen_family: syscall.AF_INET,
Version: NFNETLINK_V0,
Res_id: 0,
},
}
wb := msg.toWireFormat()
if err := syscall.Sendto(s, wb, 0, lsa); err != nil {
return nil, err
}
readMsgs(s, func(c Flow) {
if c.MsgType != NfctMsgUpdate {
return
}
flows = append(flows, c)
})
return flows, nil
}
// Follow gives a channel with all changes.
func Follow(bufferSize int) (<-chan Flow, func(), error) {
s, _, err := connectNetfilter(bufferSize, NF_NETLINK_CONNTRACK_NEW|NF_NETLINK_CONNTRACK_UPDATE|NF_NETLINK_CONNTRACK_DESTROY)
stop := func() {
syscall.Close(s)
}
if err != nil {
return nil, stop, err
}
res := make(chan Flow, 1)
go func() {
defer syscall.Close(s)
if err := readMsgs(s, func(c Flow) {
res <- c
}); err != nil {
close(res)
return
}
}()
return res, stop, nil
}
func readMsgs(s int, cb func(Flow)) error {
for {
// TODO(jpg): Re-use the receive buffer.
// Will require copying any byte slices we will take pointers to
rb := make([]byte, syscall.Getpagesize())
nr, _, err := syscall.Recvfrom(s, rb, 0)
if err != nil {
return err
}
msgs, err := syscall.ParseNetlinkMessage(rb[:nr])
if err != nil {
return err
}
for _, msg := range msgs {
if err := nfnlIsError(msg.Header); err != nil {
}
if nflnSubsysID(msg.Header.Type) != NFNL_SUBSYS_CTNETLINK {
return fmt.Errorf(
"unexpected subsys_id: %d\n",
nflnSubsysID(msg.Header.Type),
)
}
flow, err := parsePayload(msg.Data[sizeofGenmsg:])
if err != nil {
return err
}
// Disregard non-TCP flows for now
if flow.Original.Proto != syscall.IPPROTO_TCP {
continue
}
// Taken from conntrack/parse.c:__parse_message_type
switch CntlMsgTypes(nflnMsgType(msg.Header.Type)) {
case IpctnlMsgCtNew:
flow.MsgType = NfctMsgUpdate
if msg.Header.Flags&(syscall.NLM_F_CREATE|syscall.NLM_F_EXCL) > 0 {
flow.MsgType = NfctMsgNew
}
case IpctnlMsgCtDelete:
flow.MsgType = NfctMsgDestroy
}
cb(*flow)
}
}
}
type Flow struct {
MsgType NfConntrackMsg
Original Meta
Reply Meta
State TCPState
Status CtStatus
ID uint32
}
type Layer3 struct {
SrcIP net.IP
DstIP net.IP
}
type Layer4 struct {
SrcPort uint16
DstPort uint16
Proto uint8
}
type Meta struct {
Layer3
Layer4
}
func parsePayload(b []byte) (*Flow, error) {
// Adapted from libnetfilter_conntrack/src/conntrack/parse_mnl.c
flow := &Flow{}
attrs, err := parseAttrs(b)
if err != nil {
return flow, err
}
for _, attr := range attrs {
switch CtattrType(attr.Typ) {
case CtaTupleOrig:
parseTuple(attr.Msg, &flow.Original)
case CtaTupleReply:
parseTuple(attr.Msg, &flow.Reply)
case CtaProtoinfo:
parseProtoinfo(attr.Msg, flow)
case CtaId:
flow.ID = binary.BigEndian.Uint32(attr.Msg)
case CtaStatus:
flow.Status = CtStatus(binary.BigEndian.Uint32(attr.Msg))
}
}
return flow, nil
}
func parseTuple(b []byte, meta *Meta) error {
attrs, err := parseAttrs(b)
if err != nil {
return fmt.Errorf("invalid tuple attr: %s", err)
}
for _, attr := range attrs {
switch CtattrTuple(attr.Typ) {
case CtaTupleUnspec:
case CtaTupleIp:
if err := parseIP(attr.Msg, meta); err != nil {
return err
}
case CtaTupleProto:
parseProto(attr.Msg, meta)
}
}
return nil
}
func parseIP(b []byte, meta *Meta) error {
attrs, err := parseAttrs(b)
if err != nil {
return fmt.Errorf("invalid tuple attr: %s", err)
}
for _, attr := range attrs {
// TODO(jpg) IPv6 support
switch CtattrIp(attr.Typ) {
case CtaIpV4Src:
meta.SrcIP = net.IP(attr.Msg) // TODO: copy so we can reuse the buffer?
case CtaIpV4Dst:
meta.DstIP = net.IP(attr.Msg) // TODO: copy so we can reuse the buffer?
}
}
return nil
}
func parseProto(b []byte, meta *Meta) error {
attrs, err := parseAttrs(b)
if err != nil {
return fmt.Errorf("invalid tuple attr: %s", err)
}
for _, attr := range attrs {
switch CtattrL4proto(attr.Typ) {
case CtaProtoNum:
meta.Proto = uint8(attr.Msg[0])
case CtaProtoSrcPort:
meta.SrcPort = binary.BigEndian.Uint16(attr.Msg)
case CtaProtoDstPort:
meta.DstPort = binary.BigEndian.Uint16(attr.Msg)
}
}
return nil
}
func parseProtoinfo(b []byte, flow *Flow) error {
attrs, err := parseAttrs(b)
if err != nil {
return fmt.Errorf("invalid tuple attr: %s", err)
}
for _, attr := range attrs {
switch CtattrProtoinfo(attr.Typ) {
case CtaProtoinfoTcp:
if err := parseProtoinfoTCP(attr.Msg, flow); err != nil {
return err
}
default:
// we're not interested in other protocols
}
}
return nil
}
func parseProtoinfoTCP(b []byte, flow *Flow) error {
attrs, err := parseAttrs(b)
if err != nil {
return fmt.Errorf("invalid tuple attr: %s", err)
}
for _, attr := range attrs {
switch CtattrProtoinfoTcp(attr.Typ) {
case CtaProtoinfoTcpState:
flow.State = TCPState(attr.Msg[0])
default:
// we're not interested in other protocols
}
}
return nil
}
package conntrack
const (
// #defined in libnfnetlink/include/libnfnetlink/linux_nfnetlink.h
NFNL_SUBSYS_CTNETLINK = 1
NFNETLINK_V0 = 0
// #defined in libnfnetlink/include/libnfnetlink/linux_nfnetlink_compat.h
NF_NETLINK_CONNTRACK_NEW = 0x00000001
NF_NETLINK_CONNTRACK_UPDATE = 0x00000002
NF_NETLINK_CONNTRACK_DESTROY = 0x00000004
// #defined in libnfnetlink/include/libnfnetlink/libnfnetlink.h
NLA_F_NESTED = uint16(1 << 15)
NLA_F_NET_BYTEORDER = uint16(1 << 14)
NLA_TYPE_MASK = ^(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
)
type NfConntrackMsg int
const (
NfctMsgUnknown NfConntrackMsg = 0
NfctMsgNew NfConntrackMsg = 1 << 0
NfctMsgUpdate NfConntrackMsg = 1 << 1
NfctMsgDestroy NfConntrackMsg = 1 << 2
)
// Taken from libnetfilter_conntrack/src/conntrack/snprintf.c
type TCPState uint8
const (
TCPStateNone = iota
TCPStateSynSent
TCPStateSynRecv
TCPStateEstablished
TCPStateFinWait
TCPStateCloseWait
TCPStateLastAck
TCPStateTimeWait
TCPStateClose
TCPStateListen
TCPStateMax
TCPStateIgnore
)
func (s TCPState) String() string {
return map[TCPState]string{
TCPStateNone: "NONE",
TCPStateSynSent: "SYN_SENT",
TCPStateSynRecv: "SYN_RECV",
TCPStateEstablished: "ESTABLISHED",
TCPStateFinWait: "FIN_WAIT",
TCPStateCloseWait: "CLOSE_WAIT",
TCPStateLastAck: "LAST_ACK",
TCPStateTimeWait: "TIME_WAIT",
TCPStateClose: "CLOSE",
TCPStateListen: "LISTEN",
TCPStateMax: "MAX",
TCPStateIgnore: "IGNORE",
}[s]
}
// Taken from include/uapi/linux/netfilter/nf_conntrack_common.h
type CtStatus uint32
const (
IPS_EXPECTED CtStatus = 1 << iota
IPS_SEEN_REPLY
IPS_ASSURED
IPS_CONFIRMED
IPS_SRC_NAT
IPS_DST_NAT
IPS_SEQ_ADJUST
IPS_SRC_NAT_DONE
IPS_DST_NAT_DONE
IPS_DYING
IPS_FIXED_TIMEOUT
IPS_TEMPLATE
IPS_UNTRACKED
IPS_HELPER
IPS_OFFLOAD
IPS_NAT_MASK = (IPS_DST_NAT | IPS_SRC_NAT)
IPS_NAT_DONE_MASK = (IPS_DST_NAT_DONE | IPS_SRC_NAT_DONE)
)
// Taken from libnetfilter_conntrack: git://git.netfilter.org/libnetfilter_conntrack
// include/libnetfilter_conntrack/linux_nfnetlink_conntrack.h
type CntlMsgTypes int
const (
IpctnlMsgCtNew CntlMsgTypes = 0
IpctnlMsgCtGet CntlMsgTypes = 1
IpctnlMsgCtDelete CntlMsgTypes = 2
IpctnlMsgCtGetCtrzero CntlMsgTypes = 3
IpctnlMsgCtGetStatsCpu CntlMsgTypes = 4
IpctnlMsgCtGetStats CntlMsgTypes = 5
IpctnlMsgCtGetDying CntlMsgTypes = 6
IpctnlMsgCtGetUnconfirmed CntlMsgTypes = 7
IpctnlMsgMax CntlMsgTypes = 8
)
package conntrack
// code generated by enum2go.
type CtattrType int
const (
CtaUnspec CtattrType = 0
CtaTupleOrig CtattrType = 1
CtaTupleReply CtattrType = 2
CtaStatus CtattrType = 3
CtaProtoinfo CtattrType = 4
CtaHelp CtattrType = 5
CtaNatSrc CtattrType = 6
CtaTimeout CtattrType = 7
CtaMark CtattrType = 8
CtaCountersOrig CtattrType = 9
CtaCountersReply CtattrType = 10
CtaUse CtattrType = 11
CtaId CtattrType = 12
CtaNatDst CtattrType = 13
CtaTupleMaster CtattrType = 14
CtaNatSeqAdjOrig CtattrType = 15
CtaNatSeqAdjReply CtattrType = 16
CtaSecmark CtattrType = 17
CtaZone CtattrType = 18
CtaSecctx CtattrType = 19
CtaTimestamp CtattrType = 20
CtaMarkMask CtattrType = 21
CtaLabels CtattrType = 22
CtaLabelsMask CtattrType = 23
CtaMax CtattrType = 24
)
type CtattrTuple int
const (
CtaTupleUnspec CtattrTuple = 0
CtaTupleIp CtattrTuple = 1
CtaTupleProto CtattrTuple = 2
CtaTupleMax CtattrTuple = 3
)
type CtattrCounters int
const (
CtaCountersUnspec CtattrCounters = 0
CtaCountersPackets CtattrCounters = 1 /* 64bit counters */
CtaCountersBytes CtattrCounters = 2 /* 64bit counters */
CtaCounters32Packets CtattrCounters = 3 /* old 32bit counters, unused */
CtaCounters32Bytes CtattrCounters = 4 /* old 32bit counters, unused */
CtaCountersMax CtattrCounters = 5
)
type CtattrIp int
const (
CtaIpUnspec CtattrIp = 0
CtaIpV4Src CtattrIp = 1
CtaIpV4Dst CtattrIp = 2
CtaIpV6Src CtattrIp = 3
CtaIpV6Dst CtattrIp = 4
CtaIpMax CtattrIp = 5
)
type CtattrL4proto int
const (
CtaProtoUnspec CtattrL4proto = 0
CtaProtoNum CtattrL4proto = 1
CtaProtoSrcPort CtattrL4proto = 2
CtaProtoDstPort CtattrL4proto = 3
CtaProtoIcmpId CtattrL4proto = 4
CtaProtoIcmpType CtattrL4proto = 5
CtaProtoIcmpCode CtattrL4proto = 6
CtaProtoIcmpv6Id CtattrL4proto = 7
CtaProtoIcmpv6Type CtattrL4proto = 8
CtaProtoIcmpv6Code CtattrL4proto = 9
CtaProtoMax CtattrL4proto = 10
)
type CtattrProtoinfo int
const (
CtaProtoinfoUnspec CtattrProtoinfo = 0
CtaProtoinfoTcp CtattrProtoinfo = 1
CtaProtoinfoDccp CtattrProtoinfo = 2
CtaProtoinfoSctp CtattrProtoinfo = 3
CtaProtoinfoMax CtattrProtoinfo = 4
)
type CtattrProtoinfoTcp int
const (
CtaProtoinfoTcpUnspec CtattrProtoinfoTcp = 0
CtaProtoinfoTcpState CtattrProtoinfoTcp = 1
CtaProtoinfoTcpWscaleOriginal CtattrProtoinfoTcp = 2
CtaProtoinfoTcpWscaleReply CtattrProtoinfoTcp = 3
CtaProtoinfoTcpFlagsOriginal CtattrProtoinfoTcp = 4
CtaProtoinfoTcpFlagsReply CtattrProtoinfoTcp = 5
CtaProtoinfoTcpMax CtattrProtoinfoTcp = 6
)
type NfConntrackAttrGrp int
const (
AttrGrpOrigIpv4 NfConntrackAttrGrp = 0
AttrGrpReplIpv4 NfConntrackAttrGrp = 1
AttrGrpOrigIpv6 NfConntrackAttrGrp = 2
AttrGrpReplIpv6 NfConntrackAttrGrp = 3
AttrGrpOrigPort NfConntrackAttrGrp = 4
AttrGrpReplPort NfConntrackAttrGrp = 5
AttrGrpIcmp NfConntrackAttrGrp = 6
AttrGrpMasterIpv4 NfConntrackAttrGrp = 7
AttrGrpMasterIpv6 NfConntrackAttrGrp = 8
AttrGrpMasterPort NfConntrackAttrGrp = 9
AttrGrpOrigCounters NfConntrackAttrGrp = 10
AttrGrpReplCounters NfConntrackAttrGrp = 11
AttrGrpOrigAddrSrc NfConntrackAttrGrp = 12
AttrGrpOrigAddrDst NfConntrackAttrGrp = 13
AttrGrpReplAddrSrc NfConntrackAttrGrp = 14
AttrGrpReplAddrDst NfConntrackAttrGrp = 15
AttrGrpMax NfConntrackAttrGrp = 16
)
type NfConntrackQuery int
const (
NfctQCreate NfConntrackQuery = 0
NfctQUpdate NfConntrackQuery = 1
NfctQDestroy NfConntrackQuery = 2
NfctQGet NfConntrackQuery = 3
NfctQFlush NfConntrackQuery = 4
NfctQDump NfConntrackQuery = 5
NfctQDumpReset NfConntrackQuery = 6
NfctQCreateUpdate NfConntrackQuery = 7
NfctQDumpFilter NfConntrackQuery = 8
NfctQDumpFilterReset NfConntrackQuery = 9
)
package conntrack
import (
"errors"
"syscall"
)
// NFNL_MSG_TYPE
func nflnMsgType(x uint16) uint8 {
return uint8(x & 0x00ff)
}
// NFNL_SUBSYS_ID
func nflnSubsysID(x uint16) uint8 {
return uint8((x & 0xff00) >> 8)
}
// from src/libnfnetlink.c
func nfnlIsError(hdr syscall.NlMsghdr) error {
if hdr.Type == syscall.NLMSG_ERROR {
return errors.New("NLMSG_ERROR")
}
if hdr.Type == syscall.NLMSG_DONE && hdr.Flags&syscall.NLM_F_MULTI > 0 {
return errors.New("Done!")
}
return nil
}
// Round the length of a netlink route attribute up to align it
// properly.
func rtaAlignOf(attrlen int) int {
return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1)
}
package conntrack
import (
"encoding/binary"
"errors"
)
const attrHdrLength = 4
type Attr struct {
Msg []byte
Typ int
IsNested bool
IsNetByteorder bool
}
func parseAttrs(b []byte) ([]Attr, error) {
var attrs []Attr
for len(b) >= attrHdrLength {
var attr Attr
attr, b = parseAttr(b)
attrs = append(attrs, attr)
}
if len(b) != 0 {
return nil, errors.New("leftover attr bytes")
}
return attrs, nil
}
func parseAttr(b []byte) (Attr, []byte) {
l := binary.LittleEndian.Uint16(b[0:2])
// length is header + payload
l -= uint16(attrHdrLength)
typ := binary.LittleEndian.Uint16(b[2:4])
attr := Attr{
Msg: b[attrHdrLength : attrHdrLength+int(l)],
Typ: int(typ & NLA_TYPE_MASK),
IsNested: typ&NLA_F_NESTED > 0,
IsNetByteorder: typ&NLA_F_NET_BYTEORDER > 0,
}
return attr, b[rtaAlignOf(attrHdrLength+int(l)):]
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment