conntrack_linux.go 16 KB

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