From 5c32114da45e7ecc848ef228d1dc1735171798f3 Mon Sep 17 00:00:00 2001
From: Neal Cardwell <ncardwell@google.com>
Date: Thu, 28 Nov 2013 13:14:40 -0500
Subject: [PATCH] net-test: packetdrill MPLS support: parse MPLS label stacks
 in scripts

Code to parse script packets using an encapsulation layer that is an
MPLS label stack.

The syntax looks like:

  mpls (label 123, tc 1, [S], ttl 255) (label 456, tc 1, [S], ttl 255)

This is borrowed from the syntax from tcpdump, which is:

  MPLS (label 123, exp 1, [S], ttl 255) (label 456, exp 1, [S], ttl 255)

We use "tc" because RFC 5462 renamed the EXP ("experimental") field to
the TC ("traffic class") field. We use lower-case "mpls" instead of
"MPLS" for consistency with other packetdrill protocols, and for ease
of typing.

Change-Id: I2e797aead8605da9fe3f0461594cf7273d4ef0ec
---
 gtests/net/packetdrill/Makefile.common |  1 +
 gtests/net/packetdrill/lexer.l         |  4 ++
 gtests/net/packetdrill/packet.c        |  2 +
 gtests/net/packetdrill/parser.y        | 56 ++++++++++++++++++++++++++
 4 files changed, 63 insertions(+)

diff --git a/gtests/net/packetdrill/Makefile.common b/gtests/net/packetdrill/Makefile.common
index 89e3e19a..0ec741f5 100644
--- a/gtests/net/packetdrill/Makefile.common
+++ b/gtests/net/packetdrill/Makefile.common
@@ -20,6 +20,7 @@ packetdrill-lib := \
          symbols_openbsd.o \
          symbols_netbsd.o \
          gre_packet.o icmp_packet.o ip_packet.o tcp_packet.o udp_packet.o \
+         mpls_packet.o \
          run.o run_command.o run_packet.o run_system_call.o \
          script.o socket.o system.o \
          tcp_options.o tcp_options_iterator.o tcp_options_to_string.o \
diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l
index d73e6703..c52669d7 100644
--- a/gtests/net/packetdrill/lexer.l
+++ b/gtests/net/packetdrill/lexer.l
@@ -176,6 +176,10 @@ ipv6			return IPV6;
 icmp			return ICMP;
 udp			return UDP;
 gre			return GRE;
+mpls			return MPLS;
+label			return LABEL;
+tc			return TC;
+ttl			return TTL;
 inet_addr		return INET_ADDR;
 ack			return ACK;
 eol			return EOL;
diff --git a/gtests/net/packetdrill/packet.c b/gtests/net/packetdrill/packet.c
index 310738a5..c52bb143 100644
--- a/gtests/net/packetdrill/packet.c
+++ b/gtests/net/packetdrill/packet.c
@@ -31,6 +31,7 @@
 #include "ethernet.h"
 #include "gre_packet.h"
 #include "ip_packet.h"
+#include "mpls_packet.h"
 
 
 /* Info for all types of header we support. */
@@ -39,6 +40,7 @@ struct header_type_info header_types[HEADER_NUM_TYPES] = {
 	{ "IPV4",   IPPROTO_IPIP,	ETHERTYPE_IP,	ipv4_header_finish },
 	{ "IPV6",   IPPROTO_IPV6,	ETHERTYPE_IPV6, ipv6_header_finish },
 	{ "GRE",    IPPROTO_GRE,	0,		gre_header_finish },
+	{ "MPLS",   0,			ETHERTYPE_MPLS_UC, mpls_header_finish },
 	{ "TCP",    IPPROTO_TCP,	0,		NULL },
 	{ "UDP",    IPPROTO_UDP,	0,		NULL },
 	{ "ICMPV4", IPPROTO_ICMP,	0,		NULL },
diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y
index 07aa5b37..6203a0ba 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -95,6 +95,8 @@
 #include "ip_packet.h"
 #include "icmp_packet.h"
 #include "logging.h"
+#include "mpls.h"
+#include "mpls_packet.h"
 #include "tcp_packet.h"
 #include "udp_packet.h"
 #include "parse.h"
@@ -441,6 +443,8 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 	s64 time_usecs;
 	enum direction_t direction;
 	enum ip_ecn_t ip_ecn;
+	struct mpls_stack *mpls_stack;
+	struct mpls mpls_stack_entry;
 	u16 port;
 	s32 window;
 	u32 sequence_number;
@@ -474,6 +478,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %token <reserved> FAST_OPEN
 %token <reserved> ECT0 ECT1 CE ECT01 NO_ECN
 %token <reserved> IPV4 IPV6 ICMP UDP GRE MTU
+%token <reserved> MPLS LABEL TC TTL
 %token <reserved> OPTION
 %token <floating> FLOAT
 %token <integer> INTEGER HEX_INTEGER
@@ -489,6 +494,9 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %type <syscall> syscall_spec
 %type <command> command_spec
 %type <code> code_spec
+%type <mpls_stack> mpls_stack
+%type <mpls_stack_entry> mpls_stack_entry
+%type <integer> opt_mpls_stack_bottom
 %type <integer> opt_icmp_mtu
 %type <string> icmp_type opt_icmp_code flags
 %type <string> opt_tcp_fast_open_cookie tcp_fast_open_cookie
@@ -771,6 +779,54 @@ packet_prefix
 		semantic_error(error);
 	$$ = packet;
 }
+| packet_prefix MPLS mpls_stack ':' {
+	char *error = NULL;
+	struct packet *packet = $1;
+	struct mpls_stack *mpls_stack = $3;
+
+	if (mpls_header_append(packet, mpls_stack, &error))
+		semantic_error(error);
+	free(mpls_stack);
+	$$ = packet;
+}
+;
+
+mpls_stack
+:				{
+	$$ = mpls_stack_new();
+}
+| mpls_stack mpls_stack_entry	{
+	if (mpls_stack_append($1, $2))
+		semantic_error("too many MPLS labels");
+	$$ = $1;
+}
+;
+
+mpls_stack_entry
+:
+'(' LABEL INTEGER ',' TC INTEGER ',' opt_mpls_stack_bottom TTL INTEGER ')' {
+	char *error = NULL;
+	s64 label = $3;
+	s64 traffic_class = $6;
+	bool is_stack_bottom = $8;
+	s64 ttl = $10;
+	struct mpls mpls;
+
+	if (new_mpls_stack_entry(label, traffic_class, is_stack_bottom, ttl,
+				 &mpls, &error))
+		semantic_error(error);
+	$$ = mpls;
+}
+;
+
+opt_mpls_stack_bottom
+:			{ $$ = 0; }
+| '[' WORD ']' ','	{
+	if (strcmp($2, "S") != 0)
+		semantic_error("expected [S] for MPLS label stack bottom");
+	free($2);
+	$$ = 1;
+}
 ;
 
 icmp_type
-- 
GitLab