每一个都会有以下规则IP最大并发连接数控制在5个,使用connlimit-above或者connlimit-upto都可实现。
# iptables -I INPUT -p tcp --syn --dport 22 -m connlimit --connlimit-above 5 -j DROP # # iptables -L -n -v Chain INPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 flags:0x17/0x02 #conn src/32 > 5 # # # iptables -I INPUT -p tcp --syn --dport 22 -m connlimit --connlimit-upto 5 -j ACCEPT # # iptables -L -n -v Chain INPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 flags:0x17/0x02 #conn src/32 <= 5
每个C网段的所有地址连接总数控制在5个。
# iptables -I INPUT -p tcp --syn --dport 22 -m connlimit --connlimit-above 5 --connlimit-mask 24 -j DROP # # iptables -L -n -v Chain INPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 flags:0x17/0x02 #conn src/24 > 5
默认情况下–connlimit-saddr以下指定生效–connlimit-daddr,连接到每个目的地址的数量不超过5个。
# iptables -I INPUT -p tcp --syn --dport 22 -m connlimit --connlimit-above 5 --connlimit-daddr -j DROP # # iptables -L -n -v Chain INPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 flags:0x17/0x02 #conn dst/32 > 5
connlimit匹配
内核函数connlimit_mt_init注册了connlimit_mt_reg匹配结构。
static struct xt_match connlimit_mt_reg __read_mostly = { .name = "connlimit", .revision = 1, .family = NFPROTO_UNSPEC, .checkentry = connlimit_mt_check, .match = connlimit_mt, .matchsize = sizeof(struct xt_connlimit_info), .usersize = offsetof(struct xt_connlimit_info, data), .destroy = connlimit_mt_destroy, .me = THIS_MODULE, }; static int __init connlimit_mt_init(void) { return xt_register_match(&connlimit_mt_reg); }
检查函数的配置connlimit_mt_check以下是这里的初始化conncount相关结构,keylen包括zone区ID以及相应协议组的地址长度。
static int connlimit_mt_check(const struct xt_mtchk_param *par) { struct xt_connlimit_info *info = par->matchinfo; unsigned int keylen; keylen = sizeof(u32); if (par->family == NFPROTO_IPV6) keylen = sizeof(struct in6_addr); else keylen = sizeof(struct in_addr); /* init private data */ info->data = nf_conncount_init(par->net, par->family, keylen); return PTR_ERR_OR_ZERO(info->data);
匹配函数connlimit_mt,如果报纸有连接跟踪结构,请取出原始方向tuple,否则,分析报文tuple,分析失败,返回false,未能匹配。
static bool connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_connlimit_info *info = par->matchinfo; struct nf_conntrack_tuple tuple; const struct nf_conntrack_tuple *tuple_ptr = &tuple; const struct nf_conntrack_zone *zone = &nf_ct_zone_dflt; const struct nf_conn *ct; u32 key[5]; ct = nf_ct_get(skb, &ctinfo); if (ct != NULL) { tuple_ptr = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; zone = nf_ct_zone(ct); } else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), xt_family(par), net, &tuple)) { goto hotdrop; }
否则,成功获得报文的tuple信息,对于IPv6.如果设置XT_CONNLIMIT_DADDR标志(–connlimit-daddr),key选择报纸的目的地地址,否则,选择源地址。
if (xt_family(par) == NFPROTO_IPV6) { const struct ipv6hdr *iph = ipv6_hdr(skb); union nf_inet_addr addr; memcpy(&addr.ip6, (info->flags & XT_CONNLIMIT_DADDR) ? &iph->daddr : &iph->saddr, sizeof(addr.ip6)); for (i = 0; i < ARRAY_SIZE(addr.ip6); i) addr.ip6[i] &= info->mask.ip6[i]; memcpy(key, &addr, sizeof(addr.ip6)); key[4] = zone->id; } else { const struct iphdr *iph = ip_hdr(skb); key[0] = (info->flags & XT_CONNLIMIT_DADDR) ? iph->daddr : iph->saddr; key[0] &= info->mask.ip; key[1] = zone->id; }
对于IPv4协议,处理与以上相同,另外,key值中的地址部分需要与配置的掩码进行与操作(&)。之后,由key值和tuple值来获取连接的数量,最后,检查是否满足限制条件。
connections = nf_conncount_count(net, info->data, key, tuple_ptr, zone);
if (connections == 0)
/* kmalloc failed, drop it entirely */
goto hotdrop;
return (connections > info->limit) ^ !!(info->flags & XT_CONNLIMIT_INVERT);
hotdrop:
par->hotdrop = true;
return false;
内核版本 5.10