]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: ipv6: ioam6: new feature tunsrc
authorJustin Iurman <justin.iurman@uliege.be>
Sat, 17 Aug 2024 13:18:18 +0000 (15:18 +0200)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 22 Aug 2024 08:45:12 +0000 (10:45 +0200)
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>
include/uapi/linux/ioam6_iptunnel.h
net/ipv6/ioam6_iptunnel.c

index 38f6a8fdfd34362aaa145905eee80267a8d096ff..8aef21e4a8c14dc89cd87e0eeb5e46de60651388 100644 (file)
@@ -50,6 +50,12 @@ enum {
        IOAM6_IPTUNNEL_FREQ_K,          /* u32 */
        IOAM6_IPTUNNEL_FREQ_N,          /* u32 */
 
+       /* Tunnel src address.
+        * For encap,auto modes.
+        * Optional (automatic if not provided).
+        */
+       IOAM6_IPTUNNEL_SRC,             /* struct in6_addr */
+
        __IOAM6_IPTUNNEL_MAX,
 };
 
index cd2522f04edfd6c91eb0a9453406dd1bb23ebc83..e34e1ff24546d0a9708a8393739ab7f83fc7d18d 100644 (file)
@@ -42,6 +42,8 @@ struct ioam6_lwt {
        struct ioam6_lwt_freq freq;
        atomic_t pkt_cnt;
        u8 mode;
+       bool has_tunsrc;
+       struct in6_addr tunsrc;
        struct in6_addr tundst;
        struct ioam6_lwt_encap tuninfo;
 };
@@ -72,6 +74,7 @@ static const struct nla_policy ioam6_iptunnel_policy[IOAM6_IPTUNNEL_MAX + 1] = {
        [IOAM6_IPTUNNEL_MODE]   = NLA_POLICY_RANGE(NLA_U8,
                                                   IOAM6_IPTUNNEL_MODE_MIN,
                                                   IOAM6_IPTUNNEL_MODE_MAX),
+       [IOAM6_IPTUNNEL_SRC]    = NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)),
        [IOAM6_IPTUNNEL_DST]    = NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)),
        [IOAM6_IPTUNNEL_TRACE]  = NLA_POLICY_EXACT_LEN(
                                        sizeof(struct ioam6_trace_hdr)),
@@ -144,6 +147,11 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla,
        else
                mode = nla_get_u8(tb[IOAM6_IPTUNNEL_MODE]);
 
+       if (tb[IOAM6_IPTUNNEL_SRC] && mode == IOAM6_IPTUNNEL_MODE_INLINE) {
+               NL_SET_ERR_MSG(extack, "no tunnel src expected with this mode");
+               return -EINVAL;
+       }
+
        if (!tb[IOAM6_IPTUNNEL_DST] && mode != IOAM6_IPTUNNEL_MODE_INLINE) {
                NL_SET_ERR_MSG(extack, "this mode needs a tunnel destination");
                return -EINVAL;
@@ -168,16 +176,29 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla,
 
        ilwt = ioam6_lwt_state(lwt);
        err = dst_cache_init(&ilwt->cache, GFP_ATOMIC);
-       if (err) {
-               kfree(lwt);
-               return err;
-       }
+       if (err)
+               goto free_lwt;
 
        atomic_set(&ilwt->pkt_cnt, 0);
        ilwt->freq.k = freq_k;
        ilwt->freq.n = freq_n;
 
        ilwt->mode = mode;
+
+       if (!tb[IOAM6_IPTUNNEL_SRC]) {
+               ilwt->has_tunsrc = false;
+       } else {
+               ilwt->has_tunsrc = true;
+               ilwt->tunsrc = nla_get_in6_addr(tb[IOAM6_IPTUNNEL_SRC]);
+
+               if (ipv6_addr_any(&ilwt->tunsrc)) {
+                       NL_SET_ERR_MSG_ATTR(extack, tb[IOAM6_IPTUNNEL_SRC],
+                                           "invalid tunnel source address");
+                       err = -EINVAL;
+                       goto free_cache;
+               }
+       }
+
        if (tb[IOAM6_IPTUNNEL_DST])
                ilwt->tundst = nla_get_in6_addr(tb[IOAM6_IPTUNNEL_DST]);
 
@@ -202,6 +223,11 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla,
        *ts = lwt;
 
        return 0;
+free_cache:
+       dst_cache_destroy(&ilwt->cache);
+free_lwt:
+       kfree(lwt);
+       return err;
 }
 
 static int ioam6_do_fill(struct net *net, struct sk_buff *skb)
