Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ila: Perform only one translation in forwarding path

When setting up ILA in a router we noticed that the the encapsulation
is invoked twice: once in the route input path and again upon route
output. To resolve this we add a flag set_csum_neutral for the
ila_update_ipv6_locator. If this flag is set and the checksum
neutral bit is also set we assume that checksum-neutral translation
has already been performed and take no further action. The
flag is set only in ila_output path. The flag is not set for ila_input and
ila_xlat.

Tested:

Used 3 netns to set to emulate a router and two hosts. The router
translates SIR addresses between the two destinations in other two netns.
Verified ping and netperf are functional.

Signed-off-by: Tom Herbert <tom@herbertland.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Tom Herbert and committed by
David S. Miller
707a2ca4 e00431bc

+12 -9
+2 -1
net/ipv6/ila/ila.h
··· 109 109 return !!(ident.csum_neutral); 110 110 } 111 111 112 - void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p); 112 + void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p, 113 + bool set_csum_neutral); 113 114 114 115 void ila_init_saved_csum(struct ila_params *p); 115 116
+4 -2
net/ipv6/ila/ila_common.c
··· 103 103 iaddr->loc = p->locator; 104 104 } 105 105 106 - void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p) 106 + void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p, 107 + bool set_csum_neutral) 107 108 { 108 109 struct ipv6hdr *ip6h = ipv6_hdr(skb); 109 110 struct ila_addr *iaddr = ila_a2i(&ip6h->daddr); ··· 115 114 * is a locator being translated to a SIR address. 116 115 * Perform (receiver) checksum-neutral translation. 117 116 */ 118 - ila_csum_do_neutral(iaddr, p); 117 + if (!set_csum_neutral) 118 + ila_csum_do_neutral(iaddr, p); 119 119 } else { 120 120 switch (p->csum_mode) { 121 121 case ILA_CSUM_ADJUST_TRANSPORT:
+2 -2
net/ipv6/ila/ila_lwt.c
··· 26 26 if (skb->protocol != htons(ETH_P_IPV6)) 27 27 goto drop; 28 28 29 - ila_update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate)); 29 + ila_update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate), true); 30 30 31 31 return dst->lwtstate->orig_output(net, sk, skb); 32 32 ··· 42 42 if (skb->protocol != htons(ETH_P_IPV6)) 43 43 goto drop; 44 44 45 - ila_update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate)); 45 + ila_update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate), false); 46 46 47 47 return dst->lwtstate->orig_input(skb); 48 48
+4 -4
net/ipv6/ila/ila_xlat.c
··· 210 210 } 211 211 } 212 212 213 - static int ila_xlat_addr(struct sk_buff *skb); 213 + static int ila_xlat_addr(struct sk_buff *skb, bool set_csum_neutral); 214 214 215 215 static unsigned int 216 216 ila_nf_input(void *priv, 217 217 struct sk_buff *skb, 218 218 const struct nf_hook_state *state) 219 219 { 220 - ila_xlat_addr(skb); 220 + ila_xlat_addr(skb, false); 221 221 return NF_ACCEPT; 222 222 } 223 223 ··· 597 597 .size = sizeof(struct ila_net), 598 598 }; 599 599 600 - static int ila_xlat_addr(struct sk_buff *skb) 600 + static int ila_xlat_addr(struct sk_buff *skb, bool set_csum_neutral) 601 601 { 602 602 struct ila_map *ila; 603 603 struct ipv6hdr *ip6h = ipv6_hdr(skb); ··· 616 616 617 617 ila = ila_lookup_wildcards(iaddr, skb->dev->ifindex, ilan); 618 618 if (ila) 619 - ila_update_ipv6_locator(skb, &ila->xp.ip); 619 + ila_update_ipv6_locator(skb, &ila->xp.ip, set_csum_neutral); 620 620 621 621 rcu_read_unlock(); 622 622