Commit cfbe9b00 authored by David S. Miller's avatar David S. Miller

Merge branch 'ipv6-ioam-encap'

Justin Iurman says:

====================
Support for the ip6ip6 encapsulation of IOAM

v2:
 - add prerequisite patches
 - keep uapi backwards compatible by adding two new attributes
 - add more comments to document the ioam6_iptunnel uapi

In the current implementation, IOAM can only be inserted directly (i.e., only
inside packets generated locally) by default, to be compliant with RFC8200.

This patch adds support for in-transit packets and provides the ip6ip6
encapsulation of IOAM (RFC8200 compliant). Therefore, three ioam6 encap modes
are defined:

 - inline: directly inserts IOAM inside packets (by default).

 - encap:  ip6ip6 encapsulation of IOAM inside packets.

 - auto:   either inline mode for packets generated locally or encap mode for
           in-transit packets.

With current iproute2 implementation, it is configured this way:

$ ip -6 r [...] encap ioam6 trace prealloc [...]

The old syntax does not change (for backwards compatibility) and implicitly uses
the inline mode. With the new syntax, an encap mode can be specified:

(inline mode)
$ ip -6 r [...] encap ioam6 mode inline trace prealloc [...]

(encap mode)
$ ip -6 r [...] encap ioam6 mode encap tundst fc00::2 trace prealloc [...]

(auto mode)
$ ip -6 r [...] encap ioam6 mode auto tundst fc00::2 trace prealloc [...]

