Chion82 / Chion82.github.io

Chion82的未来道具研究所!
blog.chionlab.moe
4 stars 0 forks source link

从DNAT到netfilter内核子系统,浅谈Linux的Full Cone NAT实现 | ChionLab #1

Open Chion82 opened 6 years ago

Chion82 commented 6 years ago

https://blog.chionlab.moe/2018/02/09/full-cone-nat-with-linux/

KalelCooper commented 6 years ago

你好,你为什么会这么说呢 “实现应用于mangle表的hook,对每个流出流入的包进行地址和端口信息修改。相当于手动实现一遍DNAT和SNAT。这种方案可以绕过conntrack对五元组的严格验证,但实现复杂,而且性能较差。”

能不能方便的在内核中找到某个地方,直接处理每个数据包,同时实现简单的NAT和防火墙呢,可以考虑直接换掉iptables nftables 这套东西。如果有好的切入点,比如这样数据 lan 和 wan 之间经过这两个的函数

struct map { uint32_t addr; uint16_t port; uint16_t extport; struct hlist_node _extport; // 对应 input 表 struct hlist_node _intaddr; // 对应 output 表 };

int lan_to_wan(struct packet *pkt) { // 根据 pkt 的 src_addr src_port 查 output 表 // 根据表节点分配 extport // 修改数据包发送出去 }

int wan_to_lan(struct packet *pkt) { // 根据 pkt 的 dst_port 查 input 表 // 根据表节点的信息修改 pkt // 修改数据包发送出去

// 另外在 input 表上做文章可以实现防火墙,DNAT
// 查表失败的特殊处理下就能得到 DMZ

}

请教内核中方便找到这样的切入点么, 谢谢。

KalelCooper commented 6 years ago

直接干掉 conntrack 那套还有好处是,当从一个端口向外建立海量连接的时候占用的NAT资源非常少,(即便用DNAT也是一样占用很多资源的),跑BT爬虫的时候遇到过这个问题。

Chion82 commented 6 years ago

@KalelCooper 是可以的,之前我试过在实现应用在 mangle 表上的手动 NAT,也可以绕过 conntrack,但是:

KalelCooper commented 6 years ago

抛开历史包袱只实现基本的功能就能满足绝大部分需求了,我打算写一个自己用。 我能用到的功能也就是ipv4的TCP UDP ICMP的NAT,按照最简单的实现方式的话Full cone NAT发而是最简单的了。 根据数据内容自动操作防火墙的功能不用也罢了,极少应用还用这个了。 市场上的大部分路由器也是只实现了这些基本的功能。

默认阻止传入连接的防火墙功能和DNAT,可以顺手在映射表上实现了。 UDP ICMP的老化就是简单的计时器,TCP老化需要具体根据包内容判断。

UPNP这种改下miniupnp也比较容易,未来流行的趋势我认为应该是full cone nat而不应该用这种额外东西。

映射表可以这么做

// 一级映射
struct {
    uint32_t lan_addr;
    time_t  timeout;
} map[0xffff];
/*
lan 侧数据达到后,把 sport 当作 索引进行查找
判断 lan_addr 是否一致 或者 timeout 是否超时,然进行更新或填充 lan_addr timeout。
然后修改数据包 saddr 更新 chksum 发送出去。

wan 侧数据到达的时候根据 dport 查找数组, 判断 timeout 然后发送到内网

@Chion82
这个数组可以合并 TCP 和 UDP 一块处理(ICMP也可以根具id合并进去), 用 32 位timestamp 占用空间是 512KB, 如果缩小到常用端口范围可以是256KB或更小

内网机器少的时候绝大部分是这种不冲突的情况,这也是处理起来最简单最快的方案了。
*/

// 如果冲突后则用二级映射做hash查找
struct hash_map{
    uint32_t lan_addr;
    uint16_t lan_port;
    uint16_t wan_port;
};
// 这里就和iptable类似了, 但依然是full cone nat。

说了这么多可惜我不熟悉内核开发,不知道从哪里下手。 sfe fastpath 好像用的类似的方案,但是它把 conntrak 同步回去了。 如果我打算实现一个这种nat应该从那里入手呢, 可以不可以指教一二呢, 谢谢。

Chion82 commented 6 years ago

@KalelCooper 一般还是从 netfilter 模块入手。可以参考最简单的 netfilter 模块的代码。简单的映射表老化全都用计时器也是可以的。