发送方法是在钩子函数里面,处理完包之后,调用的发包函数。根据采用的不同的策略:nat\tunnel\dr,有三种不同的发送函数。另外bypass不属于LVS负载均衡处理的范畴。
NAT部分只是修改目的地址,不修改端口号。
tunnel部分是添加一个新的IP头部
dr目的地址也不修改。
发送时,都要先查找路由。
NAT发送只发送请求方向的数据,因此是进行目的NAT。dnat_handler是在ip_vs_nat_xmit中调用的。snat_handler 是在handler_response中调用的。在这两个handler中,会调用到app层的packet in 和 packet out 函数。
int ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp)
{
struct rtable *rt; /* Route to the other host */
int mtu;
struct iphdr *iph = skb->nh.iph;
//如果连接标志了客户端端口为0,将当前skb中的端口填给连接
if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT)) {
__u16 _pt, *p;
p = skb_header_pointer(skb, iph->ihl*4, sizeof(_pt), &_pt);
if (p == NULL)
goto tx_error;
ip_vs_conn_fill_cport(cp, *p);// *p是源端口
IP_VS_DBG(10, "filled cport=%d\n", ntohs(*p));
}
//查找路由,找不到的话发ICMP出错包
if (!(rt = __ip_vs_get_out_rt(cp, RT_TOS(iph->tos))))
goto tx_error_icmp;
//检查路由发出网卡的MTU,如果包长超过MTU又有DF标志,发送ICMP错误信息,而不进行分片操作
mtu = dst_mtu(&rt->u.dst);
if ((skb->len > mtu) && (iph->frag_off&__constant_htons(IP_DF))) {
ip_rt_put(rt);
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
IP_VS_DBG_RL_PKT(0, pp, skb, 0, "ip_vs_nat_xmit(): frag needed for");
goto tx_error;
}
//让skb包的IP头部分是可写的
if (!ip_vs_make_skb_writable(&skb, sizeof(struct iphdr)))
goto tx_error_put;