ila: add checksum neutral ILA translations
authorTom Herbert <tom@herbertland.com>
Sat, 23 Apr 2016 18:46:57 +0000 (11:46 -0700)
committerDavid S. Miller <davem@davemloft.net>
Tue, 26 Apr 2016 05:27:07 +0000 (01:27 -0400)
Support checksum neutral ILA as described in the ILA draft. The low
order 16 bits of the identifier are used to contain the checksum
adjustment value.

The csum-mode parameter is added to described checksum processing. There
are three values:
 - adjust transport checksum (previous behavior)
 - do checksum neutral mapping
 - do nothing

On output the csum-mode in the ila_params is checked and acted on. If
mode is checksum neutral mapping then to mapping and set C-bit.

On input, C-bit is checked. If it is set checksum-netural mapping is
done (regardless of csum-mode in ila params) and C-bit will be cleared.
If it is not set then action in csum-mode is taken.

Signed-off-by: Tom Herbert <tom@herbertland.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/uapi/linux/ila.h
net/ipv6/ila/ila.h
net/ipv6/ila/ila_common.c
net/ipv6/ila/ila_lwt.c
net/ipv6/ila/ila_xlat.c

index cd97951..948c0a9 100644 (file)
@@ -15,6 +15,7 @@ enum {
        ILA_ATTR_IFINDEX,                       /* s32 */
        ILA_ATTR_DIR,                           /* u32 */
        ILA_ATTR_PAD,
+       ILA_ATTR_CSUM_MODE,                     /* u8 */
 
        __ILA_ATTR_MAX,
 };
@@ -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 */
index f532967..d08fd2d 100644 (file)
@@ -36,11 +36,13 @@ struct ila_identifier {
        union {
                struct {
 #if defined(__LITTLE_ENDIAN_BITFIELD)
-                       u8 __space:5;
+                       u8 __space:4;
+                       u8 csum_neutral:1;
                        u8 type:3;
 #elif defined(__BIG_ENDIAN_BITFIELD)
                        u8 type:3;
-                       u8 __space:5;
+                       u8 csum_neutral:1;
+                       u8 __space:4;
 #else
 #error  "Adjust your <asm/byteorder.h> defines"
 #endif
@@ -64,6 +66,8 @@ enum {
        ILA_ATYPE_RSVD_3,
 };
 
+#define CSUM_NEUTRAL_FLAG      htonl(0x10000000)
+
 struct ila_addr {
        union {
                struct in6_addr addr;
@@ -88,6 +92,7 @@ struct ila_params {
        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)
@@ -99,8 +104,15 @@ static inline __wsum compute_csum_diff8(const __be32 *from, const __be32 *to)
        return csum_partial(diff, sizeof(diff), 0);
 }
 
+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);
 int ila_xlat_init(void);
index c3078d0..0e94042 100644 (file)
@@ -17,21 +17,50 @@ static __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *p)
 {
        struct ila_addr *iaddr = ila_a2i(&ip6h->daddr);
 
-       if (iaddr->loc.v64 == p->locator_match.v64)
+       if (p->locator_match.v64)
                return p->csum_diff;
        else
                return compute_csum_diff8((__be32 *)&iaddr->loc,
                                          (__be32 *)&p->locator);
 }
 
-void ila_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;
        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)))) {
@@ -74,6 +103,45 @@ void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p)
        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)
 {
        int ret;
index de7f6d7..4985e1a 100644 (file)
@@ -53,6 +53,7 @@ drop:
 
 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,
@@ -79,8 +80,10 @@ static int ila_build_state(struct net_device *dev, struct nlattr *nla,
 
        iaddr = (struct ila_addr *)&cfg6->fc_dst;
 
-       if (!ila_addr_is_ila(iaddr)) {
-               /* Don't allow setting a translation for a non-ILA address */
+       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;
        }
 
@@ -108,6 +111,11 @@ static int ila_build_state(struct net_device *dev, struct nlattr *nla,
        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 |
                        LWTUNNEL_STATE_INPUT_REDIRECT;
@@ -125,6 +133,8 @@ static int ila_fill_encap_info(struct sk_buff *skb,
        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;
 
index 2e6cb97..a90e572 100644 (file)
@@ -132,6 +132,7 @@ static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = {
        [ILA_ATTR_LOCATOR] = { .type = NLA_U64, },
        [ILA_ATTR_LOCATOR_MATCH] = { .type = NLA_U64, },
        [ILA_ATTR_IFINDEX] = { .type = NLA_U32, },
+       [ILA_ATTR_CSUM_MODE] = { .type = NLA_U8, },
 };
 
 static int parse_nl_config(struct genl_info *info,
@@ -147,6 +148,9 @@ static int parse_nl_config(struct genl_info *info,
                xp->ip.locator_match.v64 = (__force __be64)nla_get_u64(
                        info->attrs[ILA_ATTR_LOCATOR_MATCH]);
 
+       if (info->attrs[ILA_ATTR_CSUM_MODE])
+               xp->ip.csum_mode = nla_get_u8(info->attrs[ILA_ATTR_CSUM_MODE]);
+
        if (info->attrs[ILA_ATTR_IFINDEX])
                xp->ifindex = nla_get_s32(info->attrs[ILA_ATTR_IFINDEX]);
 
@@ -249,14 +253,9 @@ static int ila_add_mapping(struct net *net, struct ila_xlat_params *xp)
        if (!ila)
                return -ENOMEM;
 
-       ila->xp = *xp;
+       ila_init_saved_csum(&xp->ip);
 
-       /* Precompute checksum difference for translation since we
-        * know both the old identifier and the new one.
-        */
-       ila->xp.ip.csum_diff = compute_csum_diff8(
-               (__be32 *)&xp->ip.locator_match,
-               (__be32 *)&xp->ip.locator);
+       ila->xp = *xp;
 
        order = ila_order(ila);
 
@@ -408,7 +407,8 @@ static int ila_fill_info(struct ila_map *ila, struct sk_buff *msg)
            nla_put_u64_64bit(msg, ILA_ATTR_LOCATOR_MATCH,
                              (__force u64)ila->xp.ip.locator_match.v64,
                              ILA_ATTR_PAD) ||
-           nla_put_s32(msg, ILA_ATTR_IFINDEX, ila->xp.ifindex))
+           nla_put_s32(msg, ILA_ATTR_IFINDEX, ila->xp.ifindex) ||
+           nla_put_u32(msg, ILA_ATTR_CSUM_MODE, ila->xp.ip.csum_mode))
                return -1;
 
        return 0;