iptables的基本原理以及使用

netfilter/iptables使用

1.什么是iptables?

netfliter/iptables是linux一个用来过滤流量以及数据包转发的内核模块

是linux内核的一部分,iptables是linux中的一个工具,运行在用户空间,可以控制netfliter中增加修改删除数据包的处理规则,netfliter位于网卡和内核之间,可以控制计算机的进出流量

2.内核源码分析

1.hooks函数
1
2
3
4
5
6
7
8
9
enum nf_inet_hooks {
NF_INET_PRE_ROUTING,
NF_INET_LOCAL_IN,
NF_INET_FORWARD,
NF_INET_LOCAL_OUT,
NF_INET_POST_ROUTING,
NF_INET_NUMHOOKS,
NF_INET_INGRESS = NF
//hooks函数定义
2.第一个挂接点(NF_INET_PRE_ROUTING)

这个挂接点是网卡接受数据之后第一个执行的hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//网卡接受数据后调用的第一个函数就是ip_rcv函数,ipv6为ip6_rcv
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
struct net_device *orig_dev)
{
struct net *net = dev_net(dev);

skb = ip_rcv_core(skb, net);
if (skb == NULL)
return NET_RX_DROP;

return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
net, NULL, skb, dev, NULL,
ip_rcv_finish);//下一个挂接点NF_INET_PRE_ROUTING
}
3.第二个挂接点(NF_INET_LOCAL_IN)

这个挂接点在数据包进行路由之后,数据包目标ip是本机的时候执行的hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int ip_local_deliver(struct sk_buff *skb)
{
/*
* Reassemble IP fragments.
*/
struct net *net = dev_net(skb->dev);

if (ip_is_fragment(ip_hdr(skb))) {
if (ip_defrag(net, skb, IP_DEFRAG_LOCAL_DELIVER))
return 0;
}

return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN,
net, NULL, skb, skb->dev, NULL,
ip_local_deliver_finish);
}
3.第三个挂接点(NF_INET_FORWARD)

数据包进入网络层进行路由时,数据包路由选择后目标ip不是本机时执行的hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
int ip_forward(struct sk_buff *skb)
{
u32 mtu;
struct iphdr *iph; /* Our header */
struct rtable *rt; /* Route we use */
struct ip_options *opt = &(IPCB(skb)->opt);
struct net *net;

/* that should never happen */
if (skb->pkt_type != PACKET_HOST)
goto drop;

if (unlikely(skb->sk))
goto drop;

if (skb_warn_if_lro(skb))
goto drop;

if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb))
goto drop;

if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
return NET_RX_SUCCESS;

skb_forward_csum(skb);
net = dev_net(skb->dev);

/*
* According to the RFC, we must first decrease the TTL field. If
* that reaches zero, we must reply an ICMP control message telling
* that the packet's lifetime expired.
*/
if (ip_hdr(skb)->ttl <= 1)
goto too_many_hops;

if (!xfrm4_route_forward(skb))
goto drop;

rt = skb_rtable(skb);

if (opt->is_strictroute && rt->rt_uses_gateway)
goto sr_failed;

IPCB(skb)->flags |= IPSKB_FORWARDED;
mtu = ip_dst_mtu_maybe_forward(&rt->dst, true);
if (ip_exceeds_mtu(skb, mtu)) {
IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
htonl(mtu));
goto drop;
}

/* We are about to mangle packet. Copy it! */
if (skb_cow(skb, LL_RESERVED_SPACE(rt->dst.dev)+rt->dst.header_len))
goto drop;
iph = ip_hdr(skb);

/* Decrease ttl after skb cow done */
ip_decrease_ttl(iph);

/*
* We now generate an ICMP HOST REDIRECT giving the route
* we calculated.
*/
if (IPCB(skb)->flags & IPSKB_DOREDIRECT && !opt->srr &&
!skb_sec_path(skb))
ip_rt_send_redirect(skb);

if (net->ipv4.sysctl_ip_fwd_update_priority)
skb->priority = rt_tos2priority(iph->tos);

return NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD,
net, NULL, skb, skb->dev, rt->dst.dev,
ip_forward_finish);

