diff --git a/gtests/net/packetdrill/Makefile.common b/gtests/net/packetdrill/Makefile.common
index 89e3e19abd424bceda6e21b2a396237f7614d6f0..0ec741f5548a41934f055a55e0b80e5430447963 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 d73e6703325908274a235349edfdadaf4a51e8be..c52669d7d648df5783c286277788c230d0e175e7 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 310738a5d14bc551c8b998b170ba2e1e638ddc24..c52bb14392420bb92cce00bf91595676ab23fe6d 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 07aa5b378eb118062bf3399e965b4bf219097168..6203a0ba853f89a11493c2324a884c439a33315f 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