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

ALG 在 nf_conntrack的实现 #1

Open
jursonmo opened this issue Jan 29, 2019 · 0 comments
Open

ALG 在 nf_conntrack的实现 #1

jursonmo opened this issue Jan 29, 2019 · 0 comments

Comments

@jursonmo
Copy link
Owner

jursonmo commented Jan 29, 2019

ALG implementation in nf_conntrack

nf_conntrack, ftp :

ct , , 对于ftp, 被动模式下,是通过控制连接来告诉对方自己开放哪个ip和端口,由于经过nat 设备, nat helper 功能模块必须修改控制连接里的通知消息(也就是控制连接 的每个数据在postrouting ipv4_helper 点都会调用 ftp help 回调检查是否是通告ip 端口的消息,是就修改这个消息的内容,并且创建exp),本来是想告诉对方来连接“192.168.100.2:3333”,现在改成nat 出口ip和端口,比如“223.2.2.1:123”,这是修改控制连接的数据部分,所以控制连接的tcp 序列号及校验码也要改(在postrouting confirm 修改tcp 序列号及校验码),否则对方受无法正常接收这个数据。而且需要nat 设备开放某个ip和端口来让对方来连接(其实不用开放,创建exp 有相关连接信息就防火墙就接受对方的数据连接), 即netfilter 需要创建一个nf_conntrack_expect exp放在expect_hash表里, 记录这个数据连接信息,包括更改后的端口,就是为了未来有个连接能匹配到,这样对方发起新的连接时,即创建新连接是就可以通过查找expect 表匹配到这个nf_conntrack_expect exp。这个新连接即数据连接ct -->master = exp->master , 这样数据连接ct 就跟控制连接的ct 关联起来了。数据连接ct 是根据master 的nat 信息来做snat 或dnat 的修改。只有 IP_CT_NEW 状态才匹配iptables snat规则,IP_CT_RELATED 状态是不会去匹配的, 期望连接一个数据就是IP_CT_RELATED 状态, 期望连接 是根据master nat 信息来做数据的修改。
// nat 修改的是三四层的东西, help()是修改应用层上的数据,并创建expect 表项,设置序列号调整参数nf_ct_seqadj_set, 最后在confirm 再根据序列号调整参数真正修改skb tcp层的序列号nf_ct_seq_adjust。
创建新连接时nfct_seqadj_ext_add--> help()需要修改应用层上的数据时 nf_ct_seqadj_set--> 到POSTROUTING ipv4_confirm 时 nf_ct_seq_adjust

对于nat 修改:

SNAT :

static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = {
	{
              .hook		= iptable_nat_ipv4_out,
               .pf		= NFPROTO_IPV4,
               .priority	= NF_IP_PRI_NAT_SRC,
	},
iptable_nat_ipv4_out  --> nf_nat_ipv4_out()  --> nf_nat_ipv4_fn
-->  nf_nat_packet -->
if (!l3proto->manip_pkt(skb, 0, l4proto, &target, mtype))
			return NF_DROP;
nf_nat_l3proto_ipv4. manip_pkt = nf_nat_ipv4_manip_pkt;

 nf_nat_ipv4_manip_pkt():  //三层修改报文的回调函数也会去调用四层的回调函数
if (!l4proto->manip_pkt(skb, &nf_nat_l3proto_ipv4, iphdroff, hdroff,
				target, maniptype))

nf_nat_l3proto_registernf_nat_l3proto_ipv4 ):
	RCU_INIT_POINTER(nf_nat_l4protos[l3proto->l3proto][IPPROTO_TCP],
			 &nf_nat_l4proto_tcp);
	RCU_INIT_POINTER(nf_nat_l4protos[l3proto->l3proto][IPPROTO_UDP],
			 &nf_nat_l4proto_udp);
RCU_INIT_POINTER(nf_nat_l3protos[l3proto->l3proto], l3proto);
在注册nf_nat_l3proto_ipv4 三层时就默认注册四层的tcp 和udp  

DNAT :

