本文共 5351 字,大约阅读时间需要 17 分钟。
依赖NF_INET_PRE_ROUTING链上的ip_vs_pre_routing()hook函数来向client发送SYN ACK报文。ip_vs_pre_routing()源码如下:
ip_vs_pre_routing(unsigned int hooknum, struct sk_buff *skb,const struct net_device *in, const struct net_device *out,int (*okfn) (struct sk_buff *)){ struct ip_vs_iphdr iph; /* address family */ int af; struct ip_vs_service *svc; af = (skb->protocol == htons(ETH_P_IP)) ? AF_INET : AF_INET6; ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); /* drop all ip fragment except ospf */ if ((af == AF_INET) /* 通过判断ip header中frag_off字段及MF(More Fragment字段确定该包是否为ip分片包) */ && (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) /* 不drop ospf协议包,并交给协议栈继续处理 */ && (iph.protocol != IPPROTO_OSPF)) { if(sysctl_ip_vs_frag_drop_entry == 1) { IP_VS_INC_ESTATS(ip_vs_esmib, DEFENCE_IP_FRAG_DROP); return NF_DROP; } else { if (ip_vs_gather_frags(skb, IP_DEFRAG_VS_IN)) return NF_STOLEN; IP_VS_INC_ESTATS(ip_vs_esmib, DEFENCE_IP_FRAG_GATHER); ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); } } /* drop udp packet which send to tcp-vip */ if ((sysctl_ip_vs_udp_drop_entry == 1) && (iph.protocol == IPPROTO_UDP)) { /* 在svc链表中根据协议族、传输层协议及vip三者的hash key查找对应hash bucket中满足条件的svc结构体 (当bucket数量足够多时,每个hash key对应的bucket中只有一个svc结构体,查找复杂度为O(1))*/ if ((svc = ip_vs_lookup_vip(af, IPPROTO_TCP, &iph.daddr)) != NULL) { IP_VS_INC_ESTATS(ip_vs_esmib, DEFENCE_UDP_DROP); return NF_DROP; } } /* synproxy: defence synflood */ if (iph.protocol == IPPROTO_TCP) { int v = NF_ACCEPT; /* 构建回给client的syn-ack包,利用变量v作为该hook函数的返回值 */ if (0 == ip_vs_synproxy_syn_rcv(af, skb, &iph, &v)) { return v; } } return NF_ACCEPT;}
ip_vs_synproxy_syn_rcv()实现如下:
int ip_vs_synproxy_syn_rcv(int af, struct sk_buff *skb,struct ip_vs_iphdr *iph, int *verdict){ struct ip_vs_service *svc = NULL; struct tcphdr _tcph, *th; struct ip_vs_synproxy_opt tcp_opt; /* 获取client syn包的tcp header */ th = skb_header_pointer(skb, iph->len, sizeof(_tcph), &_tcph); if (unlikely(th == NULL)) { goto syn_rcv_out;} /* 判断是否为syn包 */ if (th->syn && !th->ack && !th->rst && !th->fin && /* 根据传输层协议和目标地址及目标端口获取对应的svc结构体 */ (svc = ip_vs_service_get(af, skb->mark, iph->protocol, &iph->daddr, th→dest)) /* 若syn-proxy未打开,则该包将由协议栈继续处理 */ && (svc->flags & IP_VS_CONN_F_SYNPROXY)) { // release service here, because don't use it any all. ip_vs_service_put(svc); if (ip_vs_todrop()) { /* * It seems that we are very loaded. * We have to drop this packet :( */ goto syn_rcv_out; } } else { /* * release service. */ if (svc != NULL) { ip_vs_service_put(svc); } return 1; } /* update statistics */ IP_VS_INC_ESTATS(ip_vs_esmib, SYNPROXY_SYN_CNT); /* Try to reuse skb if possible */ /* 确保该skb当前只被一个user使用,且不是clone得到的(clone的skb是共享数据,在正常情况下不能写入) */ if (unlikely(skb_shared(skb) || skb_cloned(skb))) { /* 复制一个私有的且能够修改其header和data的skb,若只需要修改header则需要调用pskb_copy() */ struct sk_buff *new_skb = skb_copy(skb, GFP_ATOMIC); if (unlikely(new_skb == NULL)) { goto syn_rcv_out; } /* Drop old skb */ kfree_skb(skb); skb = new_skb; } /* reuse skb here: deal with tcp options, exchage ip, port. */ syn_proxy_reuse_skb(af, skb, &tcp_opt); /* syn_proxy_reuse_skb()主要完成以下任务: * 1. 设置syn-ack包tcp option:解析client syn包中的tcp option,并根据这些option及sysctl相关参数,确定syn-ack包的tcp option * 2. 生成syn-ack包seq number:根据client syn包中的源地址、目标地址、源端口、目标端口、seq number、系统启动时长(jiffies / (HZ * 60))以及tcp option拼成的data使用sha1算法得到cookie_hash(__u32)即init_seq_number * !!!:在启用synproxy的情况下,服务器收到syn包时,不会分配专门的数据区,而是根据这个syn包计算出一个cookie值,这个cookie作为将要返回的SYN ACK包的初始序列号。当客户端返回一个ACK包时,根据包头信息计算cookie,与返回的确认序列 * !!! 号(初始序列号 + 1)进行对比,如果相同,则是一个正常连接,然后,分配资源,建立连接。 * 3. 设置syn-ack包syn ack flag(0x12) * 4. 交换tcp header中的源目的端口 * 5. 确定ip header中的源目的ip、ttl、tos(缺省为0) * 6. 计算ip header和tcp checksum */ if (unlikely(skb->dev == NULL)) { IP_VS_ERR_RL("%s: skb->dev is null !!!\n", __func__); goto syn_rcv_out; } /* Send the packet out */ if (likely(skb->dev->type == ARPHRD_ETHER)) { unsigned char t_hwaddr[ETH_ALEN]; /* 设置2层信息 */ /* Move the data pointer to point to the link layer header */ struct ethhdr *eth = (struct ethhdr *)skb_mac_header(skb); skb->data = (unsigned char *)skb_mac_header(skb); skb->len += ETH_HLEN; //sizeof(skb->mac.ethernet); memcpy(t_hwaddr, (eth->h_dest), ETH_ALEN); memcpy((eth->h_dest), (eth->h_source), ETH_ALEN); memcpy((eth->h_source), t_hwaddr, ETH_ALEN); skb->pkt_type = PACKET_OUTGOING; } else if (skb->dev->type == ARPHRD_LOOPBACK) { /* set link layer */ if (likely(skb_mac_header_was_set(skb))) { skb->data = skb_mac_header(skb); skb->len += sizeof(struct ethhdr); } else { skb_push(skb, sizeof(struct ethhdr)); skb_reset_mac_header(skb); } } /* 转发syn-ack包 */ dev_queue_xmit(skb); /* 告诉内核该skb不再经过后续的内核协议栈处理,但保留为该skb分配的资源 */ *verdict = NF_STOLEN; return 0;syn_rcv_out: /* Drop the packet when all things are right also, * then we needn't to kfree_skb() */ *verdict = NF_DROP; return 0;}
转载于:https://blog.51cto.com/10843840/2359415