diff --git a/net/ipv4/ipvs/ip_vs_core.c b/net/ipv4/ipvs/ip_vs_core.c --- a/net/ipv4/ipvs/ip_vs_core.c +++ b/net/ipv4/ipvs/ip_vs_core.c @@ -1084,6 +1084,15 @@ static struct nf_hook_ops ip_vs_out_ops .priority = 100, }; +/* change source only for local VS/NAT */ +static struct nf_hook_ops ip_vs_local_out_ops = { + .hook = ip_vs_out, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_IP_LOCAL_OUT, + .priority = 100, +}; + /* After packet filtering (but before ip_vs_out_icmp), catch icmp destined for 0.0.0.0/0, which is for incoming IPVS connections */ static struct nf_hook_ops ip_vs_forward_icmp_ops = { @@ -1142,10 +1151,15 @@ static int __init ip_vs_init(void) IP_VS_ERR("can't register out hook.\n"); goto cleanup_inops; } + ret = nf_register_hook(&ip_vs_local_out_ops); + if (ret < 0) { + IP_VS_ERR("can't register local out hook.\n"); + goto cleanup_outops; + } ret = nf_register_hook(&ip_vs_post_routing_ops); if (ret < 0) { IP_VS_ERR("can't register post_routing hook.\n"); - goto cleanup_outops; + goto cleanup_localoutops; } ret = nf_register_hook(&ip_vs_forward_icmp_ops); if (ret < 0) { @@ -1158,6 +1172,8 @@ static int __init ip_vs_init(void) cleanup_postroutingops: nf_unregister_hook(&ip_vs_post_routing_ops); + cleanup_localoutops: + nf_unregister_hook(&ip_vs_local_out_ops); cleanup_outops: nf_unregister_hook(&ip_vs_out_ops); cleanup_inops: @@ -1179,6 +1195,7 @@ static void __exit ip_vs_cleanup(void) nf_unregister_hook(&ip_vs_post_routing_ops); nf_unregister_hook(&ip_vs_out_ops); nf_unregister_hook(&ip_vs_in_ops); + nf_unregister_hook(&ip_vs_local_out_ops); ip_vs_conn_cleanup(); ip_vs_app_cleanup(); ip_vs_protocol_cleanup(); diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c --- a/net/ipv4/ipvs/ip_vs_ctl.c +++ b/net/ipv4/ipvs/ip_vs_ctl.c @@ -672,8 +672,17 @@ __ip_vs_update_dest(struct ip_vs_service /* check if local node and update the flags */ if (inet_addr_type(udest->addr) == RTN_LOCAL) { - conn_flags = (conn_flags & ~IP_VS_CONN_F_FWD_MASK) - | IP_VS_CONN_F_LOCALNODE; + if ((conn_flags & IP_VS_CONN_F_FWD_MASK) == IP_VS_CONN_F_MASQ) { + if (dest->port == dest->vport) { + conn_flags = (conn_flags & + ~IP_VS_CONN_F_FWD_MASK) | + IP_VS_CONN_F_LOCALNODE; + } + } + else { + conn_flags = (conn_flags & ~IP_VS_CONN_F_FWD_MASK) + | IP_VS_CONN_F_LOCALNODE; + } } /* set the IP_VS_CONN_F_NOOUTPUT flag if not masquerading/NAT */