Commit fab7b629 authored by David S. Miller's avatar David S. Miller

Merge branch 'ila-csum-neutral'

Tom Herbert says:

====================
ila: Support for checksum neutral translations

This patch set updates ILA to support draft-herbert-nvo3-ila-02. The
primary addition is support checksum neutral ILA translation.
This allows address to be performed and still keep any transport
layer checksums that include the addresses in their pseudo header to
still be correct without the translator needing to parse L4.

Other items are:
   - Structures for ILA addresses, identifiers, locators
   - Disallow translation on non-ILA addresses (check by
     type in identifier).
   - Change xlat (nf_input) to translates solely based
     on matching locators not identifiers (since identifiers
     are not obfuscated by checksum neutral).
   - Side effect if above is that multiple ILA domains are
     supported. Each local locator can map to a different
     SIR address (ILA domain), and each domain defines its
     own identifier space.

Tested: Ran TCP_RR with 200 cnxs. ILA performance is slightly better
than previously since we are not longer parsing L4 for checksum
handling. I amd seeing about 1% performance overhead. Also ran
TCP_STREAM and tested non-ILA address (type=0) are not translated.

v2: Fix compilation errors
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents a843311d 90bfe662
...@@ -15,6 +15,7 @@ enum { ...@@ -15,6 +15,7 @@ enum {
ILA_ATTR_IFINDEX, /* s32 */ ILA_ATTR_IFINDEX, /* s32 */
ILA_ATTR_DIR, /* u32 */ ILA_ATTR_DIR, /* u32 */
ILA_ATTR_PAD, ILA_ATTR_PAD,
ILA_ATTR_CSUM_MODE, /* u8 */
__ILA_ATTR_MAX, __ILA_ATTR_MAX,
}; };
...@@ -35,4 +36,10 @@ enum { ...@@ -35,4 +36,10 @@ enum {
#define ILA_DIR_IN (1 << 0) #define ILA_DIR_IN (1 << 0)
#define ILA_DIR_OUT (1 << 1) #define ILA_DIR_OUT (1 << 1)
enum {
ILA_CSUM_ADJUST_TRANSPORT,
ILA_CSUM_NEUTRAL_MAP,
ILA_CSUM_NO_ACTION,
};
#endif /* _UAPI_LINUX_ILA_H */ #endif /* _UAPI_LINUX_ILA_H */
...@@ -23,10 +23,76 @@ ...@@ -23,10 +23,76 @@
#include <net/protocol.h> #include <net/protocol.h>
#include <uapi/linux/ila.h> #include <uapi/linux/ila.h>
struct ila_locator {
union {
__u8 v8[8];
__be16 v16[4];
__be32 v32[2];
__be64 v64;
};
};
struct ila_identifier {
union {
struct {
#if defined(__LITTLE_ENDIAN_BITFIELD)
u8 __space:4;
u8 csum_neutral:1;
u8 type:3;
#elif defined(__BIG_ENDIAN_BITFIELD)
u8 type:3;
u8 csum_neutral:1;
u8 __space:4;
#else
#error "Adjust your <asm/byteorder.h> defines"
#endif
u8 __space2[7];
};
__u8 v8[8];
__be16 v16[4];
__be32 v32[2];
__be64 v64;
};
};
enum {
ILA_ATYPE_IID = 0,
ILA_ATYPE_LUID,
ILA_ATYPE_VIRT_V4,
ILA_ATYPE_VIRT_UNI_V6,
ILA_ATYPE_VIRT_MULTI_V6,
ILA_ATYPE_RSVD_1,
ILA_ATYPE_RSVD_2,
ILA_ATYPE_RSVD_3,
};
#define CSUM_NEUTRAL_FLAG htonl(0x10000000)
struct ila_addr {
union {
struct in6_addr addr;
struct {
struct ila_locator loc;
struct ila_identifier ident;
};
};
};
static inline struct ila_addr *ila_a2i(struct in6_addr *addr)
{
return (struct ila_addr *)addr;
}
static inline bool ila_addr_is_ila(struct ila_addr *iaddr)
{
return (iaddr->ident.type != ILA_ATYPE_IID);
}
struct ila_params { struct ila_params {
__be64 locator; struct ila_locator locator;
__be64 locator_match; struct ila_locator locator_match;
__wsum csum_diff; __wsum csum_diff;
u8 csum_mode;
}; };
static inline __wsum compute_csum_diff8(const __be32 *from, const __be32 *to) static inline __wsum compute_csum_diff8(const __be32 *from, const __be32 *to)
...@@ -38,7 +104,14 @@ static inline __wsum compute_csum_diff8(const __be32 *from, const __be32 *to) ...@@ -38,7 +104,14 @@ static inline __wsum compute_csum_diff8(const __be32 *from, const __be32 *to)
return csum_partial(diff, sizeof(diff), 0); return csum_partial(diff, sizeof(diff), 0);
} }
void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p); static inline bool ila_csum_neutral_set(struct ila_identifier ident)
{
return !!(ident.csum_neutral);
}
void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p);
void ila_init_saved_csum(struct ila_params *p);
int ila_lwt_init(void); int ila_lwt_init(void);
void ila_lwt_fini(void); void ila_lwt_fini(void);
......
...@@ -15,20 +15,52 @@ ...@@ -15,20 +15,52 @@
static __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *p) static __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *p)
{ {
if (*(__be64 *)&ip6h->daddr == p->locator_match) struct ila_addr *iaddr = ila_a2i(&ip6h->daddr);
if (p->locator_match.v64)
return p->csum_diff; return p->csum_diff;
else else
return compute_csum_diff8((__be32 *)&ip6h->daddr, return compute_csum_diff8((__be32 *)&iaddr->loc,
(__be32 *)&p->locator); (__be32 *)&p->locator);
} }
void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p) static void ila_csum_do_neutral(struct ila_addr *iaddr,
struct ila_params *p)
{
__sum16 *adjust = (__force __sum16 *)&iaddr->ident.v16[3];
__wsum diff, fval;
/* Check if checksum adjust value has been cached */
if (p->locator_match.v64) {
diff = p->csum_diff;
} else {
diff = compute_csum_diff8((__be32 *)iaddr,
(__be32 *)&p->locator);
}
fval = (__force __wsum)(ila_csum_neutral_set(iaddr->ident) ?
~CSUM_NEUTRAL_FLAG : CSUM_NEUTRAL_FLAG);
diff = csum_add(diff, fval);
*adjust = ~csum_fold(csum_add(diff, csum_unfold(*adjust)));
/* Flip the csum-neutral bit. Either we are doing a SIR->ILA
* translation with ILA_CSUM_NEUTRAL_MAP as the csum_method
* and the C-bit is not set, or we are doing an ILA-SIR
* tranlsation and the C-bit is set.
*/
iaddr->ident.csum_neutral ^= 1;
}
static void ila_csum_adjust_transport(struct sk_buff *skb,
struct ila_params *p)
{ {
__wsum diff; __wsum diff;
struct ipv6hdr *ip6h = ipv6_hdr(skb); struct ipv6hdr *ip6h = ipv6_hdr(skb);
struct ila_addr *iaddr = ila_a2i(&ip6h->daddr);
size_t nhoff = sizeof(struct ipv6hdr); size_t nhoff = sizeof(struct ipv6hdr);
/* First update checksum */
switch (ip6h->nexthdr) { switch (ip6h->nexthdr) {
case NEXTHDR_TCP: case NEXTHDR_TCP:
if (likely(pskb_may_pull(skb, nhoff + sizeof(struct tcphdr)))) { if (likely(pskb_may_pull(skb, nhoff + sizeof(struct tcphdr)))) {
...@@ -68,7 +100,46 @@ void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p) ...@@ -68,7 +100,46 @@ void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p)
} }
/* Now change destination address */ /* Now change destination address */
*(__be64 *)&ip6h->daddr = p->locator; iaddr->loc = p->locator;
}
void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p)
{
struct ipv6hdr *ip6h = ipv6_hdr(skb);
struct ila_addr *iaddr = ila_a2i(&ip6h->daddr);
/* First deal with the transport checksum */
if (ila_csum_neutral_set(iaddr->ident)) {
/* C-bit is set in the locator indicating that this
* is a locator being translated to a SIR address.
* Perform (receiver) checksum-neutral translation.
*/
ila_csum_do_neutral(iaddr, p);
} else {
switch (p->csum_mode) {
case ILA_CSUM_ADJUST_TRANSPORT:
ila_csum_adjust_transport(skb, p);
break;
case ILA_CSUM_NEUTRAL_MAP:
ila_csum_do_neutral(iaddr, p);
break;
case ILA_CSUM_NO_ACTION:
break;
}
}
/* Now change destination address */
iaddr->loc = p->locator;
}
void ila_init_saved_csum(struct ila_params *p)
{
if (!p->locator_match.v64)
return;
p->csum_diff = compute_csum_diff8(
(__be32 *)&p->locator_match,
(__be32 *)&p->locator);
} }
static int __init ila_init(void) static int __init ila_init(void)
......
...@@ -26,7 +26,7 @@ static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb) ...@@ -26,7 +26,7 @@ static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb)
if (skb->protocol != htons(ETH_P_IPV6)) if (skb->protocol != htons(ETH_P_IPV6))
goto drop; goto drop;
update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate)); ila_update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate));
return dst->lwtstate->orig_output(net, sk, skb); return dst->lwtstate->orig_output(net, sk, skb);
...@@ -42,7 +42,7 @@ static int ila_input(struct sk_buff *skb) ...@@ -42,7 +42,7 @@ static int ila_input(struct sk_buff *skb)
if (skb->protocol != htons(ETH_P_IPV6)) if (skb->protocol != htons(ETH_P_IPV6))
goto drop; goto drop;
update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate)); ila_update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate));
return dst->lwtstate->orig_input(skb); return dst->lwtstate->orig_input(skb);
...@@ -53,6 +53,7 @@ static int ila_input(struct sk_buff *skb) ...@@ -53,6 +53,7 @@ static int ila_input(struct sk_buff *skb)
static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = { static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = {
[ILA_ATTR_LOCATOR] = { .type = NLA_U64, }, [ILA_ATTR_LOCATOR] = { .type = NLA_U64, },
[ILA_ATTR_CSUM_MODE] = { .type = NLA_U8, },
}; };
static int ila_build_state(struct net_device *dev, struct nlattr *nla, static int ila_build_state(struct net_device *dev, struct nlattr *nla,
...@@ -64,11 +65,28 @@ static int ila_build_state(struct net_device *dev, struct nlattr *nla, ...@@ -64,11 +65,28 @@ static int ila_build_state(struct net_device *dev, struct nlattr *nla,
size_t encap_len = sizeof(*p); size_t encap_len = sizeof(*p);
struct lwtunnel_state *newts; struct lwtunnel_state *newts;
const struct fib6_config *cfg6 = cfg; const struct fib6_config *cfg6 = cfg;
struct ila_addr *iaddr;
int ret; int ret;
if (family != AF_INET6) if (family != AF_INET6)
return -EINVAL; return -EINVAL;
if (cfg6->fc_dst_len < sizeof(struct ila_locator) + 1) {
/* Need to have full locator and at least type field
* included in destination
*/
return -EINVAL;
}
iaddr = (struct ila_addr *)&cfg6->fc_dst;
if (!ila_addr_is_ila(iaddr) || ila_csum_neutral_set(iaddr->ident)) {
/* Don't allow translation for a non-ILA address or checksum
* neutral flag to be set.
*/
return -EINVAL;
}
ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla, ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla,
ila_nl_policy); ila_nl_policy);
if (ret < 0) if (ret < 0)
...@@ -84,16 +102,19 @@ static int ila_build_state(struct net_device *dev, struct nlattr *nla, ...@@ -84,16 +102,19 @@ static int ila_build_state(struct net_device *dev, struct nlattr *nla,
newts->len = encap_len; newts->len = encap_len;
p = ila_params_lwtunnel(newts); p = ila_params_lwtunnel(newts);
p->locator = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]); p->locator.v64 = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]);
if (cfg6->fc_dst_len > sizeof(__be64)) {
/* Precompute checksum difference for translation since we /* Precompute checksum difference for translation since we
* know both the old locator and the new one. * know both the old locator and the new one.
*/ */
p->locator_match = *(__be64 *)&cfg6->fc_dst; p->locator_match = iaddr->loc;
p->csum_diff = compute_csum_diff8( p->csum_diff = compute_csum_diff8(
(__be32 *)&p->locator_match, (__be32 *)&p->locator); (__be32 *)&p->locator_match, (__be32 *)&p->locator);
}
if (tb[ILA_ATTR_CSUM_MODE])
p->csum_mode = nla_get_u8(tb[ILA_ATTR_CSUM_MODE]);
ila_init_saved_csum(p);
newts->type = LWTUNNEL_ENCAP_ILA; newts->type = LWTUNNEL_ENCAP_ILA;
newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT | newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT |
...@@ -109,9 +130,11 @@ static int ila_fill_encap_info(struct sk_buff *skb, ...@@ -109,9 +130,11 @@ static int ila_fill_encap_info(struct sk_buff *skb,
{ {
struct ila_params *p = ila_params_lwtunnel(lwtstate); struct ila_params *p = ila_params_lwtunnel(lwtstate);
if (nla_put_u64_64bit(skb, ILA_ATTR_LOCATOR, (__force u64)p->locator, if (nla_put_u64_64bit(skb, ILA_ATTR_LOCATOR, (__force u64)p->locator.v64,
ILA_ATTR_PAD)) ILA_ATTR_PAD))
goto nla_put_failure; goto nla_put_failure;
if (nla_put_u64(skb, ILA_ATTR_CSUM_MODE, (__force u8)p->csum_mode))
goto nla_put_failure;
return 0; return 0;
...@@ -130,7 +153,7 @@ static int ila_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) ...@@ -130,7 +153,7 @@ static int ila_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)
struct ila_params *a_p = ila_params_lwtunnel(a); struct ila_params *a_p = ila_params_lwtunnel(a);
struct ila_params *b_p = ila_params_lwtunnel(b); struct ila_params *b_p = ila_params_lwtunnel(b);
return (a_p->locator != b_p->locator); return (a_p->locator.v64 != b_p->locator.v64);
} }
static const struct lwtunnel_encap_ops ila_encap_ops = { static const struct lwtunnel_encap_ops ila_encap_ops = {
......
...@@ -11,13 +11,11 @@ ...@@ -11,13 +11,11 @@
struct ila_xlat_params { struct ila_xlat_params {
struct ila_params ip; struct ila_params ip;
__be64 identifier;
int ifindex; int ifindex;
unsigned int dir;
}; };
struct ila_map { struct ila_map {
struct ila_xlat_params p; struct ila_xlat_params xp;
struct rhash_head node; struct rhash_head node;
struct ila_map __rcu *next; struct ila_map __rcu *next;
struct rcu_head rcu; struct rcu_head rcu;
...@@ -66,31 +64,29 @@ static __always_inline void __ila_hash_secret_init(void) ...@@ -66,31 +64,29 @@ static __always_inline void __ila_hash_secret_init(void)
net_get_random_once(&hashrnd, sizeof(hashrnd)); net_get_random_once(&hashrnd, sizeof(hashrnd));
} }
static inline u32 ila_identifier_hash(__be64 identifier) static inline u32 ila_locator_hash(struct ila_locator loc)
{ {
u32 *v = (u32 *)&identifier; u32 *v = (u32 *)loc.v32;
return jhash_2words(v[0], v[1], hashrnd); return jhash_2words(v[0], v[1], hashrnd);
} }
static inline spinlock_t *ila_get_lock(struct ila_net *ilan, __be64 identifier) static inline spinlock_t *ila_get_lock(struct ila_net *ilan,
struct ila_locator loc)
{ {
return &ilan->locks[ila_identifier_hash(identifier) & ilan->locks_mask]; return &ilan->locks[ila_locator_hash(loc) & ilan->locks_mask];
} }
static inline int ila_cmp_wildcards(struct ila_map *ila, __be64 loc, static inline int ila_cmp_wildcards(struct ila_map *ila,
int ifindex, unsigned int dir) struct ila_addr *iaddr, int ifindex)
{ {
return (ila->p.ip.locator_match && ila->p.ip.locator_match != loc) || return (ila->xp.ifindex && ila->xp.ifindex != ifindex);
(ila->p.ifindex && ila->p.ifindex != ifindex) ||
!(ila->p.dir & dir);
} }
static inline int ila_cmp_params(struct ila_map *ila, struct ila_xlat_params *p) static inline int ila_cmp_params(struct ila_map *ila,
struct ila_xlat_params *xp)
{ {
return (ila->p.ip.locator_match != p->ip.locator_match) || return (ila->xp.ifindex != xp->ifindex);
(ila->p.ifindex != p->ifindex) ||
(ila->p.dir != p->dir);
} }
static int ila_cmpfn(struct rhashtable_compare_arg *arg, static int ila_cmpfn(struct rhashtable_compare_arg *arg,
...@@ -98,17 +94,14 @@ static int ila_cmpfn(struct rhashtable_compare_arg *arg, ...@@ -98,17 +94,14 @@ static int ila_cmpfn(struct rhashtable_compare_arg *arg,
{ {
const struct ila_map *ila = obj; const struct ila_map *ila = obj;
return (ila->p.identifier != *(__be64 *)arg->key); return (ila->xp.ip.locator_match.v64 != *(__be64 *)arg->key);
} }
static inline int ila_order(struct ila_map *ila) static inline int ila_order(struct ila_map *ila)
{ {
int score = 0; int score = 0;
if (ila->p.ip.locator_match) if (ila->xp.ifindex)
score += 1 << 0;
if (ila->p.ifindex)
score += 1 << 1; score += 1 << 1;
return score; return score;
...@@ -117,7 +110,7 @@ static inline int ila_order(struct ila_map *ila) ...@@ -117,7 +110,7 @@ static inline int ila_order(struct ila_map *ila)
static const struct rhashtable_params rht_params = { static const struct rhashtable_params rht_params = {
.nelem_hint = 1024, .nelem_hint = 1024,
.head_offset = offsetof(struct ila_map, node), .head_offset = offsetof(struct ila_map, node),
.key_offset = offsetof(struct ila_map, p.identifier), .key_offset = offsetof(struct ila_map, xp.ip.locator_match),
.key_len = sizeof(u64), /* identifier */ .key_len = sizeof(u64), /* identifier */
.max_size = 1048576, .max_size = 1048576,
.min_size = 256, .min_size = 256,
...@@ -136,50 +129,45 @@ static struct genl_family ila_nl_family = { ...@@ -136,50 +129,45 @@ static struct genl_family ila_nl_family = {
}; };
static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = { static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = {
[ILA_ATTR_IDENTIFIER] = { .type = NLA_U64, },
[ILA_ATTR_LOCATOR] = { .type = NLA_U64, }, [ILA_ATTR_LOCATOR] = { .type = NLA_U64, },
[ILA_ATTR_LOCATOR_MATCH] = { .type = NLA_U64, }, [ILA_ATTR_LOCATOR_MATCH] = { .type = NLA_U64, },
[ILA_ATTR_IFINDEX] = { .type = NLA_U32, }, [ILA_ATTR_IFINDEX] = { .type = NLA_U32, },
[ILA_ATTR_DIR] = { .type = NLA_U32, }, [ILA_ATTR_CSUM_MODE] = { .type = NLA_U8, },
}; };
static int parse_nl_config(struct genl_info *info, static int parse_nl_config(struct genl_info *info,
struct ila_xlat_params *p) struct ila_xlat_params *xp)
{ {
memset(p, 0, sizeof(*p)); memset(xp, 0, sizeof(*xp));
if (info->attrs[ILA_ATTR_IDENTIFIER])
p->identifier = (__force __be64)nla_get_u64(
info->attrs[ILA_ATTR_IDENTIFIER]);
if (info->attrs[ILA_ATTR_LOCATOR]) if (info->attrs[ILA_ATTR_LOCATOR])
p->ip.locator = (__force __be64)nla_get_u64( xp->ip.locator.v64 = (__force __be64)nla_get_u64(
info->attrs[ILA_ATTR_LOCATOR]); info->attrs[ILA_ATTR_LOCATOR]);
if (info->attrs[ILA_ATTR_LOCATOR_MATCH]) if (info->attrs[ILA_ATTR_LOCATOR_MATCH])
p->ip.locator_match = (__force __be64)nla_get_u64( xp->ip.locator_match.v64 = (__force __be64)nla_get_u64(
info->attrs[ILA_ATTR_LOCATOR_MATCH]); info->attrs[ILA_ATTR_LOCATOR_MATCH]);
if (info->attrs[ILA_ATTR_IFINDEX]) if (info->attrs[ILA_ATTR_CSUM_MODE])
p->ifindex = nla_get_s32(info->attrs[ILA_ATTR_IFINDEX]); xp->ip.csum_mode = nla_get_u8(info->attrs[ILA_ATTR_CSUM_MODE]);
if (info->attrs[ILA_ATTR_DIR]) if (info->attrs[ILA_ATTR_IFINDEX])
p->dir = nla_get_u32(info->attrs[ILA_ATTR_DIR]); xp->ifindex = nla_get_s32(info->attrs[ILA_ATTR_IFINDEX]);
return 0; return 0;
} }
/* Must be called with rcu readlock */ /* Must be called with rcu readlock */
static inline struct ila_map *ila_lookup_wildcards(__be64 id, __be64 loc, static inline struct ila_map *ila_lookup_wildcards(struct ila_addr *iaddr,
int ifindex, int ifindex,
unsigned int dir,
struct ila_net *ilan) struct ila_net *ilan)
{ {
struct ila_map *ila; struct ila_map *ila;
ila = rhashtable_lookup_fast(&ilan->rhash_table, &id, rht_params); ila = rhashtable_lookup_fast(&ilan->rhash_table, &iaddr->loc,
rht_params);
while (ila) { while (ila) {
if (!ila_cmp_wildcards(ila, loc, ifindex, dir)) if (!ila_cmp_wildcards(ila, iaddr, ifindex))
return ila; return ila;
ila = rcu_access_pointer(ila->next); ila = rcu_access_pointer(ila->next);
} }
...@@ -188,15 +176,16 @@ static inline struct ila_map *ila_lookup_wildcards(__be64 id, __be64 loc, ...@@ -188,15 +176,16 @@ static inline struct ila_map *ila_lookup_wildcards(__be64 id, __be64 loc,
} }
/* Must be called with rcu readlock */ /* Must be called with rcu readlock */
static inline struct ila_map *ila_lookup_by_params(struct ila_xlat_params *p, static inline struct ila_map *ila_lookup_by_params(struct ila_xlat_params *xp,
struct ila_net *ilan) struct ila_net *ilan)
{ {
struct ila_map *ila; struct ila_map *ila;
ila = rhashtable_lookup_fast(&ilan->rhash_table, &p->identifier, ila = rhashtable_lookup_fast(&ilan->rhash_table,
&xp->ip.locator_match,
rht_params); rht_params);
while (ila) { while (ila) {
if (!ila_cmp_params(ila, p)) if (!ila_cmp_params(ila, xp))
return ila; return ila;
ila = rcu_access_pointer(ila->next); ila = rcu_access_pointer(ila->next);
} }
...@@ -221,14 +210,14 @@ static void ila_free_cb(void *ptr, void *arg) ...@@ -221,14 +210,14 @@ static void ila_free_cb(void *ptr, void *arg)
} }
} }
static int ila_xlat_addr(struct sk_buff *skb, int dir); static int ila_xlat_addr(struct sk_buff *skb);
static unsigned int static unsigned int
ila_nf_input(void *priv, ila_nf_input(void *priv,
struct sk_buff *skb, struct sk_buff *skb,
const struct nf_hook_state *state) const struct nf_hook_state *state)
{ {
ila_xlat_addr(skb, ILA_DIR_IN); ila_xlat_addr(skb);
return NF_ACCEPT; return NF_ACCEPT;
} }
...@@ -241,11 +230,11 @@ static struct nf_hook_ops ila_nf_hook_ops[] __read_mostly = { ...@@ -241,11 +230,11 @@ static struct nf_hook_ops ila_nf_hook_ops[] __read_mostly = {
}, },
}; };
static int ila_add_mapping(struct net *net, struct ila_xlat_params *p) static int ila_add_mapping(struct net *net, struct ila_xlat_params *xp)
{ {
struct ila_net *ilan = net_generic(net, ila_net_id); struct ila_net *ilan = net_generic(net, ila_net_id);
struct ila_map *ila, *head; struct ila_map *ila, *head;
spinlock_t *lock = ila_get_lock(ilan, p->identifier); spinlock_t *lock = ila_get_lock(ilan, xp->ip.locator_match);
int err = 0, order; int err = 0, order;
if (!ilan->hooks_registered) { if (!ilan->hooks_registered) {
...@@ -264,22 +253,16 @@ static int ila_add_mapping(struct net *net, struct ila_xlat_params *p) ...@@ -264,22 +253,16 @@ static int ila_add_mapping(struct net *net, struct ila_xlat_params *p)
if (!ila) if (!ila)
return -ENOMEM; return -ENOMEM;
ila->p = *p; ila_init_saved_csum(&xp->ip);
if (p->ip.locator_match) { ila->xp = *xp;
/* Precompute checksum difference for translation since we
* know both the old identifier and the new one.
*/
ila->p.ip.csum_diff = compute_csum_diff8(
(__be32 *)&p->ip.locator_match,
(__be32 *)&p->ip.locator);
}
order = ila_order(ila); order = ila_order(ila);
spin_lock(lock); spin_lock(lock);
head = rhashtable_lookup_fast(&ilan->rhash_table, &p->identifier, head = rhashtable_lookup_fast(&ilan->rhash_table,
&xp->ip.locator_match,
rht_params); rht_params);
if (!head) { if (!head) {
/* New entry for the rhash_table */ /* New entry for the rhash_table */
...@@ -289,7 +272,7 @@ static int ila_add_mapping(struct net *net, struct ila_xlat_params *p) ...@@ -289,7 +272,7 @@ static int ila_add_mapping(struct net *net, struct ila_xlat_params *p)
struct ila_map *tila = head, *prev = NULL; struct ila_map *tila = head, *prev = NULL;
do { do {
if (!ila_cmp_params(tila, p)) { if (!ila_cmp_params(tila, xp)) {
err = -EEXIST; err = -EEXIST;
goto out; goto out;
} }
...@@ -326,23 +309,23 @@ static int ila_add_mapping(struct net *net, struct ila_xlat_params *p) ...@@ -326,23 +309,23 @@ static int ila_add_mapping(struct net *net, struct ila_xlat_params *p)
return err; return err;
} }
static int ila_del_mapping(struct net *net, struct ila_xlat_params *p) static int ila_del_mapping(struct net *net, struct ila_xlat_params *xp)
{ {
struct ila_net *ilan = net_generic(net, ila_net_id); struct ila_net *ilan = net_generic(net, ila_net_id);
struct ila_map *ila, *head, *prev; struct ila_map *ila, *head, *prev;
spinlock_t *lock = ila_get_lock(ilan, p->identifier); spinlock_t *lock = ila_get_lock(ilan, xp->ip.locator_match);
int err = -ENOENT; int err = -ENOENT;
spin_lock(lock); spin_lock(lock);
head = rhashtable_lookup_fast(&ilan->rhash_table, head = rhashtable_lookup_fast(&ilan->rhash_table,
&p->identifier, rht_params); &xp->ip.locator_match, rht_params);
ila = head; ila = head;
prev = NULL; prev = NULL;
while (ila) { while (ila) {
if (ila_cmp_params(ila, p)) { if (ila_cmp_params(ila, xp)) {
prev = ila; prev = ila;
ila = rcu_dereference_protected(ila->next, ila = rcu_dereference_protected(ila->next,
lockdep_is_held(lock)); lockdep_is_held(lock));
...@@ -404,31 +387,28 @@ static int ila_nl_cmd_add_mapping(struct sk_buff *skb, struct genl_info *info) ...@@ -404,31 +387,28 @@ static int ila_nl_cmd_add_mapping(struct sk_buff *skb, struct genl_info *info)
static int ila_nl_cmd_del_mapping(struct sk_buff *skb, struct genl_info *info) static int ila_nl_cmd_del_mapping(struct sk_buff *skb, struct genl_info *info)
{ {
struct net *net = genl_info_net(info); struct net *net = genl_info_net(info);
struct ila_xlat_params p; struct ila_xlat_params xp;
int err; int err;
err = parse_nl_config(info, &p); err = parse_nl_config(info, &xp);
if (err) if (err)
return err; return err;
ila_del_mapping(net, &p); ila_del_mapping(net, &xp);
return 0; return 0;
} }
static int ila_fill_info(struct ila_map *ila, struct sk_buff *msg) static int ila_fill_info(struct ila_map *ila, struct sk_buff *msg)
{ {
if (nla_put_u64_64bit(msg, ILA_ATTR_IDENTIFIER, if (nla_put_u64_64bit(msg, ILA_ATTR_LOCATOR,
(__force u64)ila->p.identifier, (__force u64)ila->xp.ip.locator.v64,
ILA_ATTR_PAD) ||
nla_put_u64_64bit(msg, ILA_ATTR_LOCATOR,
(__force u64)ila->p.ip.locator,
ILA_ATTR_PAD) || ILA_ATTR_PAD) ||
nla_put_u64_64bit(msg, ILA_ATTR_LOCATOR_MATCH, nla_put_u64_64bit(msg, ILA_ATTR_LOCATOR_MATCH,
(__force u64)ila->p.ip.locator_match, (__force u64)ila->xp.ip.locator_match.v64,
ILA_ATTR_PAD) || ILA_ATTR_PAD) ||
nla_put_s32(msg, ILA_ATTR_IFINDEX, ila->p.ifindex) || nla_put_s32(msg, ILA_ATTR_IFINDEX, ila->xp.ifindex) ||
nla_put_u32(msg, ILA_ATTR_DIR, ila->p.dir)) nla_put_u32(msg, ILA_ATTR_CSUM_MODE, ila->xp.ip.csum_mode))
return -1; return -1;
return 0; return 0;
...@@ -460,11 +440,11 @@ static int ila_nl_cmd_get_mapping(struct sk_buff *skb, struct genl_info *info) ...@@ -460,11 +440,11 @@ static int ila_nl_cmd_get_mapping(struct sk_buff *skb, struct genl_info *info)
struct net *net = genl_info_net(info); struct net *net = genl_info_net(info);
struct ila_net *ilan = net_generic(net, ila_net_id); struct ila_net *ilan = net_generic(net, ila_net_id);
struct sk_buff *msg; struct sk_buff *msg;
struct ila_xlat_params p; struct ila_xlat_params xp;
struct ila_map *ila; struct ila_map *ila;
int ret; int ret;
ret = parse_nl_config(info, &p); ret = parse_nl_config(info, &xp);
if (ret) if (ret)
return ret; return ret;
...@@ -474,7 +454,7 @@ static int ila_nl_cmd_get_mapping(struct sk_buff *skb, struct genl_info *info) ...@@ -474,7 +454,7 @@ static int ila_nl_cmd_get_mapping(struct sk_buff *skb, struct genl_info *info)
rcu_read_lock(); rcu_read_lock();
ila = ila_lookup_by_params(&p, ilan); ila = ila_lookup_by_params(&xp, ilan);
if (ila) { if (ila) {
ret = ila_dump_info(ila, ret = ila_dump_info(ila,
info->snd_portid, info->snd_portid,
...@@ -617,45 +597,32 @@ static struct pernet_operations ila_net_ops = { ...@@ -617,45 +597,32 @@ static struct pernet_operations ila_net_ops = {
.size = sizeof(struct ila_net), .size = sizeof(struct ila_net),
}; };
static int ila_xlat_addr(struct sk_buff *skb, int dir) static int ila_xlat_addr(struct sk_buff *skb)
{ {
struct ila_map *ila; struct ila_map *ila;
struct ipv6hdr *ip6h = ipv6_hdr(skb); struct ipv6hdr *ip6h = ipv6_hdr(skb);
struct net *net = dev_net(skb->dev); struct net *net = dev_net(skb->dev);
struct ila_net *ilan = net_generic(net, ila_net_id); struct ila_net *ilan = net_generic(net, ila_net_id);
__be64 identifier, locator_match; struct ila_addr *iaddr = ila_a2i(&ip6h->daddr);
size_t nhoff;
/* Assumes skb contains a valid IPv6 header that is pulled */ /* Assumes skb contains a valid IPv6 header that is pulled */
identifier = *(__be64 *)&ip6h->daddr.in6_u.u6_addr8[8]; if (!ila_addr_is_ila(iaddr)) {
locator_match = *(__be64 *)&ip6h->daddr.in6_u.u6_addr8[0]; /* Type indicates this is not an ILA address */
nhoff = sizeof(struct ipv6hdr); return 0;
}
rcu_read_lock(); rcu_read_lock();
ila = ila_lookup_wildcards(identifier, locator_match, ila = ila_lookup_wildcards(iaddr, skb->dev->ifindex, ilan);
skb->dev->ifindex, dir, ilan);
if (ila) if (ila)
update_ipv6_locator(skb, &ila->p.ip); ila_update_ipv6_locator(skb, &ila->xp.ip);
rcu_read_unlock(); rcu_read_unlock();
return 0; return 0;
} }
int ila_xlat_incoming(struct sk_buff *skb)
{
return ila_xlat_addr(skb, ILA_DIR_IN);
}
EXPORT_SYMBOL(ila_xlat_incoming);
int ila_xlat_outgoing(struct sk_buff *skb)
{
return ila_xlat_addr(skb, ILA_DIR_OUT);
}
EXPORT_SYMBOL(ila_xlat_outgoing);
int ila_xlat_init(void) int ila_xlat_init(void)
{ {
int ret; int ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment