Commit 2aa3c9f4 authored by Julian Anastasov's avatar Julian Anastasov Committed by Pablo Neira Ayuso
Browse files

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: default avatarJulian Anastasov <ja@ssi.bg>
Signed-off-by: default avatarSimon Horman <horms@verge.net.au>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 1da40ab6
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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);
+8 −0
Original line number Diff line number Diff line
@@ -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)
+29 −0
Original line number Diff line number Diff line
@@ -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.
 */