Skip to content

Commit

Permalink
Merge branch 'ila-csum-neutral'
Browse files Browse the repository at this point in the history
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: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Apr 26, 2016
2 parents a843311 + 90bfe66 commit fab7b62
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 118 deletions.
7 changes: 7 additions & 0 deletions include/uapi/linux/ila.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ enum {
ILA_ATTR_IFINDEX, /* s32 */
ILA_ATTR_DIR, /* u32 */
ILA_ATTR_PAD,
ILA_ATTR_CSUM_MODE, /* u8 */

__ILA_ATTR_MAX,
};
Expand All @@ -35,4 +36,10 @@ enum {
#define ILA_DIR_IN (1 << 0)
#define ILA_DIR_OUT (1 << 1)

enum {
ILA_CSUM_ADJUST_TRANSPORT,
ILA_CSUM_NEUTRAL_MAP,
ILA_CSUM_NO_ACTION,
};

#endif /* _UAPI_LINUX_ILA_H */
79 changes: 76 additions & 3 deletions net/ipv6/ila/ila.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,76 @@
#include <net/protocol.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 {
__be64 locator;
__be64 locator_match;
struct ila_locator locator;
struct ila_locator locator_match;
__wsum csum_diff;
u8 csum_mode;
};

static inline __wsum compute_csum_diff8(const __be32 *from, const __be32 *to)
Expand All @@ -38,7 +104,14 @@ static inline __wsum compute_csum_diff8(const __be32 *from, const __be32 *to)
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);
void ila_lwt_fini(void);
Expand Down
81 changes: 76 additions & 5 deletions net/ipv6/ila/ila_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,52 @@

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;
else
return compute_csum_diff8((__be32 *)&ip6h->daddr,
return compute_csum_diff8((__be32 *)&iaddr->loc,
(__be32 *)&p->locator);
}

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;
}

void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p)
static void ila_csum_adjust_transport(struct sk_buff *skb,
struct ila_params *p)
{
__wsum diff;
struct ipv6hdr *ip6h = ipv6_hdr(skb);
struct ila_addr *iaddr = ila_a2i(&ip6h->daddr);
size_t nhoff = sizeof(struct ipv6hdr);

/* First update checksum */
switch (ip6h->nexthdr) {
case NEXTHDR_TCP:
if (likely(pskb_may_pull(skb, nhoff + sizeof(struct tcphdr)))) {
Expand Down Expand Up @@ -68,7 +100,46 @@ void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p)
}

/* 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)
Expand Down
49 changes: 36 additions & 13 deletions net/ipv6/ila/ila_lwt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))
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);

Expand All @@ -42,7 +42,7 @@ static int ila_input(struct sk_buff *skb)
if (skb->protocol != htons(ETH_P_IPV6))
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);

Expand All @@ -53,6 +53,7 @@ static int ila_input(struct sk_buff *skb)

static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = {
[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,
Expand All @@ -64,11 +65,28 @@ static int ila_build_state(struct net_device *dev, struct nlattr *nla,
size_t encap_len = sizeof(*p);
struct lwtunnel_state *newts;
const struct fib6_config *cfg6 = cfg;
struct ila_addr *iaddr;
int ret;

if (family != AF_INET6)
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,
ila_nl_policy);
if (ret < 0)
Expand All @@ -84,16 +102,19 @@ static int ila_build_state(struct net_device *dev, struct nlattr *nla,
newts->len = encap_len;
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
* know both the old locator and the new one.
*/
p->locator_match = *(__be64 *)&cfg6->fc_dst;
p->csum_diff = compute_csum_diff8(
(__be32 *)&p->locator_match, (__be32 *)&p->locator);
}
/* Precompute checksum difference for translation since we
* know both the old locator and the new one.
*/
p->locator_match = iaddr->loc;
p->csum_diff = compute_csum_diff8(
(__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->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT |
Expand All @@ -109,9 +130,11 @@ static int ila_fill_encap_info(struct sk_buff *skb,
{
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))
goto nla_put_failure;
if (nla_put_u64(skb, ILA_ATTR_CSUM_MODE, (__force u8)p->csum_mode))
goto nla_put_failure;

return 0;

Expand All @@ -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 *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 = {
Expand Down
Loading

0 comments on commit fab7b62

Please sign in to comment.