Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

连接跟踪的一种非常暴力的获取方式 #5

Open
ICKelin opened this issue Feb 2, 2018 · 0 comments
Open

连接跟踪的一种非常暴力的获取方式 #5

ICKelin opened this issue Feb 2, 2018 · 0 comments

Comments

@ICKelin
Copy link
Owner

ICKelin commented Feb 2, 2018

在项目当中有时需要连接跟踪相关的信息,netfilter官网提供了一个libnetfilter_conntrack的用户态库,通过netlink的方式与内核对应的模块通信操作conntrack,但是会有性能瓶颈,还有就是问题不好定位,本文提供一种用户层获取连接跟踪的实现思路,在继续阅读之前,请确保具备以下基本知识:

  • 了解netfilter
  • 能够开发基于netfilter的简单的包过滤防火墙
  • 对skb有一定的了解

解决思路很清晰,在内核层面netfilter钩子当中hook数据包,然后强行在L4和payload之间封装一层协议,有点类似运营商植入广告,将连接跟踪的信息通过这层协议传递给用户态,用户态先解这层协议。再读取数据。

比如说,用户层比较关心DNAT之前的目的地址(origin_ip)和目的端口(origin_port),那么可以将origin_ip和origin_port再加上payload的长度三个字段作为一层协议,插入到tcp和payload之间。这样用户层的应用只需要正常读数据,读取协议头,再读取payload即可。

以TCP为例,部分示例代码如下:

// tcp
static int handle_tcp(struct sk_buff *skb){
	struct iphdr *ip = ip_hdr(skb);
	struct tcphdr *tcp = tcp_hdr(skb);
	
	// conntrack
	enum ip_conntrack_info conntrack_info;	
	struct nf_conntrack_tuple *origin_tuple = NULL;
	struct nf_conn *ct = NULL;

	// inject buffer
	char insert_data[8];
	char *ptr = &insert_data[0];

	// origin payload
	char *origin_data = skb_network_header(skb) + ip->ihl * 4 + tcp->doff * 4;
	int origin_len = skb->tail - skb->network_header - ip->ihl * 4 - tcp->doff * 4;

	ct = get_conntrack(skb, &conntrack_info);
	if (ct == NULL) {
		return NF_ACCEPT;
	}

	origin_tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;

	// force accepted pkt
	if (is_accepted_pkt(origin_tuple, ip->daddr, tcp->dest) != 0) {
		return NF_ACCEPT;
	}

	// empty payload
	if (origin_len == 0) {
		return NF_ACCEPT;
	}

	// modify tcp payload
	ptr = encode32u(ptr, origin_tuple->dst.u3.ip);
	ptr = encode16u(ptr, ntohs(origin_tuple->dst.u.all));
	ptr = encode16u(ptr, origin_len);


	// expand skb tailroom
	if (skb_tailroom(skb) < 8) {
		if (expand_skb(skb, skb_headroom(skb), skb_tailroom(skb) + 40) != 0) {
			return NF_DROP;	
		}
	}

	// copy conntrack data into skb payload 
	memmove(origin_data + 8, origin_data, origin_len);
	memcpy(origin_data, &insert_data[0], 8);

	nfct_seqadj_ext_add(ct);
	__nf_nat_mangle_tcp_packet(skb, ct, conntrack_info, 
			ip->ihl * 4, 0, origin_len, 
			origin_data, origin_len + 8, true);

	return NF_ACCEPT;
}

在用户态先读八个字节的协议,将origin_ip和origin_port以及payload长度length读取出来,然后再读取length字节的数据。不需要任何库,难点在于编写一个稳定的内核模块。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant