kk Blog —— 通用基础


date [-d @int|str] [+%s|"+%F %T"]
netstat -ltunp
sar -n DEV 1

本地IP包分片--local_df,ignore_df

local_df 和 ignore_df 是一个意思,在某个版本rename了

ip_queue_xmit 函数中有:

1
2
3
4
if (ip_dont_fragment(sk, &rt->dst) && !skb->ignore_df)
	iph->frag_off = htons(IP_DF);
else
	iph->frag_off = 0;

ip_dont_fragment

1
2
3
4
5
6
7
static inline
int ip_dont_fragment(struct sock *sk, struct dst_entry *dst)
{
	return  inet_sk(sk)->pmtudisc == IP_PMTUDISC_DO ||
		(inet_sk(sk)->pmtudisc == IP_PMTUDISC_WANT &&
		 !(dst_metric_locked(dst, RTAX_MTU)));
}

一般情况下都是开启pmtu、skb->ignore_df = 0, 所以 iph->frag_off = htons(IP_DF);

ip_queue_xmit -> ip_finish_output -> ip_fragment :

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
static int ip_fragment(struct sock *sk, struct sk_buff *skb,
	       unsigned int mtu, 
	       int (*output)(struct sock *, struct sk_buff *))
{
	struct iphdr *iph = ip_hdr(skb);

	// 如果需要分片,直接进入分片函数
	if ((iph->frag_off & htons(IP_DF)) == 0)
		return ip_do_fragment(sk, skb, output);

	// 如果没设置分片,或手动设置的分片过大,则直接丢弃
	if (unlikely(!skb->ignore_df ||
		     (IPCB(skb)->frag_max_size &&
		      IPCB(skb)->frag_max_size > mtu))) {
		struct rtable *rt = skb_rtable(skb);
		struct net_device *dev = rt->dst.dev;

		IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
			  htonl(mtu));
		kfree_skb(skb);
		return -EMSGSIZE;
	}

	// 所以设置 skb->ignore_df = 1 且 skb->len > mtu 则执行到这里
	return ip_do_fragment(sk, skb, output);
}

所以设置 skb->ignore_df = 1 且 skb->len > mtu 则执行 ip_do_fragment 进行IP分片,

分片是按网卡mtu进行,如果mss小于网卡mtu-40,则需要设置 IPCB(skb)->frag_max_size

iph->frag_off 定义

1
2
3
#define IP_DF           0x4000          /* Flag: "Don't Fragment"       */
#define IP_MF           0x2000          /* Flag: "More Fragments"       */
#define IP_OFFSET       0x1FFF          /* "Fragment Offset" part       */

1) 不分片的包 iph->frag_off = htons(IP_DF)
2) 最后一个分片包 ((ntohs(iph->frag_off) & IP_OFFSET) > 0)
3) 其余分片包 ((ntohs(iph->frag_off) & IP_OFFSET) > 0 && (iph->frag_off & htons(IP_MF)) > 0)

拥塞控制模块无法卸载

如果listen的sk设置了icsk_ca_setsockopt,那么派生的child默认都是sk的cong,而不是系统默认的cong


如果:自定义拥塞控制中维护了hash表,且在init时加入hash表,在release时删除或卸载清理hash表。

那么:有些情况会导致分配了拥塞控制模块给sk,但却没调init,导致卸载清理时就找不到模块被哪个sk引用,也就无法清理。

可能原因:

1. 拥塞控制初始化时

标准内核:socket()时分配拥塞控制,但在连接建立时才会调 icsk->icsk_ca_ops->init(sk) 如果只调了 socket(), bind(), listen() 那么就不会调 icsk->icsk_ca_ops->init(sk)(所以listen的sk就一定不调init)。

内核可行修改方案:socket -> tcp_init_sock -> tcp_assign_congestion_control 时先分配一个内核内部的拥塞控制,连接建立时再分配实际拥塞控制,然后初始化。 这么改还是有缺陷:先调 socket(),再调 setsockopt 设置拥塞控制模块,因为此时sk->sk_state = TCP_CLOSE,所以不会调init

2. 拥塞控制切换时

拥塞控制切换时:如果是TCP_CLOSE状态也不会调 icsk->icsk_ca_ops->init(sk) 初始化

可行解决方法

模仿 /proc/net/tcp 遍历hash表,修改拥塞控制

特殊情况:

调 socket 时 sk 不加入hash表,在 listen 或 connect 时才会加入到hash表,如果只调socket、bind然后不再使用该fd,那么sk不仅没调icsk->icsk_ca_ops->init(sk),也没加入hash表。

