Linux下如何在应用层获取连接跟踪信息
一,办法1
写个内核模块,吧啦吧啦,这个看上去比较容易,但缺陷是:
1,如果要支持的系统环境比较复杂,比如有Ubuntu、CentOS、Fedora等不同的发行版,各个发行版还有不同的版本如Ubuntu12.04、Ubuntu14.04等,那么维护的工作量非常巨大。
2,内核代码一出错就宕机,风险极大。
二,办法2
采用pcap将数据包抓到应用层,分析数据包来进行连接跟踪,缺陷是包分析的工作量极大,抓包的对系统性能的极大损耗,而且万一有漏包,会导致连接跟踪信息不准确。
因此比较好的方式是直接利用系统自带的接口来获取,而Linux系统的nf_conntrack模块就提供了这个接口:
[root@localhost ~]# cat /proc/net/nf_conntrack ipv4 2 tcp 6 431945 ESTABLISHED src=192.168.137.1 dst=192.168.137.131 sport=23474 dport=22 src=192.168.137.131 dst=192.168.137.1 sport=22 dport=23474 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 zone=0 use=2 ipv4 2 tcp 6 299 ESTABLISHED src=192.168.137.1 dst=192.168.137.131 sport=21744 dport=22 src=192.168.137.131 dst=192.168.137.1 sport=22 dport=21744 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 zone=0 use=2 ipv4 2 tcp 6 429995 ESTABLISHED src=192.168.137.1 dst=192.168.137.131 sport=35167 dport=22 src=192.168.137.131 dst=192.168.137.1 sport=22 dport=35167 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 zone=0 use=2
上面接口是在CentOS 7.2里看到的,在比较老的CentOS系统上,可能是/proc/net/ip_conntrack,而在Ubuntu上,可能这两个接口都没有,原因是这个接口被摒弃,需要采用conntrack命令来查看:
[root@localhost ~]# conntrack -L tcp 6 431970 ESTABLISHED src=192.168.137.1 dst=192.168.137.131 sport=23474 dport=22 src=192.168.137.131 dst=192.168.137.1 sport=22 dport=23474 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1 tcp 6 299 ESTABLISHED src=192.168.137.1 dst=192.168.137.131 sport=21744 dport=22 src=192.168.137.131 dst=192.168.137.1 sport=22 dport=21744 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1 tcp 6 431224 ESTABLISHED src=192.168.137.1 dst=192.168.137.131 sport=35167 dport=22 src=192.168.137.131 dst=192.168.137.1 sport=22 dport=35167 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1 conntrack v1.4.4 (conntrack-tools): 3 flow entries have been shown.
当然,conntrack命令需要先采用yum install conntrack-tools或sudo apt-get install conntrack进行安装。
如果程序要想获取原始的连接跟踪信息,怎么办呢?非常好办,直接看看conntrack的源码即可。我通过apt-get source conntrack获取到conntrack的源码,然后把里面的一部分代码挖出来如下:
#include <stdio.h> #include <getopt.h> #include <stdlib.h> #include <stdarg.h> #include <errno.h> #include <unistd.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <time.h> #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif #include <signal.h> #include <string.h> #include <netdb.h> #include <sys/stat.h> #include <fcntl.h> #include <libmnl/libmnl.h> #include <libnetfilter_conntrack/libnetfilter_conntrack.h> static struct nfct_handle *cth, *ith; struct u32_mask { uint32_t value; uint32_t mask; }; /* These are the template objects that are used to send commands. */ static struct { struct nf_conntrack *ct; struct nf_expect *exp; /* Expectations require the expectation tuple and the mask. */ struct nf_conntrack *exptuple, *mask; /* Allows filtering/setting specific bits in the ctmark */ struct u32_mask mark; /* Allow to filter by mark from kernel-space. */ struct nfct_filter_dump_mark filter_mark_kernel; } tmpl; static int counter; static unsigned int options; static unsigned int output_mask; enum ct_options { CT_OPT_ORIG_SRC_BIT = 0, CT_OPT_ORIG_SRC = (1 << CT_OPT_ORIG_SRC_BIT), CT_OPT_ORIG_DST_BIT = 1, CT_OPT_ORIG_DST = (1 << CT_OPT_ORIG_DST_BIT), CT_OPT_ORIG = (CT_OPT_ORIG_SRC | CT_OPT_ORIG_DST), CT_OPT_REPL_SRC_BIT = 2, CT_OPT_REPL_SRC = (1 << CT_OPT_REPL_SRC_BIT), CT_OPT_REPL_DST_BIT = 3, CT_OPT_REPL_DST = (1 << CT_OPT_REPL_DST_BIT), CT_OPT_REPL = (CT_OPT_REPL_SRC | CT_OPT_REPL_DST), CT_OPT_PROTO_BIT = 4, CT_OPT_PROTO = (1 << CT_OPT_PROTO_BIT), CT_OPT_TUPLE_ORIG = (CT_OPT_ORIG | CT_OPT_PROTO), CT_OPT_TUPLE_REPL = (CT_OPT_REPL | CT_OPT_PROTO), CT_OPT_TIMEOUT_BIT = 5, CT_OPT_TIMEOUT = (1 << CT_OPT_TIMEOUT_BIT), CT_OPT_STATUS_BIT = 6, CT_OPT_STATUS = (1 << CT_OPT_STATUS_BIT), CT_OPT_ZERO_BIT = 7, CT_OPT_ZERO = (1 << CT_OPT_ZERO_BIT), CT_OPT_EVENT_MASK_BIT = 8, CT_OPT_EVENT_MASK = (1 << CT_OPT_EVENT_MASK_BIT), CT_OPT_EXP_SRC_BIT = 9, CT_OPT_EXP_SRC = (1 << CT_OPT_EXP_SRC_BIT), CT_OPT_EXP_DST_BIT = 10, CT_OPT_EXP_DST = (1 << CT_OPT_EXP_DST_BIT), CT_OPT_MASK_SRC_BIT = 11, CT_OPT_MASK_SRC = (1 << CT_OPT_MASK_SRC_BIT), CT_OPT_MASK_DST_BIT = 12, CT_OPT_MASK_DST = (1 << CT_OPT_MASK_DST_BIT), CT_OPT_NATRANGE_BIT = 13, CT_OPT_NATRANGE = (1 << CT_OPT_NATRANGE_BIT), CT_OPT_MARK_BIT = 14, CT_OPT_MARK = (1 << CT_OPT_MARK_BIT), CT_OPT_ID_BIT = 15, CT_OPT_ID = (1 << CT_OPT_ID_BIT), CT_OPT_FAMILY_BIT = 16, CT_OPT_FAMILY = (1 << CT_OPT_FAMILY_BIT), CT_OPT_SRC_NAT_BIT = 17, CT_OPT_SRC_NAT = (1 << CT_OPT_SRC_NAT_BIT), CT_OPT_DST_NAT_BIT = 18, CT_OPT_DST_NAT = (1 << CT_OPT_DST_NAT_BIT), CT_OPT_OUTPUT_BIT = 19, CT_OPT_OUTPUT = (1 << CT_OPT_OUTPUT_BIT), CT_OPT_SECMARK_BIT = 20, CT_OPT_SECMARK = (1 << CT_OPT_SECMARK_BIT), CT_OPT_BUFFERSIZE_BIT = 21, CT_OPT_BUFFERSIZE = (1 << CT_OPT_BUFFERSIZE_BIT), CT_OPT_ANY_NAT_BIT = 22, CT_OPT_ANY_NAT = (1 << CT_OPT_ANY_NAT_BIT), CT_OPT_ZONE_BIT = 23, CT_OPT_ZONE = (1 << CT_OPT_ZONE_BIT), }; #define CT_COMPARISON (CT_OPT_PROTO | CT_OPT_ORIG | CT_OPT_REPL | \ CT_OPT_MARK | CT_OPT_SECMARK | CT_OPT_STATUS | \ CT_OPT_ID | CT_OPT_ZONE) enum { _O_XML = (1 << 0), _O_EXT = (1 << 1), _O_TMS = (1 << 2), _O_ID = (1 << 3), _O_KTMS = (1 << 4), }; static int dump_xml_header_done = 1; static int mark_cmp(const struct u32_mask *m, const struct nf_conntrack *ct) { return nfct_attr_is_set(ct, ATTR_MARK) && (nfct_get_attr_u32(ct, ATTR_MARK) & m->mask) == m->value; } static int filter_mark(const struct nf_conntrack *ct) { if ((options & CT_OPT_MARK) && !mark_cmp(&tmpl.mark, ct)) return 1; return 0; } static int filter_nat(const struct nf_conntrack *obj, const struct nf_conntrack *ct) { int check_srcnat = options & CT_OPT_SRC_NAT ? 1 : 0; int check_dstnat = options & CT_OPT_DST_NAT ? 1 : 0; int has_srcnat = 0, has_dstnat = 0; uint32_t ip; uint16_t port; if (options & CT_OPT_ANY_NAT) check_srcnat = check_dstnat = 1; if (check_srcnat) { int check_address = 0, check_port = 0; if (nfct_attr_is_set(obj, ATTR_SNAT_IPV4)) { check_address = 1; ip = nfct_get_attr_u32(obj, ATTR_SNAT_IPV4); if (nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT) && ip == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST)) has_srcnat = 1; } if (nfct_attr_is_set(obj, ATTR_SNAT_PORT)) { int ret = 0; check_port = 1; port = nfct_get_attr_u16(obj, ATTR_SNAT_PORT); if (nfct_getobjopt(ct, NFCT_GOPT_IS_SPAT) && port == nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST)) ret = 1; /* the address matches but the port does not. */ if (check_address && has_srcnat && !ret) has_srcnat = 0; if (!check_address && ret) has_srcnat = 1; } if (!check_address && !check_port && (nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT) || nfct_getobjopt(ct, NFCT_GOPT_IS_SPAT))) has_srcnat = 1; } if (check_dstnat) { int check_address = 0, check_port = 0; if (nfct_attr_is_set(obj, ATTR_DNAT_IPV4)) { check_address = 1; ip = nfct_get_attr_u32(obj, ATTR_DNAT_IPV4); if (nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT) && ip == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_SRC)) has_dstnat = 1; } if (nfct_attr_is_set(obj, ATTR_DNAT_PORT)) { int ret = 0; check_port = 1; port = nfct_get_attr_u16(obj, ATTR_DNAT_PORT); if (nfct_getobjopt(ct, NFCT_GOPT_IS_DPAT) && port == nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC)) ret = 1; /* the address matches but the port does not. */ if (check_address && has_dstnat && !ret) has_dstnat = 0; if (!check_address && ret) has_dstnat = 1; } if (!check_address && !check_port && (nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT) || nfct_getobjopt(ct, NFCT_GOPT_IS_DPAT))) has_dstnat = 1; } if (options & CT_OPT_ANY_NAT) return !(has_srcnat || has_dstnat); else if ((options & CT_OPT_SRC_NAT) && (options & CT_OPT_DST_NAT)) return !(has_srcnat && has_dstnat); else if (options & CT_OPT_SRC_NAT) return !has_srcnat; else if (options & CT_OPT_DST_NAT) return !has_dstnat; return 0; } static int dump_cb(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data) { char buf[1024]; struct nf_conntrack *obj = data; unsigned int op_type = NFCT_O_DEFAULT; unsigned int op_flags = 0; if (filter_nat(obj, ct)) return NFCT_CB_CONTINUE; if (filter_mark(ct)) return NFCT_CB_CONTINUE; if (options & CT_COMPARISON && !nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK)) return NFCT_CB_CONTINUE; if (output_mask & _O_XML) { op_type = NFCT_O_XML; if (dump_xml_header_done) { dump_xml_header_done = 0; printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" "<conntrack>\n"); } } if (output_mask & _O_EXT) op_flags = NFCT_OF_SHOW_LAYER3; if (output_mask & _O_KTMS) op_flags |= NFCT_OF_TIMESTAMP; if (output_mask & _O_ID) op_flags |= NFCT_OF_ID; nfct_snprintf(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags); printf("%s\n", buf); counter++; return NFCT_CB_CONTINUE; } int main() { cth = nfct_open(CONNTRACK, 0); if (!cth) perror("Can't open handler"); nfct_callback_register(cth, NFCT_T_ALL, dump_cb, tmpl.ct); struct nfct_filter_dump *filter_dump; int res; int family = AF_UNSPEC; if (family == AF_UNSPEC) family = AF_INET; filter_dump = nfct_filter_dump_create(); if (filter_dump == NULL) perror("OOM"); nfct_filter_dump_set_attr(filter_dump, NFCT_FILTER_DUMP_MARK, &tmpl.filter_mark_kernel); nfct_filter_dump_set_attr_u8(filter_dump, NFCT_FILTER_DUMP_L3NUM, family); if (options & CT_OPT_ZERO) res = nfct_query(cth, NFCT_Q_DUMP_FILTER_RESET, filter_dump); else res = nfct_query(cth, NFCT_Q_DUMP_FILTER, filter_dump); nfct_filter_dump_destroy(filter_dump); if (dump_xml_header_done == 0) { printf("</conntrack>\n"); fflush(stdout); } nfct_close(cth); return 0; }
上面这段代码完全来之conntrack源码,我只修改了极少部分,以便让它能够正确通过编译。编译这段代码需要先安装两个库,然后再用gcc进行编译:
# yum search libmnl # yum install libmnl-devel # yum search libnetfilter # yum install libnetfilter_conntrack-devel # gcc test2.c -lmnl -lnetfilter_conntrack
执行生成的a.out就能看到对应的连接跟踪信息:
[root@localhost lenky]# ./a.out tcp 6 431967 ESTABLISHED src=192.168.137.1 dst=192.168.137.131 sport=23474 dport=22 src=192.168.137.131 dst=192.168.137.1 sport=22 dport=23474 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1 tcp 6 299 ESTABLISHED src=192.168.137.1 dst=192.168.137.131 sport=21744 dport=22 src=192.168.137.131 dst=192.168.137.1 sport=22 dport=21744 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1 tcp 6 429541 ESTABLISHED src=192.168.137.1 dst=192.168.137.131 sport=35167 dport=22 src=192.168.137.131 dst=192.168.137.1 sport=22 dport=35167 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1
如果没有正常输出,需要排查一下nf_conntrack、nf_conntrack_ipv4内核模块是否没有加载,另外需要以root权限执行程序才能获取到对应的信息。
转载请保留地址:http://www.lenky.info/archives/2018/01/2599 或 http://lenky.info/?p=2599
备注:如无特殊说明,文章内容均出自Lenky个人的真实理解而并非存心妄自揣测来故意愚人耳目。由于个人水平有限,虽力求内容正确无误,但仍然难免出错,请勿见怪,如果可以则请留言告之,并欢迎来信讨论。另外值得说明的是,Lenky的部分文章以及部分内容参考借鉴了网络上各位网友的热心分享,特别是一些带有完全参考的文章,其后附带的链接内容也许更直接、更丰富,而我只是做了一下归纳&转述,在此也一并表示感谢。关于本站的所有技术文章,欢迎转载,但请遵从CC创作共享协议,而一些私人性质较强的心情随笔,建议不要转载。
法律:根据最新颁布的《信息网络传播权保护条例》,如果您认为本文章的任何内容侵犯了您的权利,请以Email或书面等方式告知,本站将及时删除相关内容或链接。