A tunnel destination address must be configured when using the encap mode or the
auto mode.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 9ac93627 bf77b140
...@@ -56,7 +56,8 @@ static inline struct ioam6_pernet_data *ioam6_pernet(struct net *net) ...@@ -56,7 +56,8 @@ static inline struct ioam6_pernet_data *ioam6_pernet(struct net *net)
struct ioam6_namespace *ioam6_namespace(struct net *net, __be16 id); struct ioam6_namespace *ioam6_namespace(struct net *net, __be16 id);
void ioam6_fill_trace_data(struct sk_buff *skb, void ioam6_fill_trace_data(struct sk_buff *skb,
struct ioam6_namespace *ns, struct ioam6_namespace *ns,
struct ioam6_trace_hdr *trace); struct ioam6_trace_hdr *trace,
bool is_input);
int ioam6_init(void); int ioam6_init(void);
void ioam6_exit(void); void ioam6_exit(void);
......
...@@ -9,9 +9,38 @@ ...@@ -9,9 +9,38 @@
#ifndef _UAPI_LINUX_IOAM6_IPTUNNEL_H #ifndef _UAPI_LINUX_IOAM6_IPTUNNEL_H
#define _UAPI_LINUX_IOAM6_IPTUNNEL_H #define _UAPI_LINUX_IOAM6_IPTUNNEL_H
/* Encap modes:
* - inline: direct insertion
* - encap: ip6ip6 encapsulation
* - auto: inline for local packets, encap for in-transit packets
*/
enum {
__IOAM6_IPTUNNEL_MODE_MIN,
IOAM6_IPTUNNEL_MODE_INLINE,
IOAM6_IPTUNNEL_MODE_ENCAP,
IOAM6_IPTUNNEL_MODE_AUTO,
__IOAM6_IPTUNNEL_MODE_MAX,
};
#define IOAM6_IPTUNNEL_MODE_MIN (__IOAM6_IPTUNNEL_MODE_MIN + 1)
#define IOAM6_IPTUNNEL_MODE_MAX (__IOAM6_IPTUNNEL_MODE_MAX - 1)
enum { enum {
IOAM6_IPTUNNEL_UNSPEC, IOAM6_IPTUNNEL_UNSPEC,
/* Encap mode */
IOAM6_IPTUNNEL_MODE, /* u8 */
/* Tunnel dst address.
* For encap,auto modes.
*/
IOAM6_IPTUNNEL_DST, /* struct in6_addr */
/* IOAM Trace Header */
IOAM6_IPTUNNEL_TRACE, /* struct ioam6_trace_hdr */ IOAM6_IPTUNNEL_TRACE, /* struct ioam6_trace_hdr */
__IOAM6_IPTUNNEL_MAX, __IOAM6_IPTUNNEL_MAX,
}; };
......
...@@ -332,10 +332,10 @@ config IPV6_IOAM6_LWTUNNEL ...@@ -332,10 +332,10 @@ config IPV6_IOAM6_LWTUNNEL
bool "IPv6: IOAM Pre-allocated Trace insertion support" bool "IPv6: IOAM Pre-allocated Trace insertion support"
depends on IPV6 depends on IPV6
select LWTUNNEL select LWTUNNEL
select DST_CACHE
help help
Support for the inline insertion of IOAM Pre-allocated Support for the insertion of IOAM Pre-allocated Trace
Trace Header (only on locally generated packets), using Header using the lightweight tunnels mechanism.
the lightweight tunnels mechanism.
If unsure, say N. If unsure, say N.
......
...@@ -979,7 +979,7 @@ static bool ipv6_hop_ioam(struct sk_buff *skb, int optoff) ...@@ -979,7 +979,7 @@ static bool ipv6_hop_ioam(struct sk_buff *skb, int optoff)
if (!skb_valid_dst(skb)) if (!skb_valid_dst(skb))
ip6_route_input(skb); ip6_route_input(skb);
ioam6_fill_trace_data(skb, ns, trace); ioam6_fill_trace_data(skb, ns, trace, true);
break; break;
default: default:
break; break;
......
...@@ -631,7 +631,7 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, ...@@ -631,7 +631,7 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb,
struct ioam6_namespace *ns, struct ioam6_namespace *ns,
struct ioam6_trace_hdr *trace, struct ioam6_trace_hdr *trace,
struct ioam6_schema *sc, struct ioam6_schema *sc,
u8 sclen) u8 sclen, bool is_input)
{ {
struct __kernel_sock_timeval ts; struct __kernel_sock_timeval ts;
u64 raw64; u64 raw64;
...@@ -645,7 +645,7 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, ...@@ -645,7 +645,7 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb,
/* hop_lim and node_id */ /* hop_lim and node_id */
if (trace->type.bit0) { if (trace->type.bit0) {
byte = ipv6_hdr(skb)->hop_limit; byte = ipv6_hdr(skb)->hop_limit;
if (skb->dev) if (is_input)
byte--; byte--;
raw32 = dev_net(skb_dst(skb)->dev)->ipv6.sysctl.ioam6_id; raw32 = dev_net(skb_dst(skb)->dev)->ipv6.sysctl.ioam6_id;
...@@ -730,7 +730,7 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, ...@@ -730,7 +730,7 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb,
/* hop_lim and node_id (wide) */ /* hop_lim and node_id (wide) */
if (trace->type.bit8) { if (trace->type.bit8) {
byte = ipv6_hdr(skb)->hop_limit; byte = ipv6_hdr(skb)->hop_limit;
if (skb->dev) if (is_input)
byte--; byte--;
raw64 = dev_net(skb_dst(skb)->dev)->ipv6.sysctl.ioam6_id_wide; raw64 = dev_net(skb_dst(skb)->dev)->ipv6.sysctl.ioam6_id_wide;
...@@ -786,7 +786,8 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, ...@@ -786,7 +786,8 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb,
/* called with rcu_read_lock() */ /* called with rcu_read_lock() */
void ioam6_fill_trace_data(struct sk_buff *skb, void ioam6_fill_trace_data(struct sk_buff *skb,
struct ioam6_namespace *ns, struct ioam6_namespace *ns,
struct ioam6_trace_hdr *trace) struct ioam6_trace_hdr *trace,
bool is_input)
{ {
struct ioam6_schema *sc; struct ioam6_schema *sc;
u8 sclen = 0; u8 sclen = 0;
...@@ -822,7 +823,7 @@ void ioam6_fill_trace_data(struct sk_buff *skb, ...@@ -822,7 +823,7 @@ void ioam6_fill_trace_data(struct sk_buff *skb,
return; return;
} }
__ioam6_fill_trace_data(skb, ns, trace, sc, sclen); __ioam6_fill_trace_data(skb, ns, trace, sc, sclen, is_input);
trace->remlen -= trace->nodelen + sclen; trace->remlen -= trace->nodelen + sclen;
} }
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/net.h> #include <linux/net.h>
#include <linux/netlink.h>
#include <linux/in6.h> #include <linux/in6.h>
#include <linux/ioam6.h> #include <linux/ioam6.h>
#include <linux/ioam6_iptunnel.h> #include <linux/ioam6_iptunnel.h>
...@@ -17,18 +16,26 @@ ...@@ -17,18 +16,26 @@
#include <net/sock.h> #include <net/sock.h>
#include <net/lwtunnel.h> #include <net/lwtunnel.h>
#include <net/ioam6.h> #include <net/ioam6.h>
#include <net/netlink.h>
#include <net/ipv6.h>
#include <net/dst_cache.h>
#include <net/ip6_route.h>
#include <net/addrconf.h>
#define IOAM6_MASK_SHORT_FIELDS 0xff100000 #define IOAM6_MASK_SHORT_FIELDS 0xff100000
#define IOAM6_MASK_WIDE_FIELDS 0xe00000 #define IOAM6_MASK_WIDE_FIELDS 0xe00000
struct ioam6_lwt_encap { struct ioam6_lwt_encap {
struct ipv6_hopopt_hdr eh; struct ipv6_hopopt_hdr eh;
u8 pad[2]; /* 2-octet padding for 4n-alignment */ u8 pad[2]; /* 2-octet padding for 4n-alignment */
struct ioam6_hdr ioamh; struct ioam6_hdr ioamh;
struct ioam6_trace_hdr traceh; struct ioam6_trace_hdr traceh;
} __packed; } __packed;
struct ioam6_lwt { struct ioam6_lwt {
struct dst_cache cache;
u8 mode;
struct in6_addr tundst;
struct ioam6_lwt_encap tuninfo; struct ioam6_lwt_encap tuninfo;
}; };
...@@ -42,34 +49,19 @@ static struct ioam6_lwt_encap *ioam6_lwt_info(struct lwtunnel_state *lwt) ...@@ -42,34 +49,19 @@ static struct ioam6_lwt_encap *ioam6_lwt_info(struct lwtunnel_state *lwt)
return &ioam6_lwt_state(lwt)->tuninfo; return &ioam6_lwt_state(lwt)->tuninfo;
} }
static struct ioam6_trace_hdr *ioam6_trace(struct lwtunnel_state *lwt) static struct ioam6_trace_hdr *ioam6_lwt_trace(struct lwtunnel_state *lwt)
{ {
return &(ioam6_lwt_state(lwt)->tuninfo.traceh); return &(ioam6_lwt_state(lwt)->tuninfo.traceh);
} }
static const struct nla_policy ioam6_iptunnel_policy[IOAM6_IPTUNNEL_MAX + 1] = { 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_DST] = NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)),
[IOAM6_IPTUNNEL_TRACE] = NLA_POLICY_EXACT_LEN(sizeof(struct ioam6_trace_hdr)), [IOAM6_IPTUNNEL_TRACE] = NLA_POLICY_EXACT_LEN(sizeof(struct ioam6_trace_hdr)),
}; };
static int nla_put_ioam6_trace(struct sk_buff *skb, int attrtype,
struct ioam6_trace_hdr *trace)
{
struct ioam6_trace_hdr *data;
struct nlattr *nla;
int len;
len = sizeof(*trace);
nla = nla_reserve(skb, attrtype, len);
if (!nla)
return -EMSGSIZE;
data = nla_data(nla);
memcpy(data, trace, len);
return 0;
}
static bool ioam6_validate_trace_hdr(struct ioam6_trace_hdr *trace) static bool ioam6_validate_trace_hdr(struct ioam6_trace_hdr *trace)
{ {
u32 fields; u32 fields;
...@@ -97,9 +89,10 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla, ...@@ -97,9 +89,10 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla,
struct nlattr *tb[IOAM6_IPTUNNEL_MAX + 1]; struct nlattr *tb[IOAM6_IPTUNNEL_MAX + 1];
struct ioam6_lwt_encap *tuninfo; struct ioam6_lwt_encap *tuninfo;
struct ioam6_trace_hdr *trace; struct ioam6_trace_hdr *trace;
struct lwtunnel_state *s; struct lwtunnel_state *lwt;
int len_aligned; struct ioam6_lwt *ilwt;
int len, err; int len_aligned, err;
u8 mode;
if (family != AF_INET6) if (family != AF_INET6)
return -EINVAL; return -EINVAL;
...@@ -109,6 +102,16 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla, ...@@ -109,6 +102,16 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla,
if (err < 0) if (err < 0)
return err; return err;
if (!tb[IOAM6_IPTUNNEL_MODE])
mode = IOAM6_IPTUNNEL_MODE_INLINE;
else
mode = nla_get_u8(tb[IOAM6_IPTUNNEL_MODE]);
if (!tb[IOAM6_IPTUNNEL_DST] && mode != IOAM6_IPTUNNEL_MODE_INLINE) {
NL_SET_ERR_MSG(extack, "this mode needs a tunnel destination");
return -EINVAL;
}
if (!tb[IOAM6_IPTUNNEL_TRACE]) { if (!tb[IOAM6_IPTUNNEL_TRACE]) {
NL_SET_ERR_MSG(extack, "missing trace"); NL_SET_ERR_MSG(extack, "missing trace");
return -EINVAL; return -EINVAL;
...@@ -121,15 +124,24 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla, ...@@ -121,15 +124,24 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla,
return -EINVAL; return -EINVAL;
} }
len = sizeof(*tuninfo) + trace->remlen * 4; len_aligned = ALIGN(trace->remlen * 4, 8);
len_aligned = ALIGN(len, 8); lwt = lwtunnel_state_alloc(sizeof(*ilwt) + len_aligned);
if (!lwt)
s = lwtunnel_state_alloc(len_aligned);
if (!s)
return -ENOMEM; return -ENOMEM;
tuninfo = ioam6_lwt_info(s); ilwt = ioam6_lwt_state(lwt);
tuninfo->eh.hdrlen = (len_aligned >> 3) - 1; err = dst_cache_init(&ilwt->cache, GFP_ATOMIC);
if (err) {
kfree(lwt);
return err;
}
ilwt->mode = mode;
if (tb[IOAM6_IPTUNNEL_DST])
ilwt->tundst = nla_get_in6_addr(tb[IOAM6_IPTUNNEL_DST]);
tuninfo = ioam6_lwt_info(lwt);
tuninfo->eh.hdrlen = ((sizeof(*tuninfo) + len_aligned) >> 3) - 1;
tuninfo->pad[0] = IPV6_TLV_PADN; tuninfo->pad[0] = IPV6_TLV_PADN;
tuninfo->ioamh.type = IOAM6_TYPE_PREALLOC; tuninfo->ioamh.type = IOAM6_TYPE_PREALLOC;
tuninfo->ioamh.opt_type = IPV6_TLV_IOAM; tuninfo->ioamh.opt_type = IPV6_TLV_IOAM;
...@@ -138,27 +150,39 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla, ...@@ -138,27 +150,39 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla,
memcpy(&tuninfo->traceh, trace, sizeof(*trace)); memcpy(&tuninfo->traceh, trace, sizeof(*trace));
len = len_aligned - len; if (len_aligned - trace->remlen * 4) {
if (len == 1) {
tuninfo->traceh.data[trace->remlen * 4] = IPV6_TLV_PAD1;
} else if (len > 0) {
tuninfo->traceh.data[trace->remlen * 4] = IPV6_TLV_PADN; tuninfo->traceh.data[trace->remlen * 4] = IPV6_TLV_PADN;
tuninfo->traceh.data[trace->remlen * 4 + 1] = len - 2; tuninfo->traceh.data[trace->remlen * 4 + 1] = 2;
} }
s->type = LWTUNNEL_ENCAP_IOAM6; lwt->type = LWTUNNEL_ENCAP_IOAM6;
s->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT; lwt->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT;
*ts = s; *ts = lwt;
return 0; return 0;
} }
static int ioam6_do_inline(struct sk_buff *skb, struct ioam6_lwt_encap *tuninfo) static int ioam6_do_fill(struct net *net, struct sk_buff *skb)
{ {
struct ioam6_trace_hdr *trace; struct ioam6_trace_hdr *trace;
struct ipv6hdr *oldhdr, *hdr;
struct ioam6_namespace *ns; struct ioam6_namespace *ns;
trace = (struct ioam6_trace_hdr *)(skb_transport_header(skb)
+ sizeof(struct ipv6_hopopt_hdr) + 2
+ sizeof(struct ioam6_hdr));
ns = ioam6_namespace(net, trace->namespace_id);
if (ns)
ioam6_fill_trace_data(skb, ns, trace, false);
return 0;
}
static int ioam6_do_inline(struct net *net, struct sk_buff *skb,
struct ioam6_lwt_encap *tuninfo)
{
struct ipv6hdr *oldhdr, *hdr;
int hdrlen, err; int hdrlen, err;
hdrlen = (tuninfo->eh.hdrlen + 1) << 3; hdrlen = (tuninfo->eh.hdrlen + 1) << 3;
...@@ -187,80 +211,200 @@ static int ioam6_do_inline(struct sk_buff *skb, struct ioam6_lwt_encap *tuninfo) ...@@ -187,80 +211,200 @@ static int ioam6_do_inline(struct sk_buff *skb, struct ioam6_lwt_encap *tuninfo)
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));
trace = (struct ioam6_trace_hdr *)(skb_transport_header(skb) return ioam6_do_fill(net, skb);
+ sizeof(struct ipv6_hopopt_hdr) + 2 }
+ sizeof(struct ioam6_hdr));
ns = ioam6_namespace(dev_net(skb_dst(skb)->dev), trace->namespace_id); static int ioam6_do_encap(struct net *net, struct sk_buff *skb,
if (ns) struct ioam6_lwt_encap *tuninfo,
ioam6_fill_trace_data(skb, ns, trace); struct in6_addr *tundst)
{
struct dst_entry *dst = skb_dst(skb);
struct ipv6hdr *hdr, *inner_hdr;
int hdrlen, len, err;
return 0; hdrlen = (tuninfo->eh.hdrlen + 1) << 3;
len = sizeof(*hdr) + hdrlen;
err = skb_cow_head(skb, len + skb->mac_len);
if (unlikely(err))
return err;
inner_hdr = ipv6_hdr(skb);
skb_push(skb, len);
skb_reset_network_header(skb);
skb_mac_header_rebuild(skb);
skb_set_transport_header(skb, sizeof(*hdr));
tuninfo->eh.nexthdr = NEXTHDR_IPV6;
memcpy(skb_transport_header(skb), (u8 *)tuninfo, hdrlen);
hdr = ipv6_hdr(skb);
memcpy(hdr, inner_hdr, sizeof(*hdr));
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);
skb_postpush_rcsum(skb, hdr, len);
return ioam6_do_fill(net, skb);
} }
static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb) static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{ {
struct lwtunnel_state *lwt = skb_dst(skb)->lwtstate; struct dst_entry *dst = skb_dst(skb);
struct in6_addr orig_daddr;
struct ioam6_lwt *ilwt;
int err = -EINVAL; int err = -EINVAL;
if (skb->protocol != htons(ETH_P_IPV6)) if (skb->protocol != htons(ETH_P_IPV6))
goto drop; goto drop;
/* Only for packets we send and ilwt = ioam6_lwt_state(dst->lwtstate);
* that do not contain a Hop-by-Hop yet orig_daddr = ipv6_hdr(skb)->daddr;
*/
if (skb->dev || ipv6_hdr(skb)->nexthdr == NEXTHDR_HOP) switch (ilwt->mode) {
goto out; case IOAM6_IPTUNNEL_MODE_INLINE:
do_inline:
err = ioam6_do_inline(skb, ioam6_lwt_info(lwt)); /* Direct insertion - if there is no Hop-by-Hop yet */
if (unlikely(err)) if (ipv6_hdr(skb)->nexthdr == NEXTHDR_HOP)
goto out;
err = ioam6_do_inline(net, skb, &ilwt->tuninfo);
if (unlikely(err))
goto drop;
break;
case IOAM6_IPTUNNEL_MODE_ENCAP:
do_encap:
/* Encapsulation (ip6ip6) */
err = ioam6_do_encap(net, skb, &ilwt->tuninfo, &ilwt->tundst);
if (unlikely(err))
goto drop;
break;
case IOAM6_IPTUNNEL_MODE_AUTO:
/* Automatic (RFC8200 compliant):
* - local packets -> INLINE mode
* - in-transit packets -> ENCAP mode
*/
if (!skb->dev)
goto do_inline;
goto do_encap;
default:
goto drop; goto drop;
}
err = skb_cow_head(skb, LL_RESERVED_SPACE(skb_dst(skb)->dev)); err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev));
if (unlikely(err)) if (unlikely(err))
goto drop; goto drop;
if (!ipv6_addr_equal(&orig_daddr, &ipv6_hdr(skb)->daddr)) {
preempt_disable();
dst = dst_cache_get(&ilwt->cache);
preempt_enable();
if (unlikely(!dst)) {
struct ipv6hdr *hdr = ipv6_hdr(skb);
struct flowi6 fl6;
memset(&fl6, 0, sizeof(fl6));
fl6.daddr = hdr->daddr;
fl6.saddr = hdr->saddr;
fl6.flowlabel = ip6_flowinfo(hdr);
fl6.flowi6_mark = skb->mark;
fl6.flowi6_proto = hdr->nexthdr;
dst = ip6_route_output(net, NULL, &fl6);
if (dst->error) {
err = dst->error;
dst_release(dst);
goto drop;
}
preempt_disable();
dst_cache_set_ip6(&ilwt->cache, dst, &fl6.saddr);
preempt_enable();
}
skb_dst_drop(skb);
skb_dst_set(skb, dst);
return dst_output(net, sk, skb);
}
out: out:
return lwt->orig_output(net, sk, skb); return dst->lwtstate->orig_output(net, sk, skb);
drop: drop:
kfree_skb(skb); kfree_skb(skb);
return err; return err;
} }
static void ioam6_destroy_state(struct lwtunnel_state *lwt)
{
dst_cache_destroy(&ioam6_lwt_state(lwt)->cache);
}
static int ioam6_fill_encap_info(struct sk_buff *skb, static int ioam6_fill_encap_info(struct sk_buff *skb,
struct lwtunnel_state *lwtstate) struct lwtunnel_state *lwtstate)
{ {
struct ioam6_trace_hdr *trace = ioam6_trace(lwtstate); struct ioam6_lwt *ilwt = ioam6_lwt_state(lwtstate);
int err;
if (nla_put_ioam6_trace(skb, IOAM6_IPTUNNEL_TRACE, trace)) err = nla_put_u8(skb, IOAM6_IPTUNNEL_MODE, ilwt->mode);
return -EMSGSIZE; if (err)
goto ret;
return 0; if (ilwt->mode != IOAM6_IPTUNNEL_MODE_INLINE) {
err = nla_put_in6_addr(skb, IOAM6_IPTUNNEL_DST, &ilwt->tundst);
if (err)
goto ret;
}
err = nla_put(skb, IOAM6_IPTUNNEL_TRACE, sizeof(ilwt->tuninfo.traceh),
&ilwt->tuninfo.traceh);
ret:
return err;
} }
static int ioam6_encap_nlsize(struct lwtunnel_state *lwtstate) static int ioam6_encap_nlsize(struct lwtunnel_state *lwtstate)
{ {
struct ioam6_trace_hdr *trace = ioam6_trace(lwtstate); struct ioam6_lwt *ilwt = ioam6_lwt_state(lwtstate);
int nlsize;
nlsize = nla_total_size(sizeof(ilwt->mode)) +
nla_total_size(sizeof(ilwt->tuninfo.traceh));
return nla_total_size(sizeof(*trace)); if (ilwt->mode != IOAM6_IPTUNNEL_MODE_INLINE)
nlsize += nla_total_size(sizeof(ilwt->tundst));
return nlsize;
} }
static int ioam6_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) static int ioam6_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)
{ {
struct ioam6_trace_hdr *a_hdr = ioam6_trace(a); struct ioam6_trace_hdr *trace_a = ioam6_lwt_trace(a);
struct ioam6_trace_hdr *b_hdr = ioam6_trace(b); struct ioam6_trace_hdr *trace_b = ioam6_lwt_trace(b);
struct ioam6_lwt *ilwt_a = ioam6_lwt_state(a);
return (a_hdr->namespace_id != b_hdr->namespace_id); struct ioam6_lwt *ilwt_b = ioam6_lwt_state(b);
return (ilwt_a->mode != ilwt_b->mode ||
(ilwt_a->mode != IOAM6_IPTUNNEL_MODE_INLINE &&
!ipv6_addr_equal(&ilwt_a->tundst, &ilwt_b->tundst)) ||
trace_a->namespace_id != trace_b->namespace_id);
} }
static const struct lwtunnel_encap_ops ioam6_iptun_ops = { static const struct lwtunnel_encap_ops ioam6_iptun_ops = {
.build_state = ioam6_build_state, .build_state = ioam6_build_state,
.destroy_state = ioam6_destroy_state,
.output = ioam6_output, .output = ioam6_output,
.fill_encap = ioam6_fill_encap_info, .fill_encap = ioam6_fill_encap_info,
.get_encap_size = ioam6_encap_nlsize, .get_encap_size = ioam6_encap_nlsize,
.cmp_encap = ioam6_encap_cmp, .cmp_encap = ioam6_encap_cmp,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
int __init ioam6_iptunnel_init(void) int __init ioam6_iptunnel_init(void)
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
# This script evaluates the IOAM insertion for IPv6 by checking the IOAM data # This script evaluates the IOAM insertion for IPv6 by checking the IOAM data
# consistency directly inside packets on the receiver side. Tests are divided # consistency directly inside packets on the receiver side. Tests are divided
# into three categories: OUTPUT (evaluates the IOAM processing by the sender), # into three categories: OUTPUT (evaluates the IOAM processing by the sender),
# INPUT (evaluates the IOAM processing by the receiver) and GLOBAL (evaluates # INPUT (evaluates the IOAM processing by a receiver) and GLOBAL (evaluates
# wider use cases that do not fall into the other two categories). Both OUTPUT # wider use cases that do not fall into the other two categories). Both OUTPUT
# and INPUT tests only use a two-node topology (alpha and beta), while GLOBAL # and INPUT tests only use a two-node topology (alpha and beta), while GLOBAL
# tests use the entire three-node topology (alpha, beta, gamma). Each test is # tests use the entire three-node topology (alpha, beta, gamma). Each test is
...@@ -200,7 +200,7 @@ check_kernel_compatibility() ...@@ -200,7 +200,7 @@ check_kernel_compatibility()
ip -netns ioam-tmp-node link set veth0 up ip -netns ioam-tmp-node link set veth0 up
ip -netns ioam-tmp-node link set veth1 up ip -netns ioam-tmp-node link set veth1 up
ip -netns ioam-tmp-node ioam namespace add 0 &>/dev/null ip -netns ioam-tmp-node ioam namespace add 0
ns_ad=$? ns_ad=$?
ip -netns ioam-tmp-node ioam namespace show | grep -q "namespace 0" ip -netns ioam-tmp-node ioam namespace show | grep -q "namespace 0"
...@@ -214,11 +214,11 @@ check_kernel_compatibility() ...@@ -214,11 +214,11 @@ check_kernel_compatibility()
exit 1 exit 1
fi fi
ip -netns ioam-tmp-node route add db02::/64 encap ioam6 trace prealloc \ ip -netns ioam-tmp-node route add db02::/64 encap ioam6 mode inline \
type 0x800000 ns 0 size 4 dev veth0 &>/dev/null trace prealloc type 0x800000 ns 0 size 4 dev veth0
tr_ad=$? tr_ad=$?
ip -netns ioam-tmp-node -6 route | grep -q "encap ioam6 trace" ip -netns ioam-tmp-node -6 route | grep -q "encap ioam6"
tr_sh=$? tr_sh=$?
if [[ $tr_ad != 0 || $tr_sh != 0 ]] if [[ $tr_ad != 0 || $tr_sh != 0 ]]
...@@ -232,6 +232,30 @@ check_kernel_compatibility() ...@@ -232,6 +232,30 @@ check_kernel_compatibility()
ip link del veth0 2>/dev/null || true ip link del veth0 2>/dev/null || true
ip netns del ioam-tmp-node || true ip netns del ioam-tmp-node || true
lsmod | grep -q "ip6_tunnel"
ip6tnl_loaded=$?
if [ $ip6tnl_loaded = 0 ]
then
encap_tests=0
else
modprobe ip6_tunnel &>/dev/null
lsmod | grep -q "ip6_tunnel"
encap_tests=$?
if [ $encap_tests != 0 ]
then
ip a | grep -q "ip6tnl0"
encap_tests=$?
if [ $encap_tests != 0 ]
then
echo "Note: ip6_tunnel not found neither as a module nor inside the" \
"kernel, tests that require it (encap mode) will be omitted"
fi
fi
fi
} }
cleanup() cleanup()
...@@ -242,6 +266,11 @@ cleanup() ...@@ -242,6 +266,11 @@ cleanup()
ip netns del ioam-node-alpha || true ip netns del ioam-node-alpha || true
ip netns del ioam-node-beta || true ip netns del ioam-node-beta || true
ip netns del ioam-node-gamma || true ip netns del ioam-node-gamma || true
if [ $ip6tnl_loaded != 0 ]
then
modprobe -r ip6_tunnel 2>/dev/null || true
fi
} }
setup() setup()
...@@ -329,6 +358,12 @@ log_test_failed() ...@@ -329,6 +358,12 @@ log_test_failed()
printf "TEST: %-60s [FAIL]\n" "${desc}" printf "TEST: %-60s [FAIL]\n" "${desc}"
} }
log_results()
{
echo "- Tests passed: ${npassed}"
echo "- Tests failed: ${nfailed}"
}
run_test() run_test()
{ {
local name=$1 local name=$1
...@@ -349,16 +384,26 @@ run_test() ...@@ -349,16 +384,26 @@ run_test()
ip netns exec $node_src ping6 -t 64 -c 1 -W 1 $ip6_dst &>/dev/null ip netns exec $node_src ping6 -t 64 -c 1 -W 1 $ip6_dst &>/dev/null
if [ $? != 0 ] if [ $? != 0 ]
then then
nfailed=$((nfailed+1))
log_test_failed "${desc}" log_test_failed "${desc}"
kill -2 $spid &>/dev/null kill -2 $spid &>/dev/null
else else
wait $spid wait $spid
[ $? = 0 ] && log_test_passed "${desc}" || log_test_failed "${desc}" if [ $? = 0 ]
then
npassed=$((npassed+1))
log_test_passed "${desc}"
else
nfailed=$((nfailed+1))
log_test_failed "${desc}"
fi
fi fi
} }
run() run()
{ {
echo
printf "%0.s-" {1..74}
echo echo
echo "OUTPUT tests" echo "OUTPUT tests"
printf "%0.s-" {1..74} printf "%0.s-" {1..74}
...@@ -369,7 +414,8 @@ run() ...@@ -369,7 +414,8 @@ run()
for t in $TESTS_OUTPUT for t in $TESTS_OUTPUT
do do
$t $t "inline"
[ $encap_tests = 0 ] && $t "encap"
done done
# clean OUTPUT settings # clean OUTPUT settings
...@@ -377,6 +423,8 @@ run() ...@@ -377,6 +423,8 @@ run()
ip -netns ioam-node-alpha route change db01::/64 dev veth0 ip -netns ioam-node-alpha route change db01::/64 dev veth0
echo
printf "%0.s-" {1..74}
echo echo
echo "INPUT tests" echo "INPUT tests"
printf "%0.s-" {1..74} printf "%0.s-" {1..74}
...@@ -387,7 +435,8 @@ run() ...@@ -387,7 +435,8 @@ run()
for t in $TESTS_INPUT for t in $TESTS_INPUT
do do
$t $t "inline"
[ $encap_tests = 0 ] && $t "encap"
done done
# clean INPUT settings # clean INPUT settings
...@@ -396,7 +445,8 @@ run() ...@@ -396,7 +445,8 @@ run()
ip -netns ioam-node-alpha ioam namespace set 123 schema ${ALPHA[8]} ip -netns ioam-node-alpha ioam namespace set 123 schema ${ALPHA[8]}
ip -netns ioam-node-alpha route change db01::/64 dev veth0 ip -netns ioam-node-alpha route change db01::/64 dev veth0
echo
printf "%0.s-" {1..74}
echo echo
echo "GLOBAL tests" echo "GLOBAL tests"
printf "%0.s-" {1..74} printf "%0.s-" {1..74}
...@@ -404,8 +454,12 @@ run() ...@@ -404,8 +454,12 @@ run()
for t in $TESTS_GLOBAL for t in $TESTS_GLOBAL
do do
$t $t "inline"
[ $encap_tests = 0 ] && $t "encap"
done done
echo
log_results
} }
bit2type=( bit2type=(
...@@ -431,11 +485,16 @@ out_undef_ns() ...@@ -431,11 +485,16 @@ out_undef_ns()
############################################################################## ##############################################################################
local desc="Unknown IOAM namespace" local desc="Unknown IOAM namespace"
ip -netns ioam-node-alpha route change db01::/64 encap ioam6 trace prealloc \ [ "$1" = "encap" ] && mode="$1 tundst db01::1" || mode="$1"
type 0x800000 ns 0 size 4 dev veth0 [ "$1" = "encap" ] && ip -netns ioam-node-beta link set ip6tnl0 up
ip -netns ioam-node-alpha route change db01::/64 encap ioam6 mode $mode \
trace prealloc type 0x800000 ns 0 size 4 dev veth0
run_test ${FUNCNAME[0]} "${desc}" ioam-node-alpha ioam-node-beta db01::2 \ run_test ${FUNCNAME[0]} "${desc} ($1 mode)" ioam-node-alpha ioam-node-beta \
db01::1 veth0 0x800000 0 db01::2 db01::1 veth0 0x800000 0
[ "$1" = "encap" ] && ip -netns ioam-node-beta link set ip6tnl0 down
} }
out_no_room() out_no_room()
...@@ -446,11 +505,16 @@ out_no_room() ...@@ -446,11 +505,16 @@ out_no_room()
############################################################################## ##############################################################################
local desc="Missing trace room" local desc="Missing trace room"
ip -netns ioam-node-alpha route change db01::/64 encap ioam6 trace prealloc \ [ "$1" = "encap" ] && mode="$1 tundst db01::1" || mode="$1"
type 0xc00000 ns 123 size 4 dev veth0 [ "$1" = "encap" ] && ip -netns ioam-node-beta link set ip6tnl0 up
ip -netns ioam-node-alpha route change db01::/64 encap ioam6 mode $mode \
trace prealloc type 0xc00000 ns 123 size 4 dev veth0
run_test ${FUNCNAME[0]} "${desc} ($1 mode)" ioam-node-alpha ioam-node-beta \
db01::2 db01::1 veth0 0xc00000 123
run_test ${FUNCNAME[0]} "${desc}" ioam-node-alpha ioam-node-beta db01::2 \ [ "$1" = "encap" ] && ip -netns ioam-node-beta link set ip6tnl0 down
db01::1 veth0 0xc00000 123
} }
out_bits() out_bits()
...@@ -465,15 +529,21 @@ out_bits() ...@@ -465,15 +529,21 @@ out_bits()
local tmp=${bit2size[22]} local tmp=${bit2size[22]}
bit2size[22]=$(( $tmp + ${#ALPHA[9]} + ((4 - (${#ALPHA[9]} % 4)) % 4) )) bit2size[22]=$(( $tmp + ${#ALPHA[9]} + ((4 - (${#ALPHA[9]} % 4)) % 4) ))
[ "$1" = "encap" ] && mode="$1 tundst db01::1" || mode="$1"
[ "$1" = "encap" ] && ip -netns ioam-node-beta link set ip6tnl0 up
for i in {0..22} for i in {0..22}
do do
ip -netns ioam-node-alpha route change db01::/64 encap ioam6 trace \ ip -netns ioam-node-alpha route change db01::/64 encap ioam6 mode $mode \
prealloc type ${bit2type[$i]} ns 123 size ${bit2size[$i]} dev veth0 trace prealloc type ${bit2type[$i]} ns 123 size ${bit2size[$i]} \
dev veth0
run_test "out_bit$i" "${desc/<n>/$i}" ioam-node-alpha ioam-node-beta \ run_test "out_bit$i" "${desc/<n>/$i} ($1 mode)" ioam-node-alpha \
db01::2 db01::1 veth0 ${bit2type[$i]} 123 ioam-node-beta db01::2 db01::1 veth0 ${bit2type[$i]} 123
done done
[ "$1" = "encap" ] && ip -netns ioam-node-beta link set ip6tnl0 down
bit2size[22]=$tmp bit2size[22]=$tmp
} }
...@@ -485,11 +555,16 @@ out_full_supp_trace() ...@@ -485,11 +555,16 @@ out_full_supp_trace()
############################################################################## ##############################################################################
local desc="Full supported trace" local desc="Full supported trace"
ip -netns ioam-node-alpha route change db01::/64 encap ioam6 trace prealloc \ [ "$1" = "encap" ] && mode="$1 tundst db01::1" || mode="$1"
type 0xfff002 ns 123 size 100 dev veth0 [ "$1" = "encap" ] && ip -netns ioam-node-beta link set ip6tnl0 up
run_test ${FUNCNAME[0]} "${desc}" ioam-node-alpha ioam-node-beta db01::2 \ ip -netns ioam-node-alpha route change db01::/64 encap ioam6 mode $mode \
db01::1 veth0 0xfff002 123 trace prealloc type 0xfff002 ns 123 size 100 dev veth0
run_test ${FUNCNAME[0]} "${desc} ($1 mode)" ioam-node-alpha ioam-node-beta \
db01::2 db01::1 veth0 0xfff002 123
[ "$1" = "encap" ] && ip -netns ioam-node-beta link set ip6tnl0 down
} }
...@@ -510,11 +585,16 @@ in_undef_ns() ...@@ -510,11 +585,16 @@ in_undef_ns()
############################################################################## ##############################################################################
local desc="Unknown IOAM namespace" local desc="Unknown IOAM namespace"
ip -netns ioam-node-alpha route change db01::/64 encap ioam6 trace prealloc \ [ "$1" = "encap" ] && mode="$1 tundst db01::1" || mode="$1"
type 0x800000 ns 0 size 4 dev veth0 [ "$1" = "encap" ] && ip -netns ioam-node-beta link set ip6tnl0 up
ip -netns ioam-node-alpha route change db01::/64 encap ioam6 mode $mode \
trace prealloc type 0x800000 ns 0 size 4 dev veth0
run_test ${FUNCNAME[0]} "${desc}" ioam-node-alpha ioam-node-beta db01::2 \ run_test ${FUNCNAME[0]} "${desc} ($1 mode)" ioam-node-alpha ioam-node-beta \
db01::1 veth0 0x800000 0 db01::2 db01::1 veth0 0x800000 0
[ "$1" = "encap" ] && ip -netns ioam-node-beta link set ip6tnl0 down
} }
in_no_room() in_no_room()
...@@ -525,11 +605,16 @@ in_no_room() ...@@ -525,11 +605,16 @@ in_no_room()
############################################################################## ##############################################################################
local desc="Missing trace room" local desc="Missing trace room"
ip -netns ioam-node-alpha route change db01::/64 encap ioam6 trace prealloc \ [ "$1" = "encap" ] && mode="$1 tundst db01::1" || mode="$1"
type 0xc00000 ns 123 size 4 dev veth0 [ "$1" = "encap" ] && ip -netns ioam-node-beta link set ip6tnl0 up
ip -netns ioam-node-alpha route change db01::/64 encap ioam6 mode $mode \
trace prealloc type 0xc00000 ns 123 size 4 dev veth0
run_test ${FUNCNAME[0]} "${desc}" ioam-node-alpha ioam-node-beta db01::2 \ run_test ${FUNCNAME[0]} "${desc} ($1 mode)" ioam-node-alpha ioam-node-beta \
db01::1 veth0 0xc00000 123 db01::2 db01::1 veth0 0xc00000 123
[ "$1" = "encap" ] && ip -netns ioam-node-beta link set ip6tnl0 down
} }
in_bits() in_bits()
...@@ -544,15 +629,21 @@ in_bits() ...@@ -544,15 +629,21 @@ in_bits()
local tmp=${bit2size[22]} local tmp=${bit2size[22]}
bit2size[22]=$(( $tmp + ${#BETA[9]} + ((4 - (${#BETA[9]} % 4)) % 4) )) bit2size[22]=$(( $tmp + ${#BETA[9]} + ((4 - (${#BETA[9]} % 4)) % 4) ))
[ "$1" = "encap" ] && mode="$1 tundst db01::1" || mode="$1"
[ "$1" = "encap" ] && ip -netns ioam-node-beta link set ip6tnl0 up
for i in {0..22} for i in {0..22}
do do
ip -netns ioam-node-alpha route change db01::/64 encap ioam6 trace \ ip -netns ioam-node-alpha route change db01::/64 encap ioam6 mode $mode \
prealloc type ${bit2type[$i]} ns 123 size ${bit2size[$i]} dev veth0 trace prealloc type ${bit2type[$i]} ns 123 size ${bit2size[$i]} \
dev veth0
run_test "in_bit$i" "${desc/<n>/$i}" ioam-node-alpha ioam-node-beta \ run_test "in_bit$i" "${desc/<n>/$i} ($1 mode)" ioam-node-alpha \
db01::2 db01::1 veth0 ${bit2type[$i]} 123 ioam-node-beta db01::2 db01::1 veth0 ${bit2type[$i]} 123
done done
[ "$1" = "encap" ] && ip -netns ioam-node-beta link set ip6tnl0 down
bit2size[22]=$tmp bit2size[22]=$tmp
} }
...@@ -569,11 +660,16 @@ in_oflag() ...@@ -569,11 +660,16 @@ in_oflag()
# back the IOAM namespace that was previously configured on the sender. # back the IOAM namespace that was previously configured on the sender.
ip -netns ioam-node-alpha ioam namespace add 123 ip -netns ioam-node-alpha ioam namespace add 123
ip -netns ioam-node-alpha route change db01::/64 encap ioam6 trace prealloc \ [ "$1" = "encap" ] && mode="$1 tundst db01::1" || mode="$1"
type 0xc00000 ns 123 size 4 dev veth0 [ "$1" = "encap" ] && ip -netns ioam-node-beta link set ip6tnl0 up
ip -netns ioam-node-alpha route change db01::/64 encap ioam6 mode $mode \
trace prealloc type 0xc00000 ns 123 size 4 dev veth0
run_test ${FUNCNAME[0]} "${desc} ($1 mode)" ioam-node-alpha ioam-node-beta \
db01::2 db01::1 veth0 0xc00000 123
run_test ${FUNCNAME[0]} "${desc}" ioam-node-alpha ioam-node-beta db01::2 \ [ "$1" = "encap" ] && ip -netns ioam-node-beta link set ip6tnl0 down
db01::1 veth0 0xc00000 123
# And we clean the exception for this test to get things back to normal for # And we clean the exception for this test to get things back to normal for
# other INPUT tests # other INPUT tests
...@@ -588,11 +684,16 @@ in_full_supp_trace() ...@@ -588,11 +684,16 @@ in_full_supp_trace()
############################################################################## ##############################################################################
local desc="Full supported trace" local desc="Full supported trace"
ip -netns ioam-node-alpha route change db01::/64 encap ioam6 trace prealloc \ [ "$1" = "encap" ] && mode="$1 tundst db01::1" || mode="$1"
type 0xfff002 ns 123 size 80 dev veth0 [ "$1" = "encap" ] && ip -netns ioam-node-beta link set ip6tnl0 up
run_test ${FUNCNAME[0]} "${desc}" ioam-node-alpha ioam-node-beta db01::2 \ ip -netns ioam-node-alpha route change db01::/64 encap ioam6 mode $mode \
db01::1 veth0 0xfff002 123 trace prealloc type 0xfff002 ns 123 size 80 dev veth0
run_test ${FUNCNAME[0]} "${desc} ($1 mode)" ioam-node-alpha ioam-node-beta \
db01::2 db01::1 veth0 0xfff002 123
[ "$1" = "encap" ] && ip -netns ioam-node-beta link set ip6tnl0 down
} }
...@@ -611,11 +712,16 @@ fwd_full_supp_trace() ...@@ -611,11 +712,16 @@ fwd_full_supp_trace()
############################################################################## ##############################################################################
local desc="Forward - Full supported trace" local desc="Forward - Full supported trace"
ip -netns ioam-node-alpha route change db02::/64 encap ioam6 trace prealloc \ [ "$1" = "encap" ] && mode="$1 tundst db02::2" || mode="$1"
type 0xfff002 ns 123 size 244 via db01::1 dev veth0 [ "$1" = "encap" ] && ip -netns ioam-node-gamma link set ip6tnl0 up
ip -netns ioam-node-alpha route change db02::/64 encap ioam6 mode $mode \
trace prealloc type 0xfff002 ns 123 size 244 via db01::1 dev veth0
run_test ${FUNCNAME[0]} "${desc}" ioam-node-alpha ioam-node-gamma db01::2 \ run_test ${FUNCNAME[0]} "${desc} ($1 mode)" ioam-node-alpha ioam-node-gamma \
db02::2 veth0 0xfff002 123 db01::2 db02::2 veth0 0xfff002 123
[ "$1" = "encap" ] && ip -netns ioam-node-gamma link set ip6tnl0 down
} }
...@@ -625,6 +731,9 @@ fwd_full_supp_trace() ...@@ -625,6 +731,9 @@ fwd_full_supp_trace()
# # # #
################################################################################ ################################################################################
npassed=0
nfailed=0
if [ "$(id -u)" -ne 0 ] if [ "$(id -u)" -ne 0 ]
then then
echo "SKIP: Need root privileges" echo "SKIP: Need root privileges"
......
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