conntrack_linux.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. package netlink
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "errors"
  6. "fmt"
  7. "net"
  8. "github.com/vishvananda/netlink/nl"
  9. "golang.org/x/sys/unix"
  10. )
  11. // ConntrackTableType Conntrack table for the netlink operation
  12. type ConntrackTableType uint8
  13. const (
  14. // ConntrackTable Conntrack table
  15. // https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink.h -> #define NFNL_SUBSYS_CTNETLINK 1
  16. ConntrackTable = 1
  17. // ConntrackExpectTable Conntrack expect table
  18. // https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink.h -> #define NFNL_SUBSYS_CTNETLINK_EXP 2
  19. ConntrackExpectTable = 2
  20. )
  21. const (
  22. // For Parsing Mark
  23. TCP_PROTO = 6
  24. UDP_PROTO = 17
  25. )
  26. const (
  27. // backward compatibility with golang 1.6 which does not have io.SeekCurrent
  28. seekCurrent = 1
  29. )
  30. // InetFamily Family type
  31. type InetFamily uint8
  32. // -L [table] [options] List conntrack or expectation table
  33. // -G [table] parameters Get conntrack or expectation
  34. // -I [table] parameters Create a conntrack or expectation
  35. // -U [table] parameters Update a conntrack
  36. // -E [table] [options] Show events
  37. // -C [table] Show counter
  38. // -S Show statistics
  39. // ConntrackTableList returns the flow list of a table of a specific family
  40. // conntrack -L [table] [options] List conntrack or expectation table
  41. func ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {
  42. return pkgHandle.ConntrackTableList(table, family)
  43. }
  44. // ConntrackTableFlush flushes all the flows of a specified table
  45. // conntrack -F [table] Flush table
  46. // The flush operation applies to all the family types
  47. func ConntrackTableFlush(table ConntrackTableType) error {
  48. return pkgHandle.ConntrackTableFlush(table)
  49. }
  50. // ConntrackDeleteFilter deletes entries on the specified table on the base of the filter
  51. // conntrack -D [table] parameters Delete conntrack or expectation
  52. func ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter CustomConntrackFilter) (uint, error) {
  53. return pkgHandle.ConntrackDeleteFilter(table, family, filter)
  54. }
  55. // ConntrackTableList returns the flow list of a table of a specific family using the netlink handle passed
  56. // conntrack -L [table] [options] List conntrack or expectation table
  57. func (h *Handle) ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {
  58. res, err := h.dumpConntrackTable(table, family)
  59. if err != nil {
  60. return nil, err
  61. }
  62. // Deserialize all the flows
  63. var result []*ConntrackFlow
  64. for _, dataRaw := range res {
  65. result = append(result, parseRawData(dataRaw))
  66. }
  67. return result, nil
  68. }
  69. // ConntrackTableFlush flushes all the flows of a specified table using the netlink handle passed
  70. // conntrack -F [table] Flush table
  71. // The flush operation applies to all the family types
  72. func (h *Handle) ConntrackTableFlush(table ConntrackTableType) error {
  73. req := h.newConntrackRequest(table, unix.AF_INET, nl.IPCTNL_MSG_CT_DELETE, unix.NLM_F_ACK)
  74. _, err := req.Execute(unix.NETLINK_NETFILTER, 0)
  75. return err
  76. }
  77. // ConntrackDeleteFilter deletes entries on the specified table on the base of the filter using the netlink handle passed
  78. // conntrack -D [table] parameters Delete conntrack or expectation
  79. func (h *Handle) ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter CustomConntrackFilter) (uint, error) {
  80. res, err := h.dumpConntrackTable(table, family)
  81. if err != nil {
  82. return 0, err
  83. }
  84. var matched uint
  85. for _, dataRaw := range res {
  86. flow := parseRawData(dataRaw)
  87. if match := filter.MatchConntrackFlow(flow); match {
  88. req2 := h.newConntrackRequest(table, family, nl.IPCTNL_MSG_CT_DELETE, unix.NLM_F_ACK)
  89. // skip the first 4 byte that are the netfilter header, the newConntrackRequest is adding it already
  90. req2.AddRawData(dataRaw[4:])
  91. req2.Execute(unix.NETLINK_NETFILTER, 0)
  92. matched++
  93. }
  94. }
  95. return matched, nil
  96. }
  97. func (h *Handle) newConntrackRequest(table ConntrackTableType, family InetFamily, operation, flags int) *nl.NetlinkRequest {
  98. // Create the Netlink request object
  99. req := h.newNetlinkRequest((int(table)<<8)|operation, flags)
  100. // Add the netfilter header
  101. msg := &nl.Nfgenmsg{
  102. NfgenFamily: uint8(family),
  103. Version: nl.NFNETLINK_V0,
  104. ResId: 0,
  105. }
  106. req.AddData(msg)
  107. return req
  108. }
  109. func (h *Handle) dumpConntrackTable(table ConntrackTableType, family InetFamily) ([][]byte, error) {
  110. req := h.newConntrackRequest(table, family, nl.IPCTNL_MSG_CT_GET, unix.NLM_F_DUMP)
  111. return req.Execute(unix.NETLINK_NETFILTER, 0)
  112. }
  113. // The full conntrack flow structure is very complicated and can be found in the file:
  114. // http://git.netfilter.org/libnetfilter_conntrack/tree/include/internal/object.h
  115. // For the time being, the structure below allows to parse and extract the base information of a flow
  116. type ipTuple struct {
  117. Bytes uint64
  118. DstIP net.IP
  119. DstPort uint16
  120. Packets uint64
  121. Protocol uint8
  122. SrcIP net.IP
  123. SrcPort uint16
  124. }
  125. type ConntrackFlow struct {
  126. FamilyType uint8
  127. Forward ipTuple
  128. Reverse ipTuple
  129. Mark uint32
  130. }
  131. func (s *ConntrackFlow) String() string {
  132. // conntrack cmd output:
  133. // 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
  134. 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",
  135. nl.L4ProtoMap[s.Forward.Protocol], s.Forward.Protocol,
  136. s.Forward.SrcIP.String(), s.Forward.DstIP.String(), s.Forward.SrcPort, s.Forward.DstPort, s.Forward.Packets, s.Forward.Bytes,
  137. s.Reverse.SrcIP.String(), s.Reverse.DstIP.String(), s.Reverse.SrcPort, s.Reverse.DstPort, s.Reverse.Packets, s.Reverse.Bytes,
  138. s.Mark)
  139. }
  140. // This method parse the ip tuple structure
  141. // The message structure is the following:
  142. // <len, [CTA_IP_V4_SRC|CTA_IP_V6_SRC], 16 bytes for the IP>
  143. // <len, [CTA_IP_V4_DST|CTA_IP_V6_DST], 16 bytes for the IP>
  144. // <len, NLA_F_NESTED|nl.CTA_TUPLE_PROTO, 1 byte for the protocol, 3 bytes of padding>
  145. // <len, CTA_PROTO_SRC_PORT, 2 bytes for the source port, 2 bytes of padding>
  146. // <len, CTA_PROTO_DST_PORT, 2 bytes for the source port, 2 bytes of padding>
  147. func parseIpTuple(reader *bytes.Reader, tpl *ipTuple) uint8 {
  148. for i := 0; i < 2; i++ {
  149. _, t, _, v := parseNfAttrTLV(reader)
  150. switch t {
  151. case nl.CTA_IP_V4_SRC, nl.CTA_IP_V6_SRC:
  152. tpl.SrcIP = v
  153. case nl.CTA_IP_V4_DST, nl.CTA_IP_V6_DST:
  154. tpl.DstIP = v
  155. }
  156. }
  157. // Skip the next 4 bytes nl.NLA_F_NESTED|nl.CTA_TUPLE_PROTO
  158. reader.Seek(4, seekCurrent)
  159. _, t, _, v := parseNfAttrTLV(reader)
  160. if t == nl.CTA_PROTO_NUM {
  161. tpl.Protocol = uint8(v[0])
  162. }
  163. // Skip some padding 3 bytes
  164. reader.Seek(3, seekCurrent)
  165. for i := 0; i < 2; i++ {
  166. _, t, _ := parseNfAttrTL(reader)
  167. switch t {
  168. case nl.CTA_PROTO_SRC_PORT:
  169. parseBERaw16(reader, &tpl.SrcPort)
  170. case nl.CTA_PROTO_DST_PORT:
  171. parseBERaw16(reader, &tpl.DstPort)
  172. }
  173. // Skip some padding 2 byte
  174. reader.Seek(2, seekCurrent)
  175. }
  176. return tpl.Protocol
  177. }
  178. func parseNfAttrTLV(r *bytes.Reader) (isNested bool, attrType, len uint16, value []byte) {
  179. isNested, attrType, len = parseNfAttrTL(r)
  180. value = make([]byte, len)
  181. binary.Read(r, binary.BigEndian, &value)
  182. return isNested, attrType, len, value
  183. }
  184. func parseNfAttrTL(r *bytes.Reader) (isNested bool, attrType, len uint16) {
  185. binary.Read(r, nl.NativeEndian(), &len)
  186. len -= nl.SizeofNfattr
  187. binary.Read(r, nl.NativeEndian(), &attrType)
  188. isNested = (attrType & nl.NLA_F_NESTED) == nl.NLA_F_NESTED
  189. attrType = attrType & (nl.NLA_F_NESTED - 1)
  190. return isNested, attrType, len
  191. }
  192. func parseBERaw16(r *bytes.Reader, v *uint16) {
  193. binary.Read(r, binary.BigEndian, v)
  194. }
  195. func parseBERaw64(r *bytes.Reader, v *uint64) {
  196. binary.Read(r, binary.BigEndian, v)
  197. }
  198. func parseByteAndPacketCounters(r *bytes.Reader) (bytes, packets uint64) {
  199. for i := 0; i < 2; i++ {
  200. switch _, t, _ := parseNfAttrTL(r); t {
  201. case nl.CTA_COUNTERS_BYTES:
  202. parseBERaw64(r, &bytes)
  203. case nl.CTA_COUNTERS_PACKETS:
  204. parseBERaw64(r, &packets)
  205. default:
  206. return
  207. }
  208. }
  209. return
  210. }
  211. func parseRawData(data []byte) *ConntrackFlow {
  212. s := &ConntrackFlow{}
  213. var proto uint8
  214. // First there is the Nfgenmsg header
  215. // consume only the family field
  216. reader := bytes.NewReader(data)
  217. binary.Read(reader, nl.NativeEndian(), &s.FamilyType)
  218. // skip rest of the Netfilter header
  219. reader.Seek(3, seekCurrent)
  220. // The message structure is the following:
  221. // <len, NLA_F_NESTED|CTA_TUPLE_ORIG> 4 bytes
  222. // <len, NLA_F_NESTED|CTA_TUPLE_IP> 4 bytes
  223. // flow information of the forward flow
  224. // <len, NLA_F_NESTED|CTA_TUPLE_REPLY> 4 bytes
  225. // <len, NLA_F_NESTED|CTA_TUPLE_IP> 4 bytes
  226. // flow information of the reverse flow
  227. for reader.Len() > 0 {
  228. if nested, t, l := parseNfAttrTL(reader); nested {
  229. switch t {
  230. case nl.CTA_TUPLE_ORIG:
  231. if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP {
  232. proto = parseIpTuple(reader, &s.Forward)
  233. }
  234. case nl.CTA_TUPLE_REPLY:
  235. if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP {
  236. parseIpTuple(reader, &s.Reverse)
  237. } else {
  238. // Header not recognized skip it
  239. reader.Seek(int64(l), seekCurrent)
  240. }
  241. case nl.CTA_COUNTERS_ORIG:
  242. s.Forward.Bytes, s.Forward.Packets = parseByteAndPacketCounters(reader)
  243. case nl.CTA_COUNTERS_REPLY:
  244. s.Reverse.Bytes, s.Reverse.Packets = parseByteAndPacketCounters(reader)
  245. }
  246. }
  247. }
  248. if proto == TCP_PROTO {
  249. reader.Seek(64, seekCurrent)
  250. _, t, _, v := parseNfAttrTLV(reader)
  251. if t == nl.CTA_MARK {
  252. s.Mark = uint32(v[3])
  253. }
  254. } else if proto == UDP_PROTO {
  255. reader.Seek(16, seekCurrent)
  256. _, t, _, v := parseNfAttrTLV(reader)
  257. if t == nl.CTA_MARK {
  258. s.Mark = uint32(v[3])
  259. }
  260. }
  261. return s
  262. }
  263. // Conntrack parameters and options:
  264. // -n, --src-nat ip source NAT ip
  265. // -g, --dst-nat ip destination NAT ip
  266. // -j, --any-nat ip source or destination NAT ip
  267. // -m, --mark mark Set mark
  268. // -c, --secmark secmark Set selinux secmark
  269. // -e, --event-mask eventmask Event mask, eg. NEW,DESTROY
  270. // -z, --zero Zero counters while listing
  271. // -o, --output type[,...] Output format, eg. xml
  272. // -l, --label label[,...] conntrack labels
  273. // Common parameters and options:
  274. // -s, --src, --orig-src ip Source address from original direction
  275. // -d, --dst, --orig-dst ip Destination address from original direction
  276. // -r, --reply-src ip Source address from reply direction
  277. // -q, --reply-dst ip Destination address from reply direction
  278. // -p, --protonum proto Layer 4 Protocol, eg. 'tcp'
  279. // -f, --family proto Layer 3 Protocol, eg. 'ipv6'
  280. // -t, --timeout timeout Set timeout
  281. // -u, --status status Set status, eg. ASSURED
  282. // -w, --zone value Set conntrack zone
  283. // --orig-zone value Set zone for original direction
  284. // --reply-zone value Set zone for reply direction
  285. // -b, --buffer-size Netlink socket buffer size
  286. // --mask-src ip Source mask address
  287. // --mask-dst ip Destination mask address
  288. // Filter types
  289. type ConntrackFilterType uint8
  290. const (
  291. ConntrackOrigSrcIP = iota // -orig-src ip Source address from original direction
  292. ConntrackOrigDstIP // -orig-dst ip Destination address from original direction
  293. ConntrackReplySrcIP // --reply-src ip Reply Source IP
  294. ConntrackReplyDstIP // --reply-dst ip Reply Destination IP
  295. ConntrackReplyAnyIP // Match source or destination reply IP
  296. ConntrackNatSrcIP = ConntrackReplySrcIP // deprecated use instead ConntrackReplySrcIP
  297. ConntrackNatDstIP = ConntrackReplyDstIP // deprecated use instead ConntrackReplyDstIP
  298. ConntrackNatAnyIP = ConntrackReplyAnyIP // deprecated use instaed ConntrackReplyAnyIP
  299. )
  300. type CustomConntrackFilter interface {
  301. // MatchConntrackFlow applies the filter to the flow and returns true if the flow matches
  302. // the filter or false otherwise
  303. MatchConntrackFlow(flow *ConntrackFlow) bool
  304. }
  305. type ConntrackFilter struct {
  306. ipFilter map[ConntrackFilterType]net.IP
  307. }
  308. // AddIP adds an IP to the conntrack filter
  309. func (f *ConntrackFilter) AddIP(tp ConntrackFilterType, ip net.IP) error {
  310. if f.ipFilter == nil {
  311. f.ipFilter = make(map[ConntrackFilterType]net.IP)
  312. }
  313. if _, ok := f.ipFilter[tp]; ok {
  314. return errors.New("Filter attribute already present")
  315. }
  316. f.ipFilter[tp] = ip
  317. return nil
  318. }
  319. // MatchConntrackFlow applies the filter to the flow and returns true if the flow matches the filter
  320. // false otherwise
  321. func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool {
  322. if len(f.ipFilter) == 0 {
  323. // empty filter always not match
  324. return false
  325. }
  326. match := true
  327. // -orig-src ip Source address from original direction
  328. if elem, found := f.ipFilter[ConntrackOrigSrcIP]; found {
  329. match = match && elem.Equal(flow.Forward.SrcIP)
  330. }
  331. // -orig-dst ip Destination address from original direction
  332. if elem, found := f.ipFilter[ConntrackOrigDstIP]; match && found {
  333. match = match && elem.Equal(flow.Forward.DstIP)
  334. }
  335. // -src-nat ip Source NAT ip
  336. if elem, found := f.ipFilter[ConntrackReplySrcIP]; match && found {
  337. match = match && elem.Equal(flow.Reverse.SrcIP)
  338. }
  339. // -dst-nat ip Destination NAT ip
  340. if elem, found := f.ipFilter[ConntrackReplyDstIP]; match && found {
  341. match = match && elem.Equal(flow.Reverse.DstIP)
  342. }
  343. // Match source or destination reply IP
  344. if elem, found := f.ipFilter[ConntrackReplyAnyIP]; match && found {
  345. match = match && (elem.Equal(flow.Reverse.SrcIP) || elem.Equal(flow.Reverse.DstIP))
  346. }
  347. return match
  348. }
  349. var _ CustomConntrackFilter = (*ConntrackFilter)(nil)