此时 /proc/net/tcp 也找不到sk,只能遍历所有进程的所有fd,找出sk再修改

conglist.tar.gz

iptables

一台通过另一台上网

1
2
3
4
5
6
7
8
find /proc/ -name rp_filter -exec sh -c "echo 0 > {} " \;
find /proc/ -name rp_filter -exec cat {} \;

echo 1 > /proc/sys/net/ipv4/ip_forward
cat /proc/sys/net/ipv4/ip_forward

iptables -t nat -A POSTROUTING -s 12.0.0.10/24 -o wlp7s0 -j MASQUERADE
iptables -t nat -L -vn

http://blog.csdn.net/l241002209/article/details/43987933

1、添加

添加规则有两个参数:-A和-I。其中
-A是添加到规则的末尾;
-I可以插入到指定位置,没有指定位置的话默认插入到规则的首部。

2、查看

1
2
3
4
5
6
iptables -nvL –line-number

 -L 查看当前表的所有规则,默认查看的是filter表,如果要查看NAT表,可以加上-t NAT参数
 -n 不对ip地址进行反查,加上这个参数显示速度会快很多
 -v 输出详细信息,包含通过该规则的数据包数量,总字节数及相应的网络接口
 –line-number 显示规则的序列号,这个参数在删除或修改规则时会用到

3

1
2
3
4
5
6
7
8
9
10
iptables -nvL
iptables -F # 清除所有规则,但不改变默认策略
iptables -P INPUT ACCEPT  # INPUT 默认策略
iptables -P OUTPUT ACCEPT # OUTPUT 默认策略

iptables -I INPUT -s 192.168.1.5 -j DROP # 头部插入
iptables -A INPUT -p tcp --dport 22 -j ACCEPT # 尾部追加,规则按顺序匹配的,匹配到就返回

iptables -D INPUT -s 192.168.1.5 -j DROP
iptables -D INPUT 2

4、修改 修改使用-R参数

1
iptables -R INPUT 3 -j ACCEPT

https://blog.csdn.net/zqixiao_09/article/details/53401321

NAT地址转换

iptables nat 原理

同filter表一样,nat表也有三条缺省的"链"(chains):

PREROUTING:目的DNAT规则

把从外来的访问重定向到其他的机子上,比如内部SERVER,或者DMZ。

因为路由时只检查数据包的目的ip地址,所以必须在路由之前就进行目的PREROUTING DNAT; 系统先PREROUTING DNAT翻译——>再过滤(FORWARD)——>最后路由。 路由和过滤(FORWARD)中match 的目的地址,都是针对被PREROUTING DNAT之后的。

POSTROUTING:源SNAT规则

在路由以后在执行该链中的规则。

系统先路由——>再过滤(FORWARD)——>最后才进行POSTROUTING SNAT地址其match 源地址是翻译前的。

OUTPUT:定义对本地产生的数据包的目的NAT规则

内网访问外网 -J SNAT

-j SNAT 源网络地址转换,SNAT就是重写包的源IP地址, SNAT 只能用在nat表的POSTROUTING链里

固定public 地址(外网接口地址)的最基本内访外SNAT

1
iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -o eth0 -j SNAT --to 你的eth0地址

-j MASQUERADE

用于外网口public地址是DHCP动态获取的(如ADSL)

1
2
iptables -t nat  -A POSTROUTING –o eth1 –s 192.168.1.0/24 –j MASQUERADE
iptables -t nat  -A POSTROUTING -o ppp0  -j  MASQUERADE

外网访问内网 –J DNT

DNAT:目的网络地址转换,重写包的目的IP地址

典型的DNAT的例子

外部接口ip:210.83.2.206 内部接口ip:192.168.1.1

ftp服务器 : ip 192.168.1.3 web服务器 : ip 192.168.1.4

1
2
iptables -t nat -A PREROUTING -d 210.83.2.206 -p tcp --dport 21 -j DNAT --to 192.168.1.3
iptables -t nat -A PREROUTING -d 210.83.2.206 -p tcp --dport 80 -j DNAT --to 192.168.1.4

DNAT用于内部SERVER的load-balance(即CISCO的rotery)

1
iptables –t nat –A PREROUTING –d 219.142.217.161 –j DNAT --to-destination 192.168.1.24-192.168.1.25
DNAT 带端口映射(改变SERVER的端口)

一个FTP SERVER从内部192.168.100.125:21映射到216.94.87.37:2121的例子

1
iptables -t nat -A PREROUTING -p tcp -d 216.94.87.37 --dport 2121 -j DNAT --to-destination 192.168.100.125:21