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

net: ipv6: ioam6: new feature tunsrc

This patch provides a new feature (i.e., "tunsrc") for the tunnel (i.e.,
"encap") mode of ioam6. Just like seg6 already does, except it is
attached to a route. The "tunsrc" is optional: when not provided (by
default), the automatic resolution is applied. Using "tunsrc" when
possible has a benefit: performance. See the comparison:
- before (= "encap" mode): https://ibb.co/bNCzvf7
- after (= "encap" mode with "tunsrc"): https://ibb.co/PT8L6yq

Signed-off-by: Justin Iurman <justin.iurman@uliege.be>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Justin Iurman and committed by
Paolo Abeni
273f8c14 924b8bea

+63 -8
+6
include/uapi/linux/ioam6_iptunnel.h
··· 50 50 IOAM6_IPTUNNEL_FREQ_K, /* u32 */ 51 51 IOAM6_IPTUNNEL_FREQ_N, /* u32 */ 52 52 53 + /* Tunnel src address. 54 + * For encap,auto modes. 55 + * Optional (automatic if not provided). 56 + */ 57 + IOAM6_IPTUNNEL_SRC, /* struct in6_addr */ 58 + 53 59 __IOAM6_IPTUNNEL_MAX, 54 60 }; 55 61
+57 -8
net/ipv6/ioam6_iptunnel.c
··· 42 42 struct ioam6_lwt_freq freq; 43 43 atomic_t pkt_cnt; 44 44 u8 mode; 45 + bool has_tunsrc; 46 + struct in6_addr tunsrc; 45 47 struct in6_addr tundst; 46 48 struct ioam6_lwt_encap tuninfo; 47 49 }; ··· 74 72 [IOAM6_IPTUNNEL_MODE] = NLA_POLICY_RANGE(NLA_U8, 75 73 IOAM6_IPTUNNEL_MODE_MIN, 76 74 IOAM6_IPTUNNEL_MODE_MAX), 75 + [IOAM6_IPTUNNEL_SRC] = NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)), 77 76 [IOAM6_IPTUNNEL_DST] = NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)), 78 77 [IOAM6_IPTUNNEL_TRACE] = NLA_POLICY_EXACT_LEN( 79 78 sizeof(struct ioam6_trace_hdr)), ··· 147 144 else 148 145 mode = nla_get_u8(tb[IOAM6_IPTUNNEL_MODE]); 149 146 147 + if (tb[IOAM6_IPTUNNEL_SRC] && mode == IOAM6_IPTUNNEL_MODE_INLINE) { 148 + NL_SET_ERR_MSG(extack, "no tunnel src expected with this mode"); 149 + return -EINVAL; 150 + } 151 + 150 152 if (!tb[IOAM6_IPTUNNEL_DST] && mode != IOAM6_IPTUNNEL_MODE_INLINE) { 151 153 NL_SET_ERR_MSG(extack, "this mode needs a tunnel destination"); 152 154 return -EINVAL; ··· 176 168 177 169 ilwt = ioam6_lwt_state(lwt); 178 170 err = dst_cache_init(&ilwt->cache, GFP_ATOMIC); 179 - if (err) { 180 - kfree(lwt); 181 - return err; 182 - } 171 + if (err) 172 + goto free_lwt; 183 173 184 174 atomic_set(&ilwt->pkt_cnt, 0); 185 175 ilwt->freq.k = freq_k; 186 176 ilwt->freq.n = freq_n; 187 177 188 178 ilwt->mode = mode; 179 + 180 + if (!tb[IOAM6_IPTUNNEL_SRC]) { 181 + ilwt->has_tunsrc = false; 182 + } else { 183 + ilwt->has_tunsrc = true; 184 + ilwt->tunsrc = nla_get_in6_addr(tb[IOAM6_IPTUNNEL_SRC]); 185 + 186 + if (ipv6_addr_any(&ilwt->tunsrc)) { 187 + NL_SET_ERR_MSG_ATTR(extack, tb[IOAM6_IPTUNNEL_SRC], 188 + "invalid tunnel source address"); 189 + err = -EINVAL; 190 + goto free_cache; 191 + } 192 + } 193 + 189 194 if (tb[IOAM6_IPTUNNEL_DST]) 190 195 ilwt->tundst = nla_get_in6_addr(tb[IOAM6_IPTUNNEL_DST]); 191 196 ··· 223 202 *ts = lwt; 224 203 225 204 return 0; 205 + free_cache: 206 + dst_cache_destroy(&ilwt->cache); 207 + free_lwt: 208 + kfree(lwt); 209 + return err; 226 210 } 227 211 228 212 static int ioam6_do_fill(struct net *net, struct sk_buff *skb) ··· 283 257 284 258 static int ioam6_do_encap(struct net *net, struct sk_buff *skb, 285 259 struct ioam6_lwt_encap *tuninfo, 260 + bool has_tunsrc, 261 + struct in6_addr *tunsrc, 286 262 struct in6_addr *tundst) 287 263 { 288 264 struct dst_entry *dst = skb_dst(skb); ··· 314 286 hdr->nexthdr = NEXTHDR_HOP; 315 287 hdr->payload_len = cpu_to_be16(skb->len - sizeof(*hdr)); 316 288 hdr->daddr = *tundst; 317 - ipv6_dev_get_saddr(net, dst->dev, &hdr->daddr, 318 - IPV6_PREFER_SRC_PUBLIC, &hdr->saddr); 289 + 290 + if (has_tunsrc) 291 + memcpy(&hdr->saddr, tunsrc, sizeof(*tunsrc)); 292 + else 293 + ipv6_dev_get_saddr(net, dst->dev, &hdr->daddr, 294 + IPV6_PREFER_SRC_PUBLIC, &hdr->saddr); 319 295 320 296 skb_postpush_rcsum(skb, hdr, len); 321 297 ··· 361 329 case IOAM6_IPTUNNEL_MODE_ENCAP: 362 330 do_encap: 363 331 /* Encapsulation (ip6ip6) */ 364 - err = ioam6_do_encap(net, skb, &ilwt->tuninfo, &ilwt->tundst); 332 + err = ioam6_do_encap(net, skb, &ilwt->tuninfo, 333 + ilwt->has_tunsrc, &ilwt->tunsrc, 334 + &ilwt->tundst); 365 335 if (unlikely(err)) 366 336 goto drop; 367 337 ··· 449 415 goto ret; 450 416 451 417 if (ilwt->mode != IOAM6_IPTUNNEL_MODE_INLINE) { 418 + if (ilwt->has_tunsrc) { 419 + err = nla_put_in6_addr(skb, IOAM6_IPTUNNEL_SRC, 420 + &ilwt->tunsrc); 421 + if (err) 422 + goto ret; 423 + } 424 + 452 425 err = nla_put_in6_addr(skb, IOAM6_IPTUNNEL_DST, &ilwt->tundst); 453 426 if (err) 454 427 goto ret; ··· 477 436 nla_total_size(sizeof(ilwt->mode)) + 478 437 nla_total_size(sizeof(ilwt->tuninfo.traceh)); 479 438 480 - if (ilwt->mode != IOAM6_IPTUNNEL_MODE_INLINE) 439 + if (ilwt->mode != IOAM6_IPTUNNEL_MODE_INLINE) { 440 + if (ilwt->has_tunsrc) 441 + nlsize += nla_total_size(sizeof(ilwt->tunsrc)); 442 + 481 443 nlsize += nla_total_size(sizeof(ilwt->tundst)); 444 + } 482 445 483 446 return nlsize; 484 447 } ··· 497 452 return (ilwt_a->freq.k != ilwt_b->freq.k || 498 453 ilwt_a->freq.n != ilwt_b->freq.n || 499 454 ilwt_a->mode != ilwt_b->mode || 455 + ilwt_a->has_tunsrc != ilwt_b->has_tunsrc || 500 456 (ilwt_a->mode != IOAM6_IPTUNNEL_MODE_INLINE && 501 457 !ipv6_addr_equal(&ilwt_a->tundst, &ilwt_b->tundst)) || 458 + (ilwt_a->mode != IOAM6_IPTUNNEL_MODE_INLINE && 459 + ilwt_a->has_tunsrc && 460 + !ipv6_addr_equal(&ilwt_a->tunsrc, &ilwt_b->tunsrc)) || 502 461 trace_a->namespace_id != trace_b->namespace_id); 503 462 } 504 463