ipvs: Fix IPv4 FWMARK virtual services

This fixes the use of fwmarks to denote IPv4 virtual services
which was unfortunately broken as a result of the integration
of IPv6 support into IPVS, which was included in 2.6.28.

The problem arises because fwmarks are stored in the 4th octet
of a union nf_inet_addr .all, however in the case of IPv4 only
the first octet, corresponding to .ip, is assigned and compared.

In other words, using .all = { 0, 0, 0, htonl(svc->fwmark) always
results in a value of 0 (32bits) being stored for IPv4. This means
that one fwmark can be used, as it ends up being mapped to 0, but things
break down when multiple fwmarks are used, as they all end up being mapped
to 0.

As fwmarks are 32bits a reasonable fix seems to be to just store the fwmark
in .ip, and comparing and storing .ip when fwmarks are used.

This patch makes the assumption that in calls to ip_vs_ct_in_get()
and ip_vs_sched_persist() if the proto parameter is IPPROTO_IP then
we are dealing with an fwmark. I believe this is valid as ip_vs_in()
does fairly strict filtering on the protocol and IPPROTO_IP should
not be used in these calls unless explicitly passed when making
these calls for fwmarks in ip_vs_sched_persist().

Tested-by: Fabien Duchêne <fabien.duchene@student.uclouvain.be>
Cc: Joseph Mack NA3T <jmack@wm7d.net>
Cc: Julius Volz <julius.volz@gmail.com>
Signed-off-by: Simon Horman <horms@verge.net.au>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by Simon Horman and committed by David S. Miller be8be9ec e81963b1

+9 -4
+7 -2
net/netfilter/ipvs/ip_vs_conn.c
··· 260 list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { 261 if (cp->af == af && 262 ip_vs_addr_equal(af, s_addr, &cp->caddr) && 263 - ip_vs_addr_equal(af, d_addr, &cp->vaddr) && 264 s_port == cp->cport && d_port == cp->vport && 265 cp->flags & IP_VS_CONN_F_TEMPLATE && 266 protocol == cp->protocol) { ··· 701 cp->cport = cport; 702 ip_vs_addr_copy(af, &cp->vaddr, vaddr); 703 cp->vport = vport; 704 - ip_vs_addr_copy(af, &cp->daddr, daddr); 705 cp->dport = dport; 706 cp->flags = flags; 707 spin_lock_init(&cp->lock);
··· 260 list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { 261 if (cp->af == af && 262 ip_vs_addr_equal(af, s_addr, &cp->caddr) && 263 + /* protocol should only be IPPROTO_IP if 264 + * d_addr is a fwmark */ 265 + ip_vs_addr_equal(protocol == IPPROTO_IP ? AF_UNSPEC : af, 266 + d_addr, &cp->vaddr) && 267 s_port == cp->cport && d_port == cp->vport && 268 cp->flags & IP_VS_CONN_F_TEMPLATE && 269 protocol == cp->protocol) { ··· 698 cp->cport = cport; 699 ip_vs_addr_copy(af, &cp->vaddr, vaddr); 700 cp->vport = vport; 701 + /* proto should only be IPPROTO_IP if d_addr is a fwmark */ 702 + ip_vs_addr_copy(proto == IPPROTO_IP ? AF_UNSPEC : af, 703 + &cp->daddr, daddr); 704 cp->dport = dport; 705 cp->flags = flags; 706 spin_lock_init(&cp->lock);
+2 -2
net/netfilter/ipvs/ip_vs_core.c
··· 278 */ 279 if (svc->fwmark) { 280 union nf_inet_addr fwmark = { 281 - .all = { 0, 0, 0, htonl(svc->fwmark) } 282 }; 283 284 ct = ip_vs_ct_in_get(svc->af, IPPROTO_IP, &snet, 0, ··· 306 */ 307 if (svc->fwmark) { 308 union nf_inet_addr fwmark = { 309 - .all = { 0, 0, 0, htonl(svc->fwmark) } 310 }; 311 312 ct = ip_vs_conn_new(svc->af, IPPROTO_IP,
··· 278 */ 279 if (svc->fwmark) { 280 union nf_inet_addr fwmark = { 281 + .ip = htonl(svc->fwmark) 282 }; 283 284 ct = ip_vs_ct_in_get(svc->af, IPPROTO_IP, &snet, 0, ··· 306 */ 307 if (svc->fwmark) { 308 union nf_inet_addr fwmark = { 309 + .ip = htonl(svc->fwmark) 310 }; 311 312 ct = ip_vs_conn_new(svc->af, IPPROTO_IP,