static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = {
	/* Before packet filtering, change destination */
	{
             .hook		= iptable_nat_ipv4_in,
             .owner		= THIS_MODULE,
             .pf		= NFPROTO_IPV4,
              .hooknum	= NF_INET_PRE_ROUTING,
               .priority	= NF_IP_PRI_NAT_DST,
	},

iptable_nat_ipv4_in-->nf_nat_packet-->l3proto->manip_pkt(skb, 0, l4proto, &target, mtype) 修改pkt
如何确定dnat 后,路由之前,数据报文真的修改了目的地址

Netfilter中NAT的TARGET实现:
nf_nat_rule_find–>ipt_do_tables (iptable_nat_do_chain)处理snat rules. ???????

iptables .... -j SNAT , 就会匹配到 xt_target

static struct xt_target xt_nat_target_reg[] __read_mostly = {
	{
		.name		= "SNAT",
		.revision	= 0,
		.checkentry	= xt_nat_checkentry_v0,
		.target		= xt_snat_target_v0,
		.targetsize	= sizeof(struct nf_nat_ipv4_multi_range_compat),
		.family		= NFPROTO_IPV4,
		.table		= "nat",
		.hooks		= (1 << NF_INET_POST_ROUTING) |
				  (1 << NF_INET_LOCAL_IN),                   //-j SNAT 使用的链表范围
		.me		= THIS_MODULE,
	},
snat 扩展target的参数定义:
struct nf_nat_ipv4_multi_range_compat {
	unsigned int			rangesize;
	struct nf_nat_ipv4_range	range[1];
};

经过nat 设备--》 xt_snat_target_v0 --》

nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC);
      nat = nf_ct_nat_ext_add(ct);
struct nf_conn_nat *nat;
nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); //nat 功能模块

if (nfct_help(ct))  ???????需要help 
	nfct_seqadj_ext_add(ct);  // 添加序列号调整功能模块

  1. nf conntrack ftp 初始化:
static struct nf_conntrack_helper ftp[MAX_PORTS][2] __read_mostly;
nf_conntrack_ftp_init():
ftp[i][j].tuple.src.u.tcp.port = htons(ports[i]);   FTP_PORT 21
			ftp[i][j].tuple.dst.protonum = IPPROTO_TCP;
			ftp[i][j].expect_policy = &ftp_exp_policy;
			ftp[i][j].me = THIS_MODULE;
			ftp[i][j].help = help;
ret = nf_conntrack_helper_register(&ftp[i][j]);

把ftp nf_conntrack_helper 注册到 nf_ct_helper_hash 表里

  1. init_conntrack() 创建一个新的连接:
if (!exp) {
		__nf_ct_try_assign_helper(ct, tmpl, GFP_ATOMIC);
		NF_CT_STAT_INC(net, new);
	}
__nf_ct_try_assign_helper:
-->  helper = __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
help = nf_ct_helper_ext_add(ct, helper, flags); // struct nf_conn_help *help
rcu_assign_pointer(help->helper, helper);

以上就是给一个新建的ct 加入help 、helper, helper.help()回调函数什么时候调用呢????

  1. ipv4_helper 修改连接的内容
static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = {
	{
		.hook		= ipv4_helper,
		.owner		= THIS_MODULE,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_POST_ROUTING,
		.priority	= NF_IP_PRI_CONNTRACK_HELPER, 
    //优先级仅比NF_IP_PRI_CONNTRACK_CONFIRM高,即到最后才执行help回调函数
// nat 修改的是三四层的东西, help()是修改应用层上的数据,并创建expect 表项,设置序列号调整参数, 最后在confirm 再根据序列号调整参数真正修改skb tcp层的序列号。
	},
}

image
4. ipv4_confirm:离开netfilter 模块的时候

//判断是否做了序列号调整IPS_SEQ_ADJUST_BIT
if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) &&
	    !nf_is_loopback_packet(skb)) {
		if (!nf_ct_seq_adjust(skb, ct, ctinfo, ip_hdrlen(skb))) {
			NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop);
			return NF_DROP;
		}
	}

nf_ct_seq_adjust 才是真正修改报文的序列号地方


