ipvs: add function to find tunnels
Add ip_vs_find_tunnel() to match tunnel headers by family, address and optional port. Use it to properly find the tunnel real server used in received ICMP errors. Signed-off-by: Julian Anastasov <ja@ssi.bg> Signed-off-by: Simon Horman <horms@verge.net.au> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
parent
1da40ab6ca
commit
2aa3c9f48b
|
@ -1404,6 +1404,9 @@ bool ip_vs_has_real_service(struct netns_ipvs *ipvs, int af, __u16 protocol,
|
|||
struct ip_vs_dest *
|
||||
ip_vs_find_real_service(struct netns_ipvs *ipvs, int af, __u16 protocol,
|
||||
const union nf_inet_addr *daddr, __be16 dport);
|
||||
struct ip_vs_dest *ip_vs_find_tunnel(struct netns_ipvs *ipvs, int af,
|
||||
const union nf_inet_addr *daddr,
|
||||
__be16 tun_port);
|
||||
|
||||
int ip_vs_use_count_inc(void);
|
||||
void ip_vs_use_count_dec(void);
|
||||
|
|
|
@ -1598,6 +1598,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
|
|||
struct ip_vs_proto_data *pd;
|
||||
unsigned int offset, offset2, ihl, verdict;
|
||||
bool ipip, new_cp = false;
|
||||
union nf_inet_addr *raddr;
|
||||
|
||||
*related = 1;
|
||||
|
||||
|
@ -1636,15 +1637,22 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
|
|||
cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
|
||||
if (cih == NULL)
|
||||
return NF_ACCEPT; /* The packet looks wrong, ignore */
|
||||
raddr = (union nf_inet_addr *)&cih->daddr;
|
||||
|
||||
/* Special case for errors for IPIP packets */
|
||||
ipip = false;
|
||||
if (cih->protocol == IPPROTO_IPIP) {
|
||||
struct ip_vs_dest *dest;
|
||||
|
||||
if (unlikely(cih->frag_off & htons(IP_OFFSET)))
|
||||
return NF_ACCEPT;
|
||||
/* Error for our IPIP must arrive at LOCAL_IN */
|
||||
if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL))
|
||||
return NF_ACCEPT;
|
||||
dest = ip_vs_find_tunnel(ipvs, AF_INET, raddr, 0);
|
||||
/* Only for known tunnel */
|
||||
if (!dest || dest->tun_type != IP_VS_CONN_F_TUNNEL_TYPE_IPIP)
|
||||
return NF_ACCEPT;
|
||||
offset += cih->ihl * 4;
|
||||
cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
|
||||
if (cih == NULL)
|
||||
|
|
|
@ -617,6 +617,35 @@ struct ip_vs_dest *ip_vs_find_real_service(struct netns_ipvs *ipvs, int af,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* Find real service record by <af,addr,tun_port>.
|
||||
* In case of multiple records with the same <af,addr,tun_port>, only
|
||||
* the first found record is returned.
|
||||
*
|
||||
* To be called under RCU lock.
|
||||
*/
|
||||
struct ip_vs_dest *ip_vs_find_tunnel(struct netns_ipvs *ipvs, int af,
|
||||
const union nf_inet_addr *daddr,
|
||||
__be16 tun_port)
|
||||
{
|
||||
struct ip_vs_dest *dest;
|
||||
unsigned int hash;
|
||||
|
||||
/* Check for "full" addressed entries */
|
||||
hash = ip_vs_rs_hashkey(af, daddr, tun_port);
|
||||
|
||||
hlist_for_each_entry_rcu(dest, &ipvs->rs_table[hash], d_list) {
|
||||
if (dest->tun_port == tun_port &&
|
||||
dest->af == af &&
|
||||
ip_vs_addr_equal(af, &dest->addr, daddr) &&
|
||||
IP_VS_DFWD_METHOD(dest) == IP_VS_CONN_F_TUNNEL) {
|
||||
/* HIT */
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Lookup destination by {addr,port} in the given service
|
||||
* Called under RCU lock.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue