Linux connection tracking and NAT
Linux 的connection tracking和NAT都是通过netfilter框架实现的。connection tracking模块实现了防火墙的状态检测功能,包括单连接和多连接的(FTP)。NAT模块依赖于connection tracking建立的connection,在此基础上进行地址和端口的匹配和转换。PREROUTING: ip_conntrac
Linux 的connection tracking和NAT都是通过netfilter框架实现的。connection tracking模块实现了防火墙的状态检测功能,包括单连接和多连接的(FTP)。NAT模块依赖于connection tracking建立的connection,在此基础上进行地址和端口的匹配和转换。
PREROUTING: ip_conntrack_defrag
ip_conntrack_in //init conn and bind helper orexecute expectation
ip_nat_in //DNAT
POSTROUTING: ip_nat_out //SNAT
ip_conntrack_help //execute helper andgenerate expectation
ip_nat_adjust //adjust tcp seq
ip_confirm //establish conn
1.1 UDP: SNAT
1. ip_conntrack_in():
resolve_normal_ct()->
init_conntrack()-> //malloc and init conn, helper, expecation.
protocol->new() // proto special handling:udp_new()
proto->packet()->
ip_ct_refresh_acct() //refresh timer, doaccounting and send event
2. ip_nat_in():
ip_nat_fn()->//the following seems uncessary
alloc_null_binding()->
ip_nat_setup_info()
3. ip_nat_out():
ip_nat_fn()->
ip_nat_rule_find()->
ipt_do_table()->
ipt_snat_target()-> //Match & Targetsystem
ip_nat_setup_info()-> //setup snat andwill also alter dnat info
invert_tuplepr() -> //just to get original tuple
ip_ct_invert_tuple()->
protocol->invert_tuple//proto special handling
get_unique_tuple()-> //install nat info into conn
find_best_ips_proto() // ip nat
proto->unique_tuple // proto specialhandling, e.g, udp_unique_tuple
ip_nat_packet ()->
manip_pkt()-> //mangle packet based on the conn info
proto->manip_pkt // proto specialhandling: port nat
4. ip_confirm():
__ip_conntrack_confirm()//insert conn, add timer and send conn & helper & nat event
1.2 UDP: DNAT
1. ip_conntrack_in():
resolve_normal_ct()->
proto->packet()->
ip_ct_refresh_acct() //refresh timer, doaccounting and send event
2. ip_nat_in():
ip_nat_fn()->
ip_nat_packet ()->
manip_pkt()-> //mangle packet based on the conn info
proto->manip_pkt // proto specialhandling: port nat
3. ip_nat_out(): //
ip_nat_fn()->
ip_nat_packet ()->// the following seems unnecessary
manip_pkt()-> //mangle packet based on the conn info
proto->manip_pkt // proto specialhandling: port nat
5. ip_confirm()://do nothing
1.3 TCP/HTTP: SNAT
1. ip_conntrack_in():
resolve_normal_ct()->
init_conntrack()-> //malloc and init conn, helper, expecation.
protocol->new() // tcp_new(), fill conn->proto->tcp structure
proto->packet() -> //tcp_packet(),change the conn state and timer
ip_ct_refresh_acct() //refresh timer, doaccounting and send event
1.4 TCP/HTTP: DNAT
1. ip_conntrack_in():
resolve_normal_ct()->
proto->packet() -> //tcp_packet(),change the conn state and timer
ip_ct_refresh_acct() //refresh timer, doaccounting and send event
1.5 TCP/FTP: SNAT
1. ip_conntrack_in():
resolve_normal_ct()->
init_conntrack()-> //mallocand init conn, helper, expecation.
protocol->new() // tcp_new(), fill conn->proto->tcp structure
proto->packet() -> //tcp_packet(),change the conn state and timer
ip_ct_refresh_acct() //refresh timer, doaccounting and send event
1.6 TCP/FTP: DNAT
1. ip_conntrack_in():
resolve_normal_ct()->
proto->packet() -> //tcp_packet(),change the conn state and timer
ip_ct_refresh_acct() //refresh timer, doaccounting and send event
1.7 TCP/FTP(PORT Command): SNAT+ helper
1. ip_conntrack_help():
ct->helper->help()-> //protocol specialhandling: init related expectation
ip_nat_ftp() ->
ip_conntrack_expect_related() // registerexpectation
mangle_rfc959_packet()-> // mangle the packet
ip_nat_mangle_tcp_packet()->
mangle_contents()
adjust_tcp_sequence()
ip_conntrack_tcp_update()
update_nl_seq()
2. ip_nat_adjust():
ip_nat_seq_adjust() //adjust tcp seq
1.8 TCP/FTP(PORT Command): DNAT+ helper
1.
ip_conntrack_help():
ct->helper->help()->//protocol special handling: init related expectation
ip_nat_ftp() ->
update_nl_seq()
2.
ip_nat_adjust():
ip_nat_seq_adjust() //adjust tcp seq
1.9 TCP/FTP: DNAT + Expectation
1.
ip_conntrack_in():
resolve_normal_ct()->
init_conntrack()-> //malloc and init conn, helper, expecation.
exp->expectfn() // ip_nat_follow_master()
ip_nat_setup_info() // snat ???
ip_nat_setup_info() // dnat will alter snat
protocol->new() // proto special handling:udp_new()
proto->packet()->
ip_ct_refresh_acct() //refresh timer, doaccounting and send event
1.10 ICMP: SNAT
The firewall set the NEW state once gets a request packet, it’s,then sets ESTABLISHED once gets related reply packets, and then deletes thisconn.
icmp_invert_tuple()
icmp_manip_pkt()
1.11 ICMP/Error: DNAT
1. ip_conntrack_in():
proto->error() // icmp_error()
icmp_error_message()
2. ip_nat_out()
ip_nat_fn()->
ip_nat_icmp_reply_translation()->
1.12 Port Translation
不论是SNAT还是MASQ,如果有冲突,kernel都会做Port Translation
iptables-t nat -A POSTROUTING -s 172.19.0.200/32 -j SNAT --to 172.18.0.2-172.18.0.3
iptables-t nat -A PREROUTING -i eif1 -p tcp --dport 80 -j DNAT --to 172.19.0.200
iptables-t nat -A POSTROUTING -o eif1 -j MASQUERADE
iptables-A FORWARD -i eif1 -o eif2 -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables-A FORWARD -i eif2 -o eif1 -j ACCEPT
get_unique_tuple(structip_conntrack_tuple *tuple,
const struct ip_conntrack_tuple *orig_tuple,
const struct ip_nat_range *range,
struct ip_conntrack *conntrack,
enum ip_nat_manip_type maniptype)
{
struct ip_nat_protocol *proto;
/* 1) If this srcip/proto/src-proto-partis currently mapped,
and that same mapping gives a unique tuple within the given
range, use that.
This is only required for source (ie. NAT/masq) mappings.
So far, we don't do local source mappings, so multiple
manips not an issue. */
if (maniptype == IP_NAT_MANIP_SRC) {
if (find_appropriate_src(orig_tuple,tuple, range)) {
DEBUGP("get_unique_tuple:Found current src map\n");
if (!(range->flags& IP_NAT_RANGE_PROTO_RANDOM))
if(!ip_nat_used_tuple(tuple, conntrack))
return;
}
}
/* 2) Select the least-used IP/protocombination in the given
range. */
*tuple = *orig_tuple;
find_best_ips_proto(tuple, range,conntrack, maniptype);
/* 3) The per-protocol part of the manipis made to map into
the range to make a unique tuple. */
rcu_read_lock();
proto =__ip_nat_proto_find(orig_tuple->dst.protonum);
/* 如果range有random标志,则通过特定proto随即取一个*/
/* Change protocol info to have somerandomization */
if (range->flags &IP_NAT_RANGE_PROTO_RANDOM) {
proto->unique_tuple(tuple, range,maniptype, conntrack);
goto out;
}
/* 如果没有指定端口,并且当前报文中的端口没有被用,那就不需要PortTranslation */
/* Only bother mapping if it's notalready in range and unique */
if ((!(range->flags &IP_NAT_RANGE_PROTO_SPECIFIED)
|| proto->in_range(tuple, maniptype, &range->min,&range->max))
&& !ip_nat_used_tuple(tuple, conntrack))
goto out;
/* 不管了,做了PortTranslation 先*/
/* Last change: get protocol to try toobtain unique tuple. */
proto->unique_tuple(tuple, range,maniptype, conntrack);
out:
rcu_read_unlock();
}
/*具体到proto特定的函数就很直观了,如果有指定范围,从指定范围中选。如果没有指定范围,小于512的从1~511选一个未被占用的,小于1024的从600到1023选一个未被占用的,大于1024的从1024~65535- 1024 + 1选一个未被占用的。如果有random标志,在其基础上random一下*/
staticint
udp_unique_tuple(structip_conntrack_tuple *tuple,
const struct ip_nat_range *range,
enum ip_nat_manip_type maniptype,
const struct ip_conntrack *conntrack)
{
static u_int16_t port;
__be16 *portptr;
unsigned int range_size, min, i;
if (maniptype == IP_NAT_MANIP_SRC)
portptr =&tuple->src.u.udp.port;
else
portptr =&tuple->dst.u.udp.port;
/* If no range specified... */
if (!(range->flags &IP_NAT_RANGE_PROTO_SPECIFIED)) {
/* If it's dst rewrite, can't changeport */
if (maniptype == IP_NAT_MANIP_DST)
return 0;
if (ntohs(*portptr) < 1024) {
/* Loose convention:>> 512 is credential passing */
if(ntohs(*portptr)<512) {
min = 1;
range_size =511 - min + 1;
} else {
min = 600;
range_size =1023 - min + 1;
}
} else {
min = 1024;
range_size = 65535 - 1024+ 1;
}
} else {
min = ntohs(range->min.udp.port);
range_size =ntohs(range->max.udp.port) - min + 1;
}
/* Start from random port to avoidprediction */
if (range->flags & IP_NAT_RANGE_PROTO_RANDOM)
port = net_random();
for (i = 0; i < range_size; i++,port++) {
*portptr = htons(min + port %range_size);
if (!ip_nat_used_tuple(tuple,conntrack))
return 1;
}
return 0;
}
更多推荐
所有评论(0)