nf_conntrack_ftp_init ---》注册 ftp[i][j] 到 nf_ct_helper_hash, 控制连接创建时init_conntrack--》__nf_ct_try_assign_helper 尝试从nf_ct_helper_hash找到一个helper, 找到后nf_ct_helper_ext_add, 到了POSTROUTING 点---》 ipv4_helper ()
ipv4_helper ():
--》 help = nfct_help(ct);
return helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb),
ct, ctinfo); //这里就是执行 helper->help 回调函数了
image
比如 ftp helper. help():
ftp[i][j].help = help;
nf_conntrack_ftp.c -->help():
exp = nf_ct_expect_alloc(ct); //expect 表项
ret = nf_nat_ftp(skb, ctinfo, search[dir][i].ftptype,
protoff, matchoff, matchlen, exp);
struct nf_conn *ct = exp->master;
// 给exp 查找一个合适的端口,尽量保持端口不变
buflen = nf_nat_ftp_fmt_cmd(ct, type, buffer, sizeof(buffer),
&newaddr, port); //修改控制连接数据里的ftp命令
if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, matchoff,
matchlen, buffer, buflen))
---》__nf_nat_mangle_tcp_packet(..., true) // 修改数据且调整tcp序列号
SKB_LINEAR_ASSERT(skb); //检查是否线性化
// 修改payload内容

mangle_contents(skb, protoff + tcph->doff*4,
			match_offset, match_len, rep_buffer, rep_len);
        if (adjust && rep_len != match_len)
	nf_ct_seqadj_set(ct, ctinfo, tcph->seq,
		 (int)rep_len - (int)match_len);//只是设置序列号调整信息

真正修改skb的操作是在另一地方,就是离开netfilter 的时候
ipv4_confirm:

========================================================================
struct nf_conn, struct nf_conn_ext, struct nf_conn_help 及 struct nf_conntrack_helper 之间的结构关系。
image
ext->offset[ nf_ct_ext_id ] , 保存nf_ct_ext_id 内存对应于ext 结构体的偏移值
nf_nat_setup_info--》nat = nf_ct_nat_ext_add(ct);--》struct nf_conn_nat *nat =nfct_nat()-》
return nf_ct_ext_find(ct, NF_CT_EXT_NAT);到ct-->ext 去找id:NF_CT_EXT_NAT对应的信息
这个信息是 struct nf_conn_nat 结构体:

struct nf_conn_nat {
	struct hlist_node bysource;  //这个的用处是,为以后打洞用的,实际就
	struct nf_conn *ct;
	union nf_conntrack_nat_help help;
#if IS_ENABLED(CONFIG_NF_NAT_MASQUERADE_IPV4) || \
    IS_ENABLED(CONFIG_NF_NAT_MASQUERADE_IPV6)
	int masq_index;
#endif
};

ext_id 有哪些, NF_CT_EXT_NUM 代表最大值:

enum nf_ct_ext_id {
	NF_CT_EXT_HELPER,
#if defined(CONFIG_NF_NAT) || defined(CONFIG_NF_NAT_MODULE)
	NF_CT_EXT_NAT,
#endif
	NF_CT_EXT_SEQADJ,
	NF_CT_EXT_ACCT,
#ifdef CONFIG_NF_CONNTRACK_EVENTS
	NF_CT_EXT_ECACHE,
#endif
#ifdef CONFIG_NF_CONNTRACK_ZONES
	NF_CT_EXT_ZONE,
#endif
#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
	NF_CT_EXT_TSTAMP,
#endif
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
	NF_CT_EXT_TIMEOUT,
#endif
#ifdef CONFIG_NF_CONNTRACK_LABELS
	NF_CT_EXT_LABELS,
#endif
#if IS_ENABLED(CONFIG_NETFILTER_SYNPROXY)
	NF_CT_EXT_SYNPROXY,
#endif
	NF_CT_EXT_NUM,
};
@jursonmo jursonmo changed the title ALG implementation in nf_conntrack ALG 在 nf_conntrack的实现 Jan 29, 2019
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