diff mbox series

[iproute2-next,1/2] Add support for IOAM encap modes

Message ID 20211004130651.13571-2-justin.iurman@uliege.be (mailing list archive)
State Superseded
Delegated to: David Ahern
Headers show
Series Support for IOAM encap modes | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch

Commit Message

Justin Iurman Oct. 4, 2021, 1:06 p.m. UTC
This patch adds support for the three IOAM encap modes that were introduced:
inline, encap and auto.

Signed-off-by: Justin Iurman <justin.iurman@uliege.be>
---
 include/uapi/linux/ioam6_iptunnel.h |  29 ++++++
 ip/iproute_lwtunnel.c               | 142 ++++++++++++++++++++--------
 2 files changed, 129 insertions(+), 42 deletions(-)

Comments

David Ahern Oct. 5, 2021, 2:12 p.m. UTC | #1
On 10/4/21 7:06 AM, Justin Iurman wrote:
> diff --git a/ip/iproute_lwtunnel.c b/ip/iproute_lwtunnel.c
> index 218d5086..3641f9ef 100644
> --- a/ip/iproute_lwtunnel.c
> +++ b/ip/iproute_lwtunnel.c
> @@ -210,16 +210,54 @@ static void print_encap_rpl(FILE *fp, struct rtattr *encap)
>  	print_rpl_srh(fp, srh);
>  }
>  
> +static const char *ioam6_mode_types[] = {

I think you want to declare this of size IOAM6_IPTUNNEL_MODE_MAX + 1

> +	[IOAM6_IPTUNNEL_MODE_INLINE]	= "inline",
> +	[IOAM6_IPTUNNEL_MODE_ENCAP]	= "encap",
> +	[IOAM6_IPTUNNEL_MODE_AUTO]	= "auto",
> +};
> +
> +static const char *format_ioam6mode_type(int mode)
> +{
> +	if (mode < IOAM6_IPTUNNEL_MODE_MIN ||
> +	    mode > IOAM6_IPTUNNEL_MODE_MAX ||
> +	    !ioam6_mode_types[mode])

otherwise this check is not sufficient.

> +		return "<unknown>";
> +
> +	return ioam6_mode_types[mode];
> +}
> +
Justin Iurman Oct. 5, 2021, 2:45 p.m. UTC | #2
>> +static const char *ioam6_mode_types[] = {
> 
> I think you want to declare this of size IOAM6_IPTUNNEL_MODE_MAX + 1

This is automatically the case, see below explanation.

>> +	[IOAM6_IPTUNNEL_MODE_INLINE]	= "inline",
>> +	[IOAM6_IPTUNNEL_MODE_ENCAP]	= "encap",
>> +	[IOAM6_IPTUNNEL_MODE_AUTO]	= "auto",
>> +};
>> +
>> +static const char *format_ioam6mode_type(int mode)
>> +{
>> +	if (mode < IOAM6_IPTUNNEL_MODE_MIN ||
>> +	    mode > IOAM6_IPTUNNEL_MODE_MAX ||
>> +	    !ioam6_mode_types[mode])
> 
> otherwise this check is not sufficient.

Are you sure? I mean, both IOAM6_IPTUNNEL_MODE_MIN and IOAM6_IPTUNNEL_MODE_MAX respectively point to IOAM6_IPTUNNEL_MODE_INLINE and IOAM6_IPTUNNEL_MODE_AUTO. So, either the input mode is out of bound, or not defined in the array above (this one is not mandatory, but it ensures that the above array is updated accordingly with the uapi). So, what we have right now is:

__IOAM6_IPTUNNEL_MODE_MIN = 0
IOAM6_IPTUNNEL_MODE_INLINE = 1
IOAM6_IPTUNNEL_MODE_ENCAP = 2
IOAM6_IPTUNNEL_MODE_AUTO = 3
__IOAM6_IPTUNNEL_MODE_MAX = 4

IOAM6_IPTUNNEL_MODE_MIN = 1
IOAM6_IPTUNNEL_MODE_MAX = 3

ioam6_mode_types = {
  [0] (null)
  [1] "inline"
  [2] "encap"
  [3] "auto"
}

where its size is automatically/implicitly 4 (IOAM6_IPTUNNEL_MODE_MAX + 1).
David Ahern Oct. 5, 2021, 2:49 p.m. UTC | #3
On 10/5/21 8:45 AM, Justin Iurman wrote:
>>> +static const char *ioam6_mode_types[] = {
>>
>> I think you want to declare this of size IOAM6_IPTUNNEL_MODE_MAX + 1
> 
> This is automatically the case, see below explanation.
> 
>>> +	[IOAM6_IPTUNNEL_MODE_INLINE]	= "inline",
>>> +	[IOAM6_IPTUNNEL_MODE_ENCAP]	= "encap",
>>> +	[IOAM6_IPTUNNEL_MODE_AUTO]	= "auto",
>>> +};
>>> +
>>> +static const char *format_ioam6mode_type(int mode)
>>> +{
>>> +	if (mode < IOAM6_IPTUNNEL_MODE_MIN ||
>>> +	    mode > IOAM6_IPTUNNEL_MODE_MAX ||
>>> +	    !ioam6_mode_types[mode])
>>
>> otherwise this check is not sufficient.
> 
> Are you sure? I mean, both IOAM6_IPTUNNEL_MODE_MIN and IOAM6_IPTUNNEL_MODE_MAX respectively point to IOAM6_IPTUNNEL_MODE_INLINE and IOAM6_IPTUNNEL_MODE_AUTO. So, either the input mode is out of bound, or not defined in the array above (this one is not mandatory, but it ensures that the above array is updated accordingly with the uapi). So, what we have right now is:
> 
> __IOAM6_IPTUNNEL_MODE_MIN = 0
> IOAM6_IPTUNNEL_MODE_INLINE = 1
> IOAM6_IPTUNNEL_MODE_ENCAP = 2
> IOAM6_IPTUNNEL_MODE_AUTO = 3
> __IOAM6_IPTUNNEL_MODE_MAX = 4
> 
> IOAM6_IPTUNNEL_MODE_MIN = 1
> IOAM6_IPTUNNEL_MODE_MAX = 3
> 
> ioam6_mode_types = {
>   [0] (null)
>   [1] "inline"
>   [2] "encap"
>   [3] "auto"
> }
> 
> where its size is automatically/implicitly 4 (IOAM6_IPTUNNEL_MODE_MAX + 1).
> 

today yes, but tomorrow no. ie,. a new feature is added to the header
file. Header file is updated in iproute2 as part of a header file sync
but the ioam6 code is not updated to expand ioam6_mode_types. Command is
then run on a system with the new feature so

    mode > IOAM6_IPTUNNEL_MODE_MAX

will pass but then

     !ioam6_mode_types[mode])

accesses an entry beyond the size of ioam6_mode_types.
Justin Iurman Oct. 5, 2021, 2:56 p.m. UTC | #4
>>>> +static const char *ioam6_mode_types[] = {
>>>
>>> I think you want to declare this of size IOAM6_IPTUNNEL_MODE_MAX + 1
>> 
>> This is automatically the case, see below explanation.
>> 
>>>> +	[IOAM6_IPTUNNEL_MODE_INLINE]	= "inline",
>>>> +	[IOAM6_IPTUNNEL_MODE_ENCAP]	= "encap",
>>>> +	[IOAM6_IPTUNNEL_MODE_AUTO]	= "auto",
>>>> +};
>>>> +
>>>> +static const char *format_ioam6mode_type(int mode)
>>>> +{
>>>> +	if (mode < IOAM6_IPTUNNEL_MODE_MIN ||
>>>> +	    mode > IOAM6_IPTUNNEL_MODE_MAX ||
>>>> +	    !ioam6_mode_types[mode])
>>>
>>> otherwise this check is not sufficient.
>> 
>> Are you sure? I mean, both IOAM6_IPTUNNEL_MODE_MIN and IOAM6_IPTUNNEL_MODE_MAX
>> respectively point to IOAM6_IPTUNNEL_MODE_INLINE and IOAM6_IPTUNNEL_MODE_AUTO.
>> So, either the input mode is out of bound, or not defined in the array above
>> (this one is not mandatory, but it ensures that the above array is updated
>> accordingly with the uapi). So, what we have right now is:
>> 
>> __IOAM6_IPTUNNEL_MODE_MIN = 0
>> IOAM6_IPTUNNEL_MODE_INLINE = 1
>> IOAM6_IPTUNNEL_MODE_ENCAP = 2
>> IOAM6_IPTUNNEL_MODE_AUTO = 3
>> __IOAM6_IPTUNNEL_MODE_MAX = 4
>> 
>> IOAM6_IPTUNNEL_MODE_MIN = 1
>> IOAM6_IPTUNNEL_MODE_MAX = 3
>> 
>> ioam6_mode_types = {
>>   [0] (null)
>>   [1] "inline"
>>   [2] "encap"
>>   [3] "auto"
>> }
>> 
>> where its size is automatically/implicitly 4 (IOAM6_IPTUNNEL_MODE_MAX + 1).
>> 
> 
> today yes, but tomorrow no. ie,. a new feature is added to the header
> file. Header file is updated in iproute2 as part of a header file sync
> but the ioam6 code is not updated to expand ioam6_mode_types. Command is
> then run on a system with the new feature so
> 
>    mode > IOAM6_IPTUNNEL_MODE_MAX
> 
> will pass but then
> 
>     !ioam6_mode_types[mode])
> 
> accesses an entry beyond the size of ioam6_mode_types.

Indeed, sorry. I'll send the fix in -v2.

Thanks!
diff mbox series

Patch

diff --git a/include/uapi/linux/ioam6_iptunnel.h b/include/uapi/linux/ioam6_iptunnel.h
index fdf52e66..7bd20212 100644
--- a/include/uapi/linux/ioam6_iptunnel.h
+++ b/include/uapi/linux/ioam6_iptunnel.h
@@ -9,9 +9,38 @@ 
 #ifndef _LINUX_IOAM6_IPTUNNEL_H
 #define _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 {
 	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_MAX,
 };
 
