Commit 273f8c14 authored by Justin Iurman's avatar Justin Iurman Committed by Paolo Abeni

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/PT8L6yqSigned-off-by: default avatarJustin Iurman <justin.iurman@uliege.be>
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent 924b8bea
...@@ -50,6 +50,12 @@ enum { ...@@ -50,6 +50,12 @@ enum {
IOAM6_IPTUNNEL_FREQ_K, /* u32 */ IOAM6_IPTUNNEL_FREQ_K, /* u32 */
IOAM6_IPTUNNEL_FREQ_N, /* 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, __IOAM6_IPTUNNEL_MAX,
}; };
......
...@@ -42,6 +42,8 @@ struct ioam6_lwt { ...@@ -42,6 +42,8 @@ struct ioam6_lwt {
struct ioam6_lwt_freq freq; struct ioam6_lwt_freq freq;
atomic_t pkt_cnt; atomic_t pkt_cnt;
u8 mode; u8 mode;
bool has_tunsrc;
struct in6_addr tunsrc;
struct in6_addr tundst; struct in6_addr tundst;
struct ioam6_lwt_encap tuninfo; struct ioam6_lwt_encap tuninfo;
}; };
...@@ -72,6 +74,7 @@ static const struct nla_policy ioam6_iptunnel_policy[IOAM6_IPTUNNEL_MAX + 1] = { ...@@ -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] = NLA_POLICY_RANGE(NLA_U8,
IOAM6_IPTUNNEL_MODE_MIN, IOAM6_IPTUNNEL_MODE_MIN,
IOAM6_IPTUNNEL_MODE_MAX), 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_DST] = NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)),
[IOAM6_IPTUNNEL_TRACE] = NLA_POLICY_EXACT_LEN( [IOAM6_IPTUNNEL_TRACE] = NLA_POLICY_EXACT_LEN(
sizeof(struct ioam6_trace_hdr)), sizeof(struct ioam6_trace_hdr)),
...@@ -144,6 +147,11 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla, ...@@ -144,6 +147,11 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla,
else else
mode = nla_get_u8(tb[IOAM6_IPTUNNEL_MODE]); 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) { if (!tb[IOAM6_IPTUNNEL_DST] && mode != IOAM6_IPTUNNEL_MODE_INLINE) {
NL_SET_ERR_MSG(extack, "this mode needs a tunnel destination"); NL_SET_ERR_MSG(extack, "this mode needs a tunnel destination");
return -EINVAL; return -EINVAL;
...@@ -168,16 +176,29 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla, ...@@ -168,16 +176,29 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla,
ilwt = ioam6_lwt_state(lwt); ilwt = ioam6_lwt_state(lwt);
err = dst_cache_init(&ilwt->cache, GFP_ATOMIC); err = dst_cache_init(&ilwt->cache, GFP_ATOMIC);
if (err) { if (err)
kfree(lwt); goto free_lwt;
return err;
}
atomic_set(&ilwt->pkt_cnt, 0); atomic_set(&ilwt->pkt_cnt, 0);
ilwt->freq.k = freq_k; ilwt->freq.k = freq_k;
ilwt->freq.n = freq_n; ilwt->freq.n = freq_n;
ilwt->mode = mode; 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]) if (tb[IOAM6_IPTUNNEL_DST])
ilwt->tundst = nla_get_in6_addr(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, ...@@ -202,6 +223,11 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla,
*ts = lwt; *ts = lwt;
return 0; 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) 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, ...@@ -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, static int ioam6_do_encap(struct net *net, struct sk_buff *skb,
struct ioam6_lwt_encap *tuninfo, struct ioam6_lwt_encap *tuninfo,
bool has_tunsrc,
struct in6_addr *tunsrc,
struct in6_addr *tundst) struct in6_addr *tundst)
{ {
struct dst_entry *dst = skb_dst(skb); struct dst_entry *dst = skb_dst(skb);
...@@ -286,8 +314,12 @@ static int ioam6_do_encap(struct net *net, struct sk_buff *skb, ...@@ -286,8 +314,12 @@ static int ioam6_do_encap(struct net *net, struct sk_buff *skb,
hdr->nexthdr = NEXTHDR_HOP; hdr->nexthdr = NEXTHDR_HOP;
hdr->payload_len = cpu_to_be16(skb->len - sizeof(*hdr)); hdr->payload_len = cpu_to_be16(skb->len - sizeof(*hdr));
hdr->daddr = *tundst; 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); skb_postpush_rcsum(skb, hdr, len);
...@@ -329,7 +361,9 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb) ...@@ -329,7 +361,9 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
case IOAM6_IPTUNNEL_MODE_ENCAP: case IOAM6_IPTUNNEL_MODE_ENCAP:
do_encap: do_encap:
/* Encapsulation (ip6ip6) */ /* 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)) if (unlikely(err))
goto drop; goto drop;
...@@ -415,6 +449,13 @@ static int ioam6_fill_encap_info(struct sk_buff *skb, ...@@ -415,6 +449,13 @@ static int ioam6_fill_encap_info(struct sk_buff *skb,
goto ret; goto ret;
if (ilwt->mode != IOAM6_IPTUNNEL_MODE_INLINE) { 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); err = nla_put_in6_addr(skb, IOAM6_IPTUNNEL_DST, &ilwt->tundst);
if (err) if (err)
goto ret; goto ret;
...@@ -436,8 +477,12 @@ static int ioam6_encap_nlsize(struct lwtunnel_state *lwtstate) ...@@ -436,8 +477,12 @@ static int ioam6_encap_nlsize(struct lwtunnel_state *lwtstate)
nla_total_size(sizeof(ilwt->mode)) + nla_total_size(sizeof(ilwt->mode)) +
nla_total_size(sizeof(ilwt->tuninfo.traceh)); 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)); nlsize += nla_total_size(sizeof(ilwt->tundst));
}
return nlsize; return nlsize;
} }
...@@ -452,8 +497,12 @@ static int ioam6_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) ...@@ -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 || return (ilwt_a->freq.k != ilwt_b->freq.k ||
ilwt_a->freq.n != ilwt_b->freq.n || ilwt_a->freq.n != ilwt_b->freq.n ||
ilwt_a->mode != ilwt_b->mode || ilwt_a->mode != ilwt_b->mode ||
ilwt_a->has_tunsrc != ilwt_b->has_tunsrc ||
(ilwt_a->mode != IOAM6_IPTUNNEL_MODE_INLINE && (ilwt_a->mode != IOAM6_IPTUNNEL_MODE_INLINE &&
!ipv6_addr_equal(&ilwt_a->tundst, &ilwt_b->tundst)) || !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); trace_a->namespace_id != trace_b->namespace_id);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment