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>
This commit is contained in:
parent
e81963b180
commit
be8be9eccb
|
@ -260,7 +260,10 @@ struct ip_vs_conn *ip_vs_ct_in_get
|
|||
list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) {
|
||||
if (cp->af == af &&
|
||||
ip_vs_addr_equal(af, s_addr, &cp->caddr) &&
|
||||
ip_vs_addr_equal(af, d_addr, &cp->vaddr) &&
|
||||
/* protocol should only be IPPROTO_IP if
|
||||
* d_addr is a fwmark */
|
||||
ip_vs_addr_equal(protocol == IPPROTO_IP ? AF_UNSPEC : af,
|
||||
d_addr, &cp->vaddr) &&
|
||||
s_port == cp->cport && d_port == cp->vport &&
|
||||
cp->flags & IP_VS_CONN_F_TEMPLATE &&
|
||||
protocol == cp->protocol) {
|
||||
|
@ -698,7 +701,9 @@ ip_vs_conn_new(int af, int proto, const union nf_inet_addr *caddr, __be16 cport,
|
|||
cp->cport = cport;
|
||||
ip_vs_addr_copy(af, &cp->vaddr, vaddr);
|
||||
cp->vport = vport;
|
||||
ip_vs_addr_copy(af, &cp->daddr, daddr);
|
||||
/* proto should only be IPPROTO_IP if d_addr is a fwmark */
|
||||
ip_vs_addr_copy(proto == IPPROTO_IP ? AF_UNSPEC : af,
|
||||
&cp->daddr, daddr);
|
||||
cp->dport = dport;
|
||||
cp->flags = flags;
|
||||
spin_lock_init(&cp->lock);
|
||||
|
|
|
@ -278,7 +278,7 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
|
|||
*/
|
||||
if (svc->fwmark) {
|
||||
union nf_inet_addr fwmark = {
|
||||
.all = { 0, 0, 0, htonl(svc->fwmark) }
|
||||
.ip = htonl(svc->fwmark)
|
||||
};
|
||||
|
||||
ct = ip_vs_ct_in_get(svc->af, IPPROTO_IP, &snet, 0,
|
||||
|
@ -306,7 +306,7 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
|
|||
*/
|
||||
if (svc->fwmark) {
|
||||
union nf_inet_addr fwmark = {
|
||||
.all = { 0, 0, 0, htonl(svc->fwmark) }
|
||||
.ip = htonl(svc->fwmark)
|
||||
};
|
||||
|
||||
ct = ip_vs_conn_new(svc->af, IPPROTO_IP,
|
||||
|
|
Loading…
Reference in New Issue