Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ enum {
	ILA_CSUM_ADJUST_TRANSPORT,
	ILA_CSUM_NEUTRAL_MAP,
	ILA_CSUM_NO_ACTION,
	ILA_CSUM_NEUTRAL_MAP_AUTO,
};

#endif /* _UAPI_LINUX_ILA_H */
+39 −26
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ static __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *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)
{
	__sum16 *adjust = (__force __sum16 *)&iaddr->ident.v16[3];
@@ -60,13 +60,23 @@ static void ila_csum_do_neutral(struct ila_addr *iaddr,
	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)
{
	__sum16 *adjust = (__force __sum16 *)&iaddr->ident.v16[3];
	__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);
	struct ipv6hdr *ip6h = ipv6_hdr(skb);
	__wsum diff;

	switch (ip6h->nexthdr) {
	case NEXTHDR_TCP:
@@ -105,36 +115,39 @@ static void ila_csum_adjust_transport(struct sk_buff *skb,
		}
		break;
	}

	/* Now change destination address */
	iaddr->loc = p->locator;
}

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 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.
		 */
		if (!set_csum_neutral)
			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);
		if (sir2ila) {
			if (WARN_ON(ila_csum_neutral_set(iaddr->ident))) {
				/* Checksum flag should never be
				 * set in a formatted SIR address.
				 */
				break;
		case ILA_CSUM_NO_ACTION:
			}
		} else if (!ila_csum_neutral_set(iaddr->ident)) {
			/* ILA to SIR translation and C-bit isn't
			 * set so we're good.
			 */
			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 */
+15 −14
Original line number Diff line number Diff line
@@ -127,6 +127,7 @@ static int ila_build_state(struct nlattr *nla,
	struct lwtunnel_state *newts;
	const struct fib6_config *cfg6 = cfg;
	struct ila_addr *iaddr;
	u8 csum_mode = ILA_CSUM_NO_ACTION;
	int ret;

	if (family != AF_INET6)
@@ -139,15 +140,6 @@ static int ila_build_state(struct nlattr *nla,
		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);
	if (ret < 0)
		return ret;
@@ -155,6 +147,19 @@ static int ila_build_state(struct nlattr *nla,
	if (!tb[ILA_ATTR_LOCATOR])
		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));
	if (!newts)
		return -ENOMEM;
@@ -168,17 +173,13 @@ static int ila_build_state(struct nlattr *nla,

	p = ila_params_lwtunnel(newts);

	p->csum_mode = csum_mode;
	p->locator.v64 = (__force __be64)nla_get_u64(tb[ILA_ATTR_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);

+6 −4
Original line number Diff line number Diff line
@@ -138,6 +138,8 @@ static int parse_nl_config(struct genl_info *info,

	if (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])
		xp->ifindex = nla_get_s32(info->attrs[ILA_ATTR_IFINDEX]);
@@ -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
ila_nf_input(void *priv,
@@ -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,
			      ILA_ATTR_PAD) ||
	    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 0;
@@ -607,7 +609,7 @@ static struct pernet_operations ila_net_ops = {
	.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 ipv6hdr *ip6h = ipv6_hdr(skb);
@@ -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);
	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();