Commit 84287bb3 authored by Tom Herbert's avatar Tom Herbert Committed by David S. Miller

ila: add checksum neutral map auto

Add checksum neutral auto that performs checksum neutral mapping
without using the C-bit. This is enabled by configuration of
a mapping.

The checksum neutral function has been split into
ila_csum_do_neutral_fmt and ila_csum_do_neutral_nofmt. The former
handles the C-bit and includes it in the adjustment value. The latter
just sets the adjustment value on the locator diff only.

Added configuration for checksum neutral map aut in ila_lwt
and ila_xlat.
Signed-off-by: default avatarTom Herbert <tom@quantonium.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 80661e76
...@@ -41,6 +41,7 @@ enum { ...@@ -41,6 +41,7 @@ enum {
ILA_CSUM_ADJUST_TRANSPORT, ILA_CSUM_ADJUST_TRANSPORT,
ILA_CSUM_NEUTRAL_MAP, ILA_CSUM_NEUTRAL_MAP,
ILA_CSUM_NO_ACTION, ILA_CSUM_NO_ACTION,
ILA_CSUM_NEUTRAL_MAP_AUTO,
}; };
#endif /* _UAPI_LINUX_ILA_H */ #endif /* _UAPI_LINUX_ILA_H */
...@@ -37,8 +37,8 @@ static __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *p) ...@@ -37,8 +37,8 @@ static __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *p)
return get_csum_diff_iaddr(ila_a2i(&ip6h->daddr), p); return get_csum_diff_iaddr(ila_a2i(&ip6h->daddr), p);
} }
static void ila_csum_do_neutral(struct ila_addr *iaddr, static void ila_csum_do_neutral_fmt(struct ila_addr *iaddr,
struct ila_params *p) struct ila_params *p)
{ {
__sum16 *adjust = (__force __sum16 *)&iaddr->ident.v16[3]; __sum16 *adjust = (__force __sum16 *)&iaddr->ident.v16[3];
__wsum diff, fval; __wsum diff, fval;
...@@ -60,13 +60,23 @@ static void ila_csum_do_neutral(struct ila_addr *iaddr, ...@@ -60,13 +60,23 @@ static void ila_csum_do_neutral(struct ila_addr *iaddr,
iaddr->ident.csum_neutral ^= 1; iaddr->ident.csum_neutral ^= 1;
} }
static void ila_csum_adjust_transport(struct sk_buff *skb, static void ila_csum_do_neutral_nofmt(struct ila_addr *iaddr,
struct ila_params *p) struct ila_params *p)
{ {
__sum16 *adjust = (__force __sum16 *)&iaddr->ident.v16[3];
__wsum diff; __wsum diff;
struct ipv6hdr *ip6h = ipv6_hdr(skb);
struct ila_addr *iaddr = ila_a2i(&ip6h->daddr); diff = get_csum_diff_iaddr(iaddr, p);
*adjust = ~csum_fold(csum_add(diff, csum_unfold(*adjust)));
}
static void ila_csum_adjust_transport(struct sk_buff *skb,
struct ila_params *p)
{
size_t nhoff = sizeof(struct ipv6hdr); size_t nhoff = sizeof(struct ipv6hdr);
struct ipv6hdr *ip6h = ipv6_hdr(skb);
__wsum diff;
switch (ip6h->nexthdr) { switch (ip6h->nexthdr) {
case NEXTHDR_TCP: case NEXTHDR_TCP:
...@@ -105,36 +115,39 @@ static void ila_csum_adjust_transport(struct sk_buff *skb, ...@@ -105,36 +115,39 @@ static void ila_csum_adjust_transport(struct sk_buff *skb,
} }
break; break;
} }
/* Now change destination address */
iaddr->loc = p->locator;
} }
void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p, void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p,
bool set_csum_neutral) bool sir2ila)
{ {
struct ipv6hdr *ip6h = ipv6_hdr(skb); struct ipv6hdr *ip6h = ipv6_hdr(skb);
struct ila_addr *iaddr = ila_a2i(&ip6h->daddr); struct ila_addr *iaddr = ila_a2i(&ip6h->daddr);
/* First deal with the transport checksum */ switch (p->csum_mode) {
if (ila_csum_neutral_set(iaddr->ident)) { case ILA_CSUM_ADJUST_TRANSPORT:
/* C-bit is set in the locator indicating that this ila_csum_adjust_transport(skb, p);
* is a locator being translated to a SIR address. break;
* Perform (receiver) checksum-neutral translation. case ILA_CSUM_NEUTRAL_MAP:
*/ if (sir2ila) {
if (!set_csum_neutral) if (WARN_ON(ila_csum_neutral_set(iaddr->ident))) {
ila_csum_do_neutral(iaddr, p); /* Checksum flag should never be
} else { * set in a formatted SIR address.
switch (p->csum_mode) { */
case ILA_CSUM_ADJUST_TRANSPORT: break;
ila_csum_adjust_transport(skb, p); }
break; } else if (!ila_csum_neutral_set(iaddr->ident)) {
case ILA_CSUM_NEUTRAL_MAP: /* ILA to SIR translation and C-bit isn't
ila_csum_do_neutral(iaddr, p); * set so we're good.
break; */
case ILA_CSUM_NO_ACTION:
break; break;
} }
ila_csum_do_neutral_fmt(iaddr, p);
break;
case ILA_CSUM_NEUTRAL_MAP_AUTO:
ila_csum_do_neutral_nofmt(iaddr, p);
break;
case ILA_CSUM_NO_ACTION:
break;
} }
/* Now change destination address */ /* Now change destination address */
......
...@@ -127,6 +127,7 @@ static int ila_build_state(struct nlattr *nla, ...@@ -127,6 +127,7 @@ static int ila_build_state(struct nlattr *nla,
struct lwtunnel_state *newts; struct lwtunnel_state *newts;
const struct fib6_config *cfg6 = cfg; const struct fib6_config *cfg6 = cfg;
struct ila_addr *iaddr; struct ila_addr *iaddr;
u8 csum_mode = ILA_CSUM_NO_ACTION;
int ret; int ret;
if (family != AF_INET6) if (family != AF_INET6)
...@@ -139,15 +140,6 @@ static int ila_build_state(struct nlattr *nla, ...@@ -139,15 +140,6 @@ static int ila_build_state(struct nlattr *nla,
return -EINVAL; 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, extack); ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla, ila_nl_policy, extack);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -155,6 +147,19 @@ static int ila_build_state(struct nlattr *nla, ...@@ -155,6 +147,19 @@ static int ila_build_state(struct nlattr *nla,
if (!tb[ILA_ATTR_LOCATOR]) if (!tb[ILA_ATTR_LOCATOR])
return -EINVAL; return -EINVAL;
iaddr = (struct ila_addr *)&cfg6->fc_dst;
if (tb[ILA_ATTR_CSUM_MODE])
csum_mode = nla_get_u8(tb[ILA_ATTR_CSUM_MODE]);
if (csum_mode == ILA_CSUM_NEUTRAL_MAP &&
ila_csum_neutral_set(iaddr->ident)) {
/* Don't allow translation if checksum neutral bit is
* configured and it's set in the SIR address.
*/
return -EINVAL;
}
newts = lwtunnel_state_alloc(sizeof(*ilwt)); newts = lwtunnel_state_alloc(sizeof(*ilwt));
if (!newts) if (!newts)
return -ENOMEM; return -ENOMEM;
...@@ -168,17 +173,13 @@ static int ila_build_state(struct nlattr *nla, ...@@ -168,17 +173,13 @@ static int ila_build_state(struct nlattr *nla,
p = ila_params_lwtunnel(newts); p = ila_params_lwtunnel(newts);
p->csum_mode = csum_mode;
p->locator.v64 = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]); p->locator.v64 = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]);
/* 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 = iaddr->loc; 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); ila_init_saved_csum(p);
......
...@@ -138,6 +138,8 @@ static int parse_nl_config(struct genl_info *info, ...@@ -138,6 +138,8 @@ static int parse_nl_config(struct genl_info *info,
if (info->attrs[ILA_ATTR_CSUM_MODE]) if (info->attrs[ILA_ATTR_CSUM_MODE])
xp->ip.csum_mode = nla_get_u8(info->attrs[ILA_ATTR_CSUM_MODE]); xp->ip.csum_mode = nla_get_u8(info->attrs[ILA_ATTR_CSUM_MODE]);
else
xp->ip.csum_mode = ILA_CSUM_NO_ACTION;
if (info->attrs[ILA_ATTR_IFINDEX]) if (info->attrs[ILA_ATTR_IFINDEX])
xp->ifindex = nla_get_s32(info->attrs[ILA_ATTR_IFINDEX]); xp->ifindex = nla_get_s32(info->attrs[ILA_ATTR_IFINDEX]);
...@@ -198,7 +200,7 @@ static void ila_free_cb(void *ptr, void *arg) ...@@ -198,7 +200,7 @@ static void ila_free_cb(void *ptr, void *arg)
} }
} }
static int ila_xlat_addr(struct sk_buff *skb, bool set_csum_neutral); static int ila_xlat_addr(struct sk_buff *skb, bool sir2ila);
static unsigned int static unsigned int
ila_nf_input(void *priv, ila_nf_input(void *priv,
...@@ -396,7 +398,7 @@ static int ila_fill_info(struct ila_map *ila, struct sk_buff *msg) ...@@ -396,7 +398,7 @@ static int ila_fill_info(struct ila_map *ila, struct sk_buff *msg)
(__force u64)ila->xp.ip.locator_match.v64, (__force u64)ila->xp.ip.locator_match.v64,
ILA_ATTR_PAD) || 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)) nla_put_u8(msg, ILA_ATTR_CSUM_MODE, ila->xp.ip.csum_mode))
return -1; return -1;
return 0; return 0;
...@@ -607,7 +609,7 @@ static struct pernet_operations ila_net_ops = { ...@@ -607,7 +609,7 @@ 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, bool set_csum_neutral) static int ila_xlat_addr(struct sk_buff *skb, bool sir2ila)
{ {
struct ila_map *ila; struct ila_map *ila;
struct ipv6hdr *ip6h = ipv6_hdr(skb); struct ipv6hdr *ip6h = ipv6_hdr(skb);
...@@ -626,7 +628,7 @@ static int ila_xlat_addr(struct sk_buff *skb, bool set_csum_neutral) ...@@ -626,7 +628,7 @@ static int ila_xlat_addr(struct sk_buff *skb, bool set_csum_neutral)
ila = ila_lookup_wildcards(iaddr, skb->dev->ifindex, ilan); ila = ila_lookup_wildcards(iaddr, skb->dev->ifindex, ilan);
if (ila) if (ila)
ila_update_ipv6_locator(skb, &ila->xp.ip, set_csum_neutral); ila_update_ipv6_locator(skb, &ila->xp.ip, sir2ila);
rcu_read_unlock(); rcu_read_unlock();
......
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