@@ -257,6 +283,8 @@ static int ioam6_do_inline(struct net *net, struct sk_buff *skb,
 
 static int ioam6_do_encap(struct net *net, struct sk_buff *skb,
                          struct ioam6_lwt_encap *tuninfo,
+                         bool has_tunsrc,
+                         struct in6_addr *tunsrc,
                          struct in6_addr *tundst)
 {
        struct dst_entry *dst = skb_dst(skb);
@@ -286,8 +314,12 @@ static int ioam6_do_encap(struct net *net, struct sk_buff *skb,
        hdr->nexthdr = NEXTHDR_HOP;
        hdr->payload_len = cpu_to_be16(skb->len - sizeof(*hdr));
        hdr->daddr = *tundst;
-       ipv6_dev_get_saddr(net, dst->dev, &hdr->daddr,
-                          IPV6_PREFER_SRC_PUBLIC, &hdr->saddr);
+
+       if (has_tunsrc)
+               memcpy(&hdr->saddr, tunsrc, sizeof(*tunsrc));
+       else
+               ipv6_dev_get_saddr(net, dst->dev, &hdr->daddr,
+                                  IPV6_PREFER_SRC_PUBLIC, &hdr->saddr);
 
        skb_postpush_rcsum(skb, hdr, len);
 
@@ -329,7 +361,9 @@ do_inline:
        case IOAM6_IPTUNNEL_MODE_ENCAP:
 do_encap:
                /* Encapsulation (ip6ip6) */
-               err = ioam6_do_encap(net, skb, &ilwt->tuninfo, &ilwt->tundst);
+               err = ioam6_do_encap(net, skb, &ilwt->tuninfo,
+                                    ilwt->has_tunsrc, &ilwt->tunsrc,
+                                    &ilwt->tundst);
                if (unlikely(err))
                        goto drop;
 
@@ -415,6 +449,13 @@ static int ioam6_fill_encap_info(struct sk_buff *skb,
                goto ret;
 
        if (ilwt->mode != IOAM6_IPTUNNEL_MODE_INLINE) {
+               if (ilwt->has_tunsrc) {
+                       err = nla_put_in6_addr(skb, IOAM6_IPTUNNEL_SRC,
+                                              &ilwt->tunsrc);
+                       if (err)
+                               goto ret;
+               }
+
                err = nla_put_in6_addr(skb, IOAM6_IPTUNNEL_DST, &ilwt->tundst);
                if (err)
                        goto ret;
@@ -436,8 +477,12 @@ static int ioam6_encap_nlsize(struct lwtunnel_state *lwtstate)
                  nla_total_size(sizeof(ilwt->mode)) +
                  nla_total_size(sizeof(ilwt->tuninfo.traceh));
 
-       if (ilwt->mode != IOAM6_IPTUNNEL_MODE_INLINE)
+       if (ilwt->mode != IOAM6_IPTUNNEL_MODE_INLINE) {
+               if (ilwt->has_tunsrc)
+                       nlsize += nla_total_size(sizeof(ilwt->tunsrc));
+
                nlsize += nla_total_size(sizeof(ilwt->tundst));
+       }
 
        return nlsize;
 }
@@ -452,8 +497,12 @@ static int ioam6_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)
        return (ilwt_a->freq.k != ilwt_b->freq.k ||
                ilwt_a->freq.n != ilwt_b->freq.n ||
                ilwt_a->mode != ilwt_b->mode ||
+               ilwt_a->has_tunsrc != ilwt_b->has_tunsrc ||
                (ilwt_a->mode != IOAM6_IPTUNNEL_MODE_INLINE &&
                 !ipv6_addr_equal(&ilwt_a->tundst, &ilwt_b->tundst)) ||
+               (ilwt_a->mode != IOAM6_IPTUNNEL_MODE_INLINE &&
+                ilwt_a->has_tunsrc &&
+                !ipv6_addr_equal(&ilwt_a->tunsrc, &ilwt_b->tunsrc)) ||
                trace_a->namespace_id != trace_b->namespace_id);
 }