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

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: Tom Herbert <tom@quantonium.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Tom Herbert and committed by
David S. Miller
84287bb3 80661e76

+61 -44
+1
include/uapi/linux/ila.h
··· 41 41 ILA_CSUM_ADJUST_TRANSPORT, 42 42 ILA_CSUM_NEUTRAL_MAP, 43 43 ILA_CSUM_NO_ACTION, 44 + ILA_CSUM_NEUTRAL_MAP_AUTO, 44 45 }; 45 46 46 47 #endif /* _UAPI_LINUX_ILA_H */
+39 -26
net/ipv6/ila/ila_common.c
··· 37 37 return get_csum_diff_iaddr(ila_a2i(&ip6h->daddr), p); 38 38 } 39 39 40 - static void ila_csum_do_neutral(struct ila_addr *iaddr, 41 - struct ila_params *p) 40 + static void ila_csum_do_neutral_fmt(struct ila_addr *iaddr, 41 + struct ila_params *p) 42 42 { 43 43 __sum16 *adjust = (__force __sum16 *)&iaddr->ident.v16[3]; 44 44 __wsum diff, fval; ··· 60 60 iaddr->ident.csum_neutral ^= 1; 61 61 } 62 62 63 + static void ila_csum_do_neutral_nofmt(struct ila_addr *iaddr, 64 + struct ila_params *p) 65 + { 66 + __sum16 *adjust = (__force __sum16 *)&iaddr->ident.v16[3]; 67 + __wsum diff; 68 + 69 + diff = get_csum_diff_iaddr(iaddr, p); 70 + 71 + *adjust = ~csum_fold(csum_add(diff, csum_unfold(*adjust))); 72 + } 73 + 63 74 static void ila_csum_adjust_transport(struct sk_buff *skb, 64 75 struct ila_params *p) 65 76 { 66 - __wsum diff; 67 - struct ipv6hdr *ip6h = ipv6_hdr(skb); 68 - struct ila_addr *iaddr = ila_a2i(&ip6h->daddr); 69 77 size_t nhoff = sizeof(struct ipv6hdr); 78 + struct ipv6hdr *ip6h = ipv6_hdr(skb); 79 + __wsum diff; 70 80 71 81 switch (ip6h->nexthdr) { 72 82 case NEXTHDR_TCP: ··· 115 105 } 116 106 break; 117 107 } 118 - 119 - /* Now change destination address */ 120 - iaddr->loc = p->locator; 121 108 } 122 109 123 110 void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p, 124 - bool set_csum_neutral) 111 + bool sir2ila) 125 112 { 126 113 struct ipv6hdr *ip6h = ipv6_hdr(skb); 127 114 struct ila_addr *iaddr = ila_a2i(&ip6h->daddr); 128 115 129 - /* First deal with the transport checksum */ 130 - if (ila_csum_neutral_set(iaddr->ident)) { 131 - /* C-bit is set in the locator indicating that this 132 - * is a locator being translated to a SIR address. 133 - * Perform (receiver) checksum-neutral translation. 134 - */ 135 - if (!set_csum_neutral) 136 - ila_csum_do_neutral(iaddr, p); 137 - } else { 138 - switch (p->csum_mode) { 139 - case ILA_CSUM_ADJUST_TRANSPORT: 140 - ila_csum_adjust_transport(skb, p); 141 - break; 142 - case ILA_CSUM_NEUTRAL_MAP: 143 - ila_csum_do_neutral(iaddr, p); 144 - break; 145 - case ILA_CSUM_NO_ACTION: 116 + switch (p->csum_mode) { 117 + case ILA_CSUM_ADJUST_TRANSPORT: 118 + ila_csum_adjust_transport(skb, p); 119 + break; 120 + case ILA_CSUM_NEUTRAL_MAP: 121 + if (sir2ila) { 122 + if (WARN_ON(ila_csum_neutral_set(iaddr->ident))) { 123 + /* Checksum flag should never be 124 + * set in a formatted SIR address. 125 + */ 126 + break; 127 + } 128 + } else if (!ila_csum_neutral_set(iaddr->ident)) { 129 + /* ILA to SIR translation and C-bit isn't 130 + * set so we're good. 131 + */ 146 132 break; 147 133 } 134 + ila_csum_do_neutral_fmt(iaddr, p); 135 + break; 136 + case ILA_CSUM_NEUTRAL_MAP_AUTO: 137 + ila_csum_do_neutral_nofmt(iaddr, p); 138 + break; 139 + case ILA_CSUM_NO_ACTION: 140 + break; 148 141 } 149 142 150 143 /* Now change destination address */
+15 -14
net/ipv6/ila/ila_lwt.c
··· 127 127 struct lwtunnel_state *newts; 128 128 const struct fib6_config *cfg6 = cfg; 129 129 struct ila_addr *iaddr; 130 + u8 csum_mode = ILA_CSUM_NO_ACTION; 130 131 int ret; 131 132 132 133 if (family != AF_INET6) ··· 140 139 return -EINVAL; 141 140 } 142 141 143 - iaddr = (struct ila_addr *)&cfg6->fc_dst; 144 - 145 - if (!ila_addr_is_ila(iaddr) || ila_csum_neutral_set(iaddr->ident)) { 146 - /* Don't allow translation for a non-ILA address or checksum 147 - * neutral flag to be set. 148 - */ 149 - return -EINVAL; 150 - } 151 - 152 142 ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla, ila_nl_policy, extack); 153 143 if (ret < 0) 154 144 return ret; 155 145 156 146 if (!tb[ILA_ATTR_LOCATOR]) 157 147 return -EINVAL; 148 + 149 + iaddr = (struct ila_addr *)&cfg6->fc_dst; 150 + 151 + if (tb[ILA_ATTR_CSUM_MODE]) 152 + csum_mode = nla_get_u8(tb[ILA_ATTR_CSUM_MODE]); 153 + 154 + if (csum_mode == ILA_CSUM_NEUTRAL_MAP && 155 + ila_csum_neutral_set(iaddr->ident)) { 156 + /* Don't allow translation if checksum neutral bit is 157 + * configured and it's set in the SIR address. 158 + */ 159 + return -EINVAL; 160 + } 158 161 159 162 newts = lwtunnel_state_alloc(sizeof(*ilwt)); 160 163 if (!newts) ··· 173 168 174 169 p = ila_params_lwtunnel(newts); 175 170 171 + p->csum_mode = csum_mode; 176 172 p->locator.v64 = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]); 177 173 178 174 /* Precompute checksum difference for translation since we 179 175 * know both the old locator and the new one. 180 176 */ 181 177 p->locator_match = iaddr->loc; 182 - p->csum_diff = compute_csum_diff8( 183 - (__be32 *)&p->locator_match, (__be32 *)&p->locator); 184 - 185 - if (tb[ILA_ATTR_CSUM_MODE]) 186 - p->csum_mode = nla_get_u8(tb[ILA_ATTR_CSUM_MODE]); 187 178 188 179 ila_init_saved_csum(p); 189 180
+6 -4
net/ipv6/ila/ila_xlat.c
··· 138 138 139 139 if (info->attrs[ILA_ATTR_CSUM_MODE]) 140 140 xp->ip.csum_mode = nla_get_u8(info->attrs[ILA_ATTR_CSUM_MODE]); 141 + else 142 + xp->ip.csum_mode = ILA_CSUM_NO_ACTION; 141 143 142 144 if (info->attrs[ILA_ATTR_IFINDEX]) 143 145 xp->ifindex = nla_get_s32(info->attrs[ILA_ATTR_IFINDEX]); ··· 200 198 } 201 199 } 202 200 203 - static int ila_xlat_addr(struct sk_buff *skb, bool set_csum_neutral); 201 + static int ila_xlat_addr(struct sk_buff *skb, bool sir2ila); 204 202 205 203 static unsigned int 206 204 ila_nf_input(void *priv, ··· 398 396 (__force u64)ila->xp.ip.locator_match.v64, 399 397 ILA_ATTR_PAD) || 400 398 nla_put_s32(msg, ILA_ATTR_IFINDEX, ila->xp.ifindex) || 401 - nla_put_u32(msg, ILA_ATTR_CSUM_MODE, ila->xp.ip.csum_mode)) 399 + nla_put_u8(msg, ILA_ATTR_CSUM_MODE, ila->xp.ip.csum_mode)) 402 400 return -1; 403 401 404 402 return 0; ··· 609 607 .size = sizeof(struct ila_net), 610 608 }; 611 609 612 - static int ila_xlat_addr(struct sk_buff *skb, bool set_csum_neutral) 610 + static int ila_xlat_addr(struct sk_buff *skb, bool sir2ila) 613 611 { 614 612 struct ila_map *ila; 615 613 struct ipv6hdr *ip6h = ipv6_hdr(skb); ··· 628 626 629 627 ila = ila_lookup_wildcards(iaddr, skb->dev->ifindex, ilan); 630 628 if (ila) 631 - ila_update_ipv6_locator(skb, &ila->xp.ip, set_csum_neutral); 629 + ila_update_ipv6_locator(skb, &ila->xp.ip, sir2ila); 632 630 633 631 rcu_read_unlock(); 634 632