diff --git a/ip/iproute_lwtunnel.c b/ip/iproute_lwtunnel.c
index 218d5086..3641f9ef 100644
--- a/ip/iproute_lwtunnel.c
+++ b/ip/iproute_lwtunnel.c
@@ -210,16 +210,54 @@  static void print_encap_rpl(FILE *fp, struct rtattr *encap)
 	print_rpl_srh(fp, srh);
 }
 
+static const char *ioam6_mode_types[] = {
+	[IOAM6_IPTUNNEL_MODE_INLINE]	= "inline",
+	[IOAM6_IPTUNNEL_MODE_ENCAP]	= "encap",
+	[IOAM6_IPTUNNEL_MODE_AUTO]	= "auto",
+};
+
+static const char *format_ioam6mode_type(int mode)
+{
+	if (mode < IOAM6_IPTUNNEL_MODE_MIN ||
+	    mode > IOAM6_IPTUNNEL_MODE_MAX ||
+	    !ioam6_mode_types[mode])
+		return "<unknown>";
+
+	return ioam6_mode_types[mode];
+}
+
+static __u8 read_ioam6mode_type(const char *mode)
+{
+	__u8 i;
+
+	for (i = IOAM6_IPTUNNEL_MODE_MIN; i <= IOAM6_IPTUNNEL_MODE_MAX; i++) {
+		if (ioam6_mode_types[i] && !strcmp(mode, ioam6_mode_types[i]))
+			return i;
+	}
+
+	return 0;
+}
+
 static void print_encap_ioam6(FILE *fp, struct rtattr *encap)
 {
 	struct rtattr *tb[IOAM6_IPTUNNEL_MAX + 1];
 	struct ioam6_trace_hdr *trace;
+	__u8 mode;
 
 	parse_rtattr_nested(tb, IOAM6_IPTUNNEL_MAX, encap);
+	if (!tb[IOAM6_IPTUNNEL_MODE] || !tb[IOAM6_IPTUNNEL_TRACE])
+		return;
 
-	if (!tb[IOAM6_IPTUNNEL_TRACE])
+	mode = rta_getattr_u8(tb[IOAM6_IPTUNNEL_MODE]);
+	if (!tb[IOAM6_IPTUNNEL_DST] && mode != IOAM6_IPTUNNEL_MODE_INLINE)
 		return;
 
+	print_string(PRINT_ANY, "mode", "mode %s ", format_ioam6mode_type(mode));
+
+	if (mode != IOAM6_IPTUNNEL_MODE_INLINE)
+		print_string(PRINT_ANY, "tundst", "tundst %s ",
+			     rt_addr_n2a_rta(AF_INET6, tb[IOAM6_IPTUNNEL_DST]));
+
 	trace = RTA_DATA(tb[IOAM6_IPTUNNEL_TRACE]);
 
 	print_null(PRINT_ANY, "trace", "trace ", NULL);
@@ -884,23 +922,48 @@  out:
 static int parse_encap_ioam6(struct rtattr *rta, size_t len, int *argcp,
 			     char ***argvp)
 {
+	int ns_found = 0, argc = *argcp;
+	__u16 trace_ns, trace_size = 0;
 	struct ioam6_trace_hdr *trace;
 	char **argv = *argvp;
-	int argc = *argcp;
-	int ns_found = 0;
-	__u16 size = 0;
-	__u32 type = 0;
-	__u16 ns;
+	__u32 trace_type = 0;
+	inet_prefix addr;
+	__u8 mode;
 
-	trace = calloc(1, sizeof(*trace));
-	if (!trace)
-		return -1;
+	if (strcmp(*argv, "mode") != 0) {
+		mode = IOAM6_IPTUNNEL_MODE_INLINE;
+	} else {
+		NEXT_ARG();
 
-	if (strcmp(*argv, "trace"))
+		mode = read_ioam6mode_type(*argv);
+		if (!mode)
+			invarg("Invalid mode", *argv);
+
+		NEXT_ARG();
+	}
+
+	if (strcmp(*argv, "tundst") != 0) {
+		if (mode != IOAM6_IPTUNNEL_MODE_INLINE)
+			missarg("tundst");
+	} else {
+		if (mode == IOAM6_IPTUNNEL_MODE_INLINE)
+			invarg("Inline mode does not need tundst", *argv);
+
+		NEXT_ARG();
+
+		get_addr(&addr, *argv, AF_INET6);
+		if (addr.family != AF_INET6 || addr.bytelen != 16)
+			invarg("Invalid IPv6 address for tundst", *argv);
+
+		NEXT_ARG();
+	}
+
+	if (strcmp(*argv, "trace") != 0)
 		missarg("trace");
 
 	NEXT_ARG();
-	if (strcmp(*argv, "prealloc"))
+
+	if (strcmp(*argv, "prealloc") != 0)
 		missarg("prealloc");
 
 	while (NEXT_ARG_OK()) {
@@ -909,63 +972,58 @@  static int parse_encap_ioam6(struct rtattr *rta, size_t len, int *argcp,
 		if (strcmp(*argv, "type") == 0) {
 			NEXT_ARG();
 
-			if (type)
+			if (trace_type)
 				duparg2("type", *argv);
 
-			if (get_u32(&type, *argv, 0) || !type)
-				invarg("Invalid type", *argv);
-
-			trace->type_be32 = htonl(type << 8);
-
+			if (get_u32(&trace_type, *argv, 0) || !trace_type)
+				invarg("Invalid trace type", *argv);
 		} else if (strcmp(*argv, "ns") == 0) {
 			NEXT_ARG();
 
 			if (ns_found++)
 				duparg2("ns", *argv);
 
-			if (!type)
-				missarg("type");
-
-			if (get_u16(&ns, *argv, 0))
+			if (get_u16(&trace_ns, *argv, 0))
 				invarg("Invalid namespace ID", *argv);
-
-			trace->namespace_id = htons(ns);
-
 		} else if (strcmp(*argv, "size") == 0) {
 			NEXT_ARG();
 
-			if (size)
+			if (trace_size)
 				duparg2("size", *argv);
 
-			if (!type)
-				missarg("type");
-			if (!ns_found)
-				missarg("ns");
+			if (get_u16(&trace_size, *argv, 0) || !trace_size)
+				invarg("Invalid trace size", *argv);
 
-			if (get_u16(&size, *argv, 0) || !size)
-				invarg("Invalid size", *argv);
-
-			if (size % 4)
-				invarg("Size must be a 4-octet multiple", *argv);
-			if (size > IOAM6_TRACE_DATA_SIZE_MAX)
-				invarg("Size too big", *argv);
-
-			trace->remlen = (__u8)(size / 4);
+			if (trace_size % 4)
+				invarg("Trace size must be a 4-octet multiple",
+				       *argv);
 
+			if (trace_size > IOAM6_TRACE_DATA_SIZE_MAX)
+				invarg("Trace size is too big", *argv);
 		} else {
 			break;
 		}
 	}
 
-	if (!type)
+	if (!trace_type)
 		missarg("type");
 	if (!ns_found)
 		missarg("ns");
-	if (!size)
+	if (!trace_size)
 		missarg("size");
 
-	if (rta_addattr_l(rta, len, IOAM6_IPTUNNEL_TRACE, trace,
-			  sizeof(*trace))) {
+	trace = calloc(1, sizeof(*trace));
+	if (!trace)
+		return -1;
+
+	trace->type_be32 = htonl(trace_type << 8);
+	trace->namespace_id = htons(trace_ns);
+	trace->remlen = (__u8)(trace_size / 4);
+
+	if (rta_addattr8(rta, len, IOAM6_IPTUNNEL_MODE, mode) ||
+	    (mode != IOAM6_IPTUNNEL_MODE_INLINE &&
+	     rta_addattr_l(rta, len, IOAM6_IPTUNNEL_DST, &addr.data, addr.bytelen)) ||
+	    rta_addattr_l(rta, len, IOAM6_IPTUNNEL_TRACE, trace, sizeof(*trace))) {
 		free(trace);
 		return -1;
 	}