Add functions for getting/creating IPv6 connections and connection templates where these diverge significantly from the IPv4 versions. Signed-off-by: Julius R. Volz <juliusv@xxxxxxxxxx> 2 files changed, 211 insertions(+), 0 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index d04d5c6..6a58dff 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -816,6 +816,19 @@ extern struct ip_vs_conn *ip_vs_ct_in_get extern struct ip_vs_conn *ip_vs_conn_out_get (int protocol, __be32 s_addr, __be16 s_port, __be32 d_addr, __be16 d_port); +#ifdef CONFIG_IP_VS_IPV6 +extern struct ip_vs_conn * +ip_vs_conn_in_get_v6(int protocol, const struct in6_addr *s_addr, __be16 s_port, + const struct in6_addr *d_addr, __be16 d_port); +extern struct ip_vs_conn * +ip_vs_ct_in_get_v6(int protocol, const struct in6_addr *s_addr, __be16 s_port, + const struct in6_addr *d_addr, __be16 d_port); +extern struct ip_vs_conn * +ip_vs_conn_out_get_v6(int protocol, const struct in6_addr *s_addr, + __be16 s_port, const struct in6_addr *d_addr, + __be16 d_port); +#endif + /* put back the conn without restarting its timer */ static inline void __ip_vs_conn_put(struct ip_vs_conn *cp) { @@ -828,6 +841,15 @@ extern struct ip_vs_conn * ip_vs_conn_new(int proto, __be32 caddr, __be16 cport, __be32 vaddr, __be16 vport, __be32 daddr, __be16 dport, unsigned flags, struct ip_vs_dest *dest); + +#ifdef CONFIG_IP_VS_IPV6 +extern struct ip_vs_conn * +ip_vs_conn_new_v6(int proto, const struct in6_addr *caddr, __be16 cport, + const struct in6_addr *vaddr, __be16 vport, + const struct in6_addr *daddr, __be16 dport, unsigned flags, + struct ip_vs_dest *dest); +#endif + extern void ip_vs_conn_expire_now(struct ip_vs_conn *cp); extern const char * ip_vs_state_name(__u16 proto, int state); diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 4ee5dac..30e1ad2 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -236,6 +236,36 @@ static inline struct ip_vs_conn *__ip_vs_conn_in_get return NULL; } +#ifdef CONFIG_IP_VS_IPV6 +static inline struct ip_vs_conn *__ip_vs_conn_in_get_v6 +(int protocol, const struct in6_addr *s_addr, __be16 s_port, const struct in6_addr *d_addr, __be16 d_port) +{ + unsigned hash; + struct ip_vs_conn *cp; + + hash = ip_vs_conn_hashkey_v6(protocol, s_addr, s_port); + + ct_read_lock(hash); + + list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { + if (cp->af == AF_INET6 && + ipv6_addr_equal(s_addr, &cp->caddr.v6) && s_port==cp->cport && + ipv6_addr_equal(d_addr, &cp->vaddr.v6) && d_port==cp->vport && + ((!s_port) ^ (!(cp->flags & IP_VS_CONN_F_NO_CPORT))) && + protocol==cp->protocol) { + /* HIT */ + atomic_inc(&cp->refcnt); + ct_read_unlock(hash); + return cp; + } + } + + ct_read_unlock(hash); + + return NULL; +} +#endif + struct ip_vs_conn *ip_vs_conn_in_get (int protocol, __be32 s_addr, __be16 s_port, __be32 d_addr, __be16 d_port) { @@ -254,6 +284,26 @@ struct ip_vs_conn *ip_vs_conn_in_get return cp; } +#ifdef CONFIG_IP_VS_IPV6 +struct ip_vs_conn *ip_vs_conn_in_get_v6 +(int protocol, const struct in6_addr *s_addr, __be16 s_port, const struct in6_addr *d_addr, __be16 d_port) +{ + struct ip_vs_conn *cp; + + cp = __ip_vs_conn_in_get_v6(protocol, s_addr, s_port, d_addr, d_port); + if (!cp && atomic_read(&ip_vs_conn_no_cport_cnt)) + cp = __ip_vs_conn_in_get_v6(protocol, s_addr, 0, d_addr, d_port); + + IP_VS_DBG(9, "lookup/in %s " NIP6_FMT ":%d->" NIP6_FMT ":%d %s\n", + ip_vs_proto_name(protocol), + NIP6(*s_addr), ntohs(s_port), + NIP6(*d_addr), ntohs(d_port), + cp?"hit":"not hit"); + + return cp; +} +#endif + /* Get reference to connection template */ struct ip_vs_conn *ip_vs_ct_in_get (int protocol, __be32 s_addr, __be16 s_port, __be32 d_addr, __be16 d_port) @@ -290,6 +340,44 @@ struct ip_vs_conn *ip_vs_ct_in_get return cp; } +#ifdef CONFIG_IP_VS_IPV6 +/* Get reference to connection template */ +struct ip_vs_conn *ip_vs_ct_in_get_v6 +(int protocol, const struct in6_addr *s_addr, __be16 s_port, const struct in6_addr *d_addr, __be16 d_port) +{ + unsigned hash; + struct ip_vs_conn *cp; + + hash = ip_vs_conn_hashkey_v6(protocol, s_addr, s_port); + + ct_read_lock(hash); + + list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { + if (cp->af == AF_INET6 && + ipv6_addr_equal(s_addr, &cp->caddr.v6) && s_port==cp->cport && + ipv6_addr_equal(d_addr, &cp->vaddr.v6) && d_port==cp->vport && + cp->flags & IP_VS_CONN_F_TEMPLATE && + protocol==cp->protocol) { + /* HIT */ + atomic_inc(&cp->refcnt); + goto out; + } + } + cp = NULL; + + out: + ct_read_unlock(hash); + + IP_VS_DBG(9, "template lookup/in %s " NIP6_FMT ":%d->" NIP6_FMT ":%d %s\n", + ip_vs_proto_name(protocol), + NIP6(*s_addr), ntohs(s_port), + NIP6(*d_addr), ntohs(d_port), + cp?"hit":"not hit"); + + return cp; +} +#endif + /* * Gets ip_vs_conn associated with supplied parameters in the ip_vs_conn_tab. * Called for pkts coming from inside-to-OUTside. @@ -332,6 +420,44 @@ struct ip_vs_conn *ip_vs_conn_out_get return ret; } +#ifdef CONFIG_IP_VS_IPV6 +struct ip_vs_conn *ip_vs_conn_out_get_v6 +(int protocol, const struct in6_addr *s_addr, __be16 s_port, const struct in6_addr *d_addr, __be16 d_port) +{ + unsigned hash; + struct ip_vs_conn *cp, *ret=NULL; + + /* + * Check for "full" addressed entries + */ + hash = ip_vs_conn_hashkey_v6(protocol, d_addr, d_port); + + ct_read_lock(hash); + + list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { + if (cp->af == AF_INET6 && + ipv6_addr_equal(d_addr, &cp->caddr.v6) && d_port==cp->cport && + ipv6_addr_equal(s_addr, &cp->daddr.v6) && s_port==cp->dport && + protocol == cp->protocol) { + /* HIT */ + atomic_inc(&cp->refcnt); + ret = cp; + break; + } + } + + ct_read_unlock(hash); + + IP_VS_DBG(9, "lookup/out %s " NIP6_FMT ":%d->" NIP6_FMT ":%d %s\n", + ip_vs_proto_name(protocol), + NIP6(*s_addr), ntohs(s_port), + NIP6(*d_addr), ntohs(d_port), + ret?"hit":"not hit"); + + return ret; +} +#endif + /* * Put back the conn and restart its timer with its timeout @@ -766,6 +892,69 @@ ip_vs_conn_new(int proto, __be32 caddr, __be16 cport, __be32 vaddr, __be16 vport return cp; } +#ifdef CONFIG_IP_VS_IPV6 +struct ip_vs_conn * +ip_vs_conn_new_v6(int proto, const struct in6_addr *caddr, __be16 cport, + const struct in6_addr *vaddr, __be16 vport, + const struct in6_addr *daddr, __be16 dport, unsigned flags, + struct ip_vs_dest *dest) +{ + struct ip_vs_conn *cp; + struct ip_vs_protocol *pp = ip_vs_proto_get(proto); + + cp = kmem_cache_zalloc(ip_vs_conn_cachep, GFP_ATOMIC); + if (cp == NULL) { + IP_VS_ERR_RL("ip_vs_conn_new_v6: no memory available.\n"); + return NULL; + } + + INIT_LIST_HEAD(&cp->c_list); + setup_timer(&cp->timer, ip_vs_conn_expire, (unsigned long)cp); + cp->af = AF_INET6; + cp->protocol = proto; + cp->caddr.v6 = *caddr; + cp->cport = cport; + cp->vaddr.v6 = *vaddr; + cp->vport = vport; + cp->daddr.v6 = *daddr; + cp->dport = dport; + cp->flags = flags; + spin_lock_init(&cp->lock); + + /* + * Set the entry is referenced by the current thread before hashing + * it in the table, so that other thread run ip_vs_random_dropentry + * but cannot drop this entry. + */ + atomic_set(&cp->refcnt, 1); + + atomic_set(&cp->n_control, 0); + atomic_set(&cp->in_pkts, 0); + + atomic_inc(&ip_vs_conn_count); + if (flags & IP_VS_CONN_F_NO_CPORT) + atomic_inc(&ip_vs_conn_no_cport_cnt); + + /* Bind the connection with a destination server */ + ip_vs_bind_dest(cp, dest); + + /* Set its state and timeout */ + cp->state = 0; + cp->timeout = 3*HZ; + + /* Bind its packet transmitter */ + ip_vs_bind_xmit_v6(cp); + + if (unlikely(pp && atomic_read(&pp->appcnt))) + ip_vs_bind_app(cp, pp); + + /* Hash it in the ip_vs_conn_tab finally */ + ip_vs_conn_hash(cp); + + return cp; +} +#endif + /* * /proc/net/ip_vs_conn entries -- 1.5.3.6 -- To unsubscribe from this list: send the line "unsubscribe lvs-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html