sr_failed:
/*
* Strict routing permits no gatewaying
*/
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0);
goto drop;

too_many_hops:
/* Tell the sender its packet died... */
__IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS);
icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
drop:
kfree_skb(skb);
return NET_RX_DROP;
}
4.第四个挂接点(NF_INET_LOCAL_OUT)

本机数据向外发送数据时刚从传输层出来第一个执行的hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct iphdr *iph = ip_hdr(skb);

iph->tot_len = htons(skb->len);
ip_send_check(iph);

/* if egress device is enslaved to an L3 master device pass the
* skb to its handler for processing
*/
skb = l3mdev_ip_out(sk, skb);
if (unlikely(!skb))
return 0;

skb->protocol = htons(ETH_P_IP);

return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT,
net, sk, skb, NULL, skb_dst(skb)->dev,
dst_output);
}
5.第五个挂接点(NF_INET_POST_ROUTING)

数据在执行路由选择之后即将进入链路层的时候执行的hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct net_device *dev = skb_dst(skb)->dev, *indev = skb->dev;

IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len);

skb->dev = dev;
skb->protocol = htons(ETH_P_IP);

return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
net, sk, skb, indev, dev,
ip_finish_output,
!(IPCB(skb)->flags & IPSKB_REROUTED));
}

2.流量在netfilter中的走向

iptables

3.iptables的结构

1.表

  • raw

    用于赶上连接之前处理数据包,比如在数据流量大的时候,可以定义某条规则不进入跟踪链,提高iptables效率

  • mangle

    用于修改数据包,如TOS,QOS

  • nat

    用于数据包的网络地址转换

  • filter

    用于数据包的过滤

2.链

  • PREROUTING

    用于路由器前转换(如源地址转换(SNAT))

  • INPUT

    用于入站流量处理

  • FORWARD

    用于转发流量处理

  • OUTPUT

    用于出战流量处理

  • POSTROUTING

    用于路由后转换(如目标地址转换(DNAT))

4.基本用法

命令格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
iptables -t [table] COMMAND(chains) -p [protocol] -s [source ip address] -d [destination ip address] ..... -j ACTION
-R/I/A(修改/插入/追加)
-t 指定表
-p 指定协议(tcp/udp/icmp)
-s 指定源ip地址(ip/mask)
-d 指定目标ip地址
--dport 指定目标端口
--sport 指定源端口
-j 指定处理类型(DROP/REJECT/ACCEPT/REDIRECT/MASQUERADE)(丢弃,丢弃并回应,允许,重定向,伪装源ip地址)
-m 指定扩展类型(multiport/state/owner)
--state 指定连接状态(NEW,ESTABLISHED)
--tcp-flags(SYN,ACK,PSH,FIN,)
--icmp-type(指定icmp的标志位,echo-request,echo-reply)
-i 指定入站网卡
-o 指定出战网卡
1.配置链的默认策略
1
iptables -t filter -P INPUT DROP/ACCEPT
2.配置数据包过滤
1
2
#配置
iptables -t filter -I INPUT -t tcp -s 0.0.0.0/0 -d 0.0.0.0/0 --dport 80 -j DROP
3.配置nat
1
2
3
4
#配置源地址转换
iptables -t nat -I PREROUTING -s 0.0.0.0/0 -d 0.0.0.0 -j MASQUERADE
#配置目标地址转换
iptables -t nat -I POSTROUTING -s 0.0.0.0/0 -d 0.0.0.0/0 -j DNAT --to-destination 192.168.50.1/24
4.配置保存iptables规则
1
iptables-save > ./iptables.txt
5.配置导入iptables规则
1
iptables-restore < ./iptables.txt

总结

iptable是linux中控制流量进出的非常有效的工具,在了解iptables的同时可以非常深刻的了解linux中接收流量后流量的走向。

参考:https://zhuanlan.zhihu.com/p/507786224


iptables的基本原理以及使用
https://dreamaccount.github.io/2022/04/30/iptables/
作者
404NotFound
发布于
2022年4月30日
许可协议