diff --git a/gtests/net/packetdrill/Makefile.common b/gtests/net/packetdrill/Makefile.common
index 0ec741f5548a41934f055a55e0b80e5430447963..9ff5027c931ae354cfd5fcdbbbf56634bfa4b312 100644
--- a/gtests/net/packetdrill/Makefile.common
+++ b/gtests/net/packetdrill/Makefile.common
@@ -19,7 +19,8 @@ packetdrill-lib := \
          symbols_freebsd.o \
          symbols_openbsd.o \
          symbols_netbsd.o \
-         gre_packet.o icmp_packet.o ip_packet.o tcp_packet.o udp_packet.o \
+         gre_packet.o icmp_packet.o ip_packet.o \
+         tcp_packet.o udp_packet.o udplite_packet.o \
          mpls_packet.o \
          run.o run_command.o run_packet.o run_system_call.o \
          script.o socket.o system.o \
diff --git a/gtests/net/packetdrill/checksum.c b/gtests/net/packetdrill/checksum.c
index b37dc00b95c1f6b754ff4cb4847055388da0b8b1..06ced251bc6ac59589a7edfe46c0830b5a1afaac 100644
--- a/gtests/net/packetdrill/checksum.c
+++ b/gtests/net/packetdrill/checksum.c
@@ -99,6 +99,14 @@ __be16 tcp_udp_v4_checksum(struct in_addr src_ip, struct in_addr dst_ip,
 	sum = ip_checksum_partial(payload, len, sum);
 	return ip_checksum_fold(sum);
 }
+__be16 udplite_v4_checksum(struct in_addr src_ip, struct in_addr dst_ip,
+			   u8 protocol, const void *payload, u16 len, u16 cov)
+{
+	u64 sum = tcp_udp_v4_header_checksum_partial(
+		src_ip, dst_ip, protocol, len);
+	sum = ip_checksum_partial(payload, cov, sum);
+	return ip_checksum_fold(sum);
+}
 
 /* Calculates and returns IPv4 header checksum. */
 __be16 ipv4_checksum(void *ip_header, size_t ip_header_bytes)
@@ -148,6 +156,16 @@ __be16 tcp_udp_v6_checksum(const struct in6_addr *src_ip,
 	return ip_checksum_fold(sum);
 }
 
+__be16 udplite_v6_checksum(const struct in6_addr *src_ip,
+			   const struct in6_addr *dst_ip,
+			   u8 protocol, const void *payload, u32 len, u16 cov)
+{
+	u64 sum = tcp_udp_v6_header_checksum_partial(
+		src_ip, dst_ip, protocol, len);
+	sum = ip_checksum_partial(payload, cov, sum);
+	return ip_checksum_fold(sum);
+}
+
 #define CRC32C(c, d) (c = (c>>8) ^ crc_c[(c^(d))&0xFF])
 
 static u32 crc_c[256] = {
diff --git a/gtests/net/packetdrill/checksum.h b/gtests/net/packetdrill/checksum.h
index 43681d2cdfbe83395db852d4a4fcf758b4e37e3b..d4154cd8d7b85f5973a5ae20811171f40cac6baf 100644
--- a/gtests/net/packetdrill/checksum.h
+++ b/gtests/net/packetdrill/checksum.h
@@ -39,6 +39,11 @@ extern __be16 ipv4_checksum(void *ip_header, size_t ip_header_bytes);
 extern __be16 tcp_udp_v4_checksum(struct in_addr src_ip, struct in_addr dst_ip,
 				  u8 protocol, const void *payload, u16 len);
 
+/* Calculates UDPLite checksum for IPv4 (in network byte order). */
+extern __be16 udplite_v4_checksum(struct in_addr src_ip, struct in_addr dst_ip,
+				  u8 protocol, const void *payload,
+				  u16 len, u16 cov);
+
 /* IPv6 ... */
 
 /* Calculates TCP, UDP, or ICMP checksum for IPv6 (in network byte order). */
@@ -46,6 +51,12 @@ extern __be16 tcp_udp_v6_checksum(const struct in6_addr *src_ip,
 				  const struct in6_addr *dst_ip,
 				  u8 protocol, const void *payload, u32 len);
 
+/* Calculates UDPLite checksum for IPv6 (in network byte order). */
+extern __be16 udplite_v6_checksum(const struct in6_addr *src_ip,
+				  const struct in6_addr *dst_ip,
+				  u8 protocol, const void *payload,
+				  u32 len, u16 cov);
+
 /* SCTP ... */
 
 /* Calculates the CRC32C checksum used by SCTP (in network byte order). */
diff --git a/gtests/net/packetdrill/checksum_test.c b/gtests/net/packetdrill/checksum_test.c
index 55ac51e29d5398e491a358e57ef7ab627f16406a..72133a55917cfa40c588a57691add59e99fbe9a7 100644
--- a/gtests/net/packetdrill/checksum_test.c
+++ b/gtests/net/packetdrill/checksum_test.c
@@ -30,6 +30,7 @@
 #include "ipv6.h"
 #include "sctp.h"
 #include "tcp.h"
+#include "udplite.h"
 
 static void test_tcp_udp_v4_checksum(void)
 {
@@ -130,11 +131,70 @@ static void test_sctp_crc32c(void)
 	assert(crc32c == 0xdad73774);
 }
 
+static void test_udplite_v4_checksum(void)
+{
+	u8 data[] = {
+		0x45, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00,
+		0xff, 0x88, 0xf8, 0xab, 0x01, 0x01, 0x01, 0x01,
+		0xc0, 0xa8, 0x00, 0x01, 0x04, 0xd2, 0xeb, 0x35,
+		0x00, 0x09, 0x86, 0xaf, 0xc6, 0x45, 0x46
+	};
+	struct ipv4 *ip = (struct ipv4 *)data;
+	struct udplite *udplite;
+	int len = sizeof(data) - sizeof(struct ipv4);
+	u16 cov = 9;
+	u16 checksum;
+
+	udplite = (struct udplite *)(data + sizeof(struct ipv4));
+	checksum =
+	    ntohs(udplite_v4_checksum(ip->src_ip, ip->dst_ip,
+				      IPPROTO_UDPLITE, udplite, len, cov));
+	assert(checksum == 0);
+
+	udplite->check = 0;
+	checksum =
+	    ntohs(udplite_v4_checksum(ip->src_ip, ip->dst_ip,
+				      IPPROTO_UDPLITE, udplite, len, cov));
+	assert(checksum == 0x86af);
+}
+
+static void test_udplite_v6_checksum(void)
+{
+	u8 data[] = {
+		0x60, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x88, 0xff,
+		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+		0xfd, 0x3d, 0xfa, 0x7b, 0xd1, 0x7d, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+		0xd3, 0xe2, 0x1f, 0x90, 0x00, 0x09, 0x4e, 0xfd,
+		0xc6, 0x45, 0x46
+	};
+	struct ipv6 *ipv6 = (struct ipv6 *)data;
+	struct udplite *udplite;
+	int len = sizeof(data) - sizeof(struct ipv6);
+	u16 cov = 9;
+	u16 checksum;
+
+	udplite = (struct udplite *)(data + sizeof(struct ipv6));
+	checksum =
+	    ntohs(udplite_v6_checksum(&ipv6->src_ip, &ipv6->dst_ip,
+				      IPPROTO_UDPLITE, udplite, len, cov));
+	assert(checksum == 0);
+
+	udplite->check = 0;
+	checksum =
+	    ntohs(udplite_v6_checksum(&ipv6->src_ip, &ipv6->dst_ip,
+				      IPPROTO_UDPLITE, udplite, len, cov));
+	assert(checksum == 0x4efd);
+}
+
 int main(void)
 {
 	test_tcp_udp_v4_checksum();
 	test_tcp_udp_v6_checksum();
 	test_ipv4_checksum();
 	test_sctp_crc32c();
+	test_udplite_v4_checksum();
+	test_udplite_v6_checksum();
 	return 0;
 }
diff --git a/gtests/net/packetdrill/contrib/packetdrill.vim b/gtests/net/packetdrill/contrib/packetdrill.vim
index 4e1e8b9bfd2c6ef0ca2ae489456f408de99da7a9..53d8b02b0543329760423cd7109ae29a6581ee79 100644
--- a/gtests/net/packetdrill/contrib/packetdrill.vim
+++ b/gtests/net/packetdrill/contrib/packetdrill.vim
@@ -1,7 +1,7 @@
 " Vim syntax file
 " Language:	Packetdrill
 " Maintainer:	Barath Raghavan <barath@google.com>
-" Last Change:	2013 Jul 27
+" Last Change:	2014 Sep 22
 
 " Quit when a (custom) syntax file was already loaded
 if exists("b:current_syntax")
diff --git a/gtests/net/packetdrill/header.h b/gtests/net/packetdrill/header.h
index 4c4216ce81cb4b652e9eda8009bfafb57bf246e9..389b126dd3c2c31266899f5a3b258d819d62b2ca 100644
--- a/gtests/net/packetdrill/header.h
+++ b/gtests/net/packetdrill/header.h
@@ -41,6 +41,7 @@
 #include "mpls.h"
 #include "tcp.h"
 #include "udp.h"
+#include "udplite.h"
 
 struct packet;
 
@@ -53,6 +54,7 @@ enum header_t {
 	HEADER_MPLS,
 	HEADER_TCP,
 	HEADER_UDP,
+	HEADER_UDPLITE,
 	HEADER_ICMPV4,
 	HEADER_ICMPV6,
 	HEADER_NUM_TYPES
@@ -71,6 +73,7 @@ struct header {
 		struct mpls *mpls;
 		struct tcp *tcp;
 		struct udp *udp;
+		struct udplite *udplite;
 		struct icmpv4 *icmpv4;
 		struct icmpv6 *icmpv6;
 	} h;
diff --git a/gtests/net/packetdrill/icmp_packet.c b/gtests/net/packetdrill/icmp_packet.c
index 0aec9f00164d6dd760ea92aa39e6a24fd60e348c..3ded8fe59cc32e5111f221fc7e5a62603ff6fd26 100644
--- a/gtests/net/packetdrill/icmp_packet.c
+++ b/gtests/net/packetdrill/icmp_packet.c
@@ -280,8 +280,9 @@ struct packet *new_icmp_packet(int address_family,
 				const char *type_string,
 				const char *code_string,
 				int protocol,
+				u16 payload_bytes,
 				u32 tcp_start_sequence,
-				u32 payload_bytes,
+				u16 udplite_checksum_coverage,
 				s64 mtu,
 				char **error)
 {
@@ -355,6 +356,17 @@ struct packet *new_icmp_packet(int address_family,
 		u32 *seq = packet_echoed_tcp_seq(packet);
 		*seq = htonl(tcp_start_sequence);
 	}
+	if (protocol == IPPROTO_UDP) {
+		u16 *cov = packet_echoed_udp_len(packet);
+		*cov = htons(payload_bytes + sizeof(struct udp));
+	}
+	if (protocol == IPPROTO_UDPLITE) {
+		u16 *cov = packet_echoed_udplite_cov(packet);
+		u16 *checksum = packet_echoed_udplite_checksum(packet);
+		*cov = htons(udplite_checksum_coverage);
+		/* Zero checksum is not allowed, so choose some valid value */
+		*checksum = htons(0xffff);
+	}
 
 	packet->ip_bytes = ip_bytes;
 	return packet;
diff --git a/gtests/net/packetdrill/icmp_packet.h b/gtests/net/packetdrill/icmp_packet.h
index ecaa8990a56f7ba892b5b71994d411d6206c4254..ba3db691bb23668257c3c9493131aa3faeec0f38 100644
--- a/gtests/net/packetdrill/icmp_packet.h
+++ b/gtests/net/packetdrill/icmp_packet.h
@@ -45,8 +45,9 @@ extern struct packet *new_icmp_packet(int address_family,
 				      const char *type_string,
 				      const char *code_string,
 				      int protocol,
+				      u16 payload_bytes,
 				      u32 tcp_start_sequence,
-				      u32 payload_bytes,
+				      u16 udplite_checksum_coverage,
 				      s64 mtu,
 				      char **error);
 
diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l
index 09cefdcdf414e51d616393f9830b9154896e3a15..cc57de998593a6e2015746863a668ea865b01cc5 100644
--- a/gtests/net/packetdrill/lexer.l
+++ b/gtests/net/packetdrill/lexer.l
@@ -175,6 +175,7 @@ ipv4			return IPV4;
 ipv6			return IPV6;
 icmp			return ICMP;
 udp			return UDP;
+udplite			return UDPLITE;
 gre			return GRE;
 mpls			return MPLS;
 label			return LABEL;
diff --git a/gtests/net/packetdrill/netdev.c b/gtests/net/packetdrill/netdev.c
index c6e36fcb1f5a82f7dccc2108d77114b1979e0301..4c1f1ce8038e88d87892774f3988f2e74ee3bc4d 100644
--- a/gtests/net/packetdrill/netdev.c
+++ b/gtests/net/packetdrill/netdev.c
@@ -347,8 +347,9 @@ static int local_netdev_send(struct netdev *a_netdev,
 	assert(packet->ip_bytes > 0);
 	/* We do IPv4 and IPv6 */
 	assert(packet->ipv4 || packet->ipv6);
-	/* We only do TCP and ICMP */
-	assert(packet->tcp || packet->udp || packet->icmpv4 || packet->icmpv6);
+	/* We only do TCP, UDP, UDPLite and ICMP */
+	assert(packet->tcp || packet->udp || packet->udplite ||
+	       packet->icmpv4 || packet->icmpv6);
 
 	DEBUGP("local_netdev_send\n");
 
diff --git a/gtests/net/packetdrill/packet.c b/gtests/net/packetdrill/packet.c
index c52bb14392420bb92cce00bf91595676ab23fe6d..bb62f3b69535cf41edf4db9f9d905aef2aaa9787 100644
--- a/gtests/net/packetdrill/packet.c
+++ b/gtests/net/packetdrill/packet.c
@@ -36,15 +36,16 @@
 
 /* Info for all types of header we support. */
 struct header_type_info header_types[HEADER_NUM_TYPES] = {
-	{ "NONE",   0,			0,		NULL },
-	{ "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 },
-	{ "ICMPV6", IPPROTO_ICMPV6,	0,		NULL },
+	{ "NONE",    0,			0,		NULL },
+	{ "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 },
+	{ "UDPLITE", IPPROTO_UDPLITE,	0,		NULL },
+	{ "ICMPV4",  IPPROTO_ICMP,	0,		NULL },
+	{ "ICMPV6",  IPPROTO_ICMPV6,	0,		NULL },
 };
 
 struct packet *packet_new(u32 buffer_bytes)
@@ -159,6 +160,7 @@ static struct packet *packet_copy_with_headroom(struct packet *old_packet,
 	packet->ipv6	= offset_ptr(old_base, new_base, old_packet->ipv6);
 	packet->tcp	= offset_ptr(old_base, new_base, old_packet->tcp);
 	packet->udp	= offset_ptr(old_base, new_base, old_packet->udp);
+	packet->udplite	= offset_ptr(old_base, new_base, old_packet->udplite);
 	packet->icmpv4	= offset_ptr(old_base, new_base, old_packet->icmpv4);
 	packet->icmpv6	= offset_ptr(old_base, new_base, old_packet->icmpv6);
 
diff --git a/gtests/net/packetdrill/packet.h b/gtests/net/packetdrill/packet.h
index fd4a50ceb9ec1c166e72fb8bf19baaf59ab4f6ad..57f8f73d01754c5ed475dbc3f121c295435eb729 100644
--- a/gtests/net/packetdrill/packet.h
+++ b/gtests/net/packetdrill/packet.h
@@ -38,6 +38,7 @@
 #include "ipv6.h"
 #include "tcp.h"
 #include "udp.h"
+#include "udplite.h"
 #include "unaligned.h"
 
 /* The data offset field is 4 bits, and specifies the length of the TCP header,
@@ -47,6 +48,7 @@
 
 #define MAX_TCP_DATAGRAM_BYTES (64*1024)	/* for sanity-checking */
 #define MAX_UDP_DATAGRAM_BYTES (64*1024)	/* for sanity-checking */
+#define MAX_UDPLITE_DATAGRAM_BYTES (64*1024)	/* for sanity-checking */
 
 /* We allow reading pretty big packets, since some interface MTUs can
  * be pretty big (the Linux loopback MTU, for example, is typically
@@ -92,6 +94,7 @@ struct packet {
 	/* Layer 4 */
 	struct tcp *tcp;	/* start of TCP header, if present */
 	struct udp *udp;	/* start of UDP header, if present */
+	struct udplite *udplite;/* start of UDPLite header, if present */
 	struct icmpv4 *icmpv4;	/* start of ICMPv4 header, if present */
 	struct icmpv6 *icmpv6;	/* start of ICMPv6 header, if present */
 
@@ -221,6 +224,8 @@ static inline int layer4_header_len(int protocol)
 		return sizeof(struct tcp);
 	if (protocol == IPPROTO_UDP)
 		return sizeof(struct udp);
+	if (protocol == IPPROTO_UDPLITE)
+		return sizeof(struct udplite);
 	assert(!"bad protocol");
 	return 0;
 }
@@ -239,6 +244,13 @@ static inline int packet_udp_header_len(const struct packet *packet)
 	return sizeof(struct udp);
 }
 
+/* Return the length of the UDPLite header. */
+static inline int packet_udplite_header_len(const struct packet *packet)
+{
+	assert(packet->udplite);
+	return sizeof(struct udplite);
+}
+
 /* Return the length of the TCP options. */
 static inline int packet_tcp_options_len(const struct packet *packet)
 {
@@ -280,7 +292,10 @@ static inline u8 *packet_payload(struct packet *packet)
 		return ((u8 *) packet->tcp) + packet_tcp_header_len(packet);
 	if (packet->udp)
 		return ((u8 *) packet->udp) + packet_udp_header_len(packet);
-	assert(!"no valid payload; not TCP or UDP!?");
+	if (packet->udplite)
+		return ((u8 *) packet->udplite) +
+		       packet_udplite_header_len(packet);
+	assert(!"no valid payload; not TCP or UDP or UDPLite!?");
 	return NULL;
 }
 
@@ -369,6 +384,15 @@ static inline struct udp *packet_echoed_udp_header(struct packet *packet)
 	return NULL;
 }
 
+/* Return the location of the UDPLITE header echoed by an ICMP message. */
+static inline struct
+udplite *packet_echoed_udplite_header(struct packet *packet)
+{
+	if (packet_echoed_ip_protocol(packet) == IPPROTO_UDPLITE)
+		return (struct udplite *)(packet_echoed_layer4_header(packet));
+	return NULL;
+}
+
 /* Return the location of the TCP sequence number echoed by an ICMP message. */
 static inline u32 *packet_echoed_tcp_seq(struct packet *packet)
 {
@@ -382,4 +406,48 @@ static inline u32 *packet_echoed_tcp_seq(struct packet *packet)
 	return seq;
 }
 
+/* Return the location of the UDP length echoed by an ICMP message. */
+static inline u16 *packet_echoed_udp_len(struct packet *packet)
+{
+	struct udp *echoed_udp = packet_echoed_udp_header(packet);
+
+	assert(echoed_udp);
+	u16 *len = &(echoed_udp->len);
+	/* Check that the len field is actually in the space we
+	 * reserved for the echoed prefix of the UDP header.
+	 */
+	assert((char *) (len + 1) <= (char *) echoed_udp + ICMP_ECHO_BYTES);
+	return len;
+}
+
+/* Return the location of the UDPLite checksum coverage echoed by an ICMP
+   message. */
+static inline u16 *packet_echoed_udplite_cov(struct packet *packet)
+{
+	struct udplite *echoed_udplite = packet_echoed_udplite_header(packet);
+
+	assert(echoed_udplite);
+	u16 *cov = &(echoed_udplite->cov);
+	/* Check that the len field is actually in the space we
+	 * reserved for the echoed prefix of the UDPlite header.
+	 */
+	assert((char *) (cov + 1) <= (char *) echoed_udplite + ICMP_ECHO_BYTES);
+	return cov;
+}
+
+/* Return the location of the UDPLite checksum echoed by an ICMP message. */
+static inline u16 *packet_echoed_udplite_checksum(struct packet *packet)
+{
+	struct udplite *echoed_udplite = packet_echoed_udplite_header(packet);
+
+	assert(echoed_udplite);
+	u16 *checksum = &(echoed_udplite->check);
+	/* Check that the len field is actually in the space we
+	 * reserved for the echoed prefix of the UDPlite header.
+	 */
+	assert((char *) (checksum + 1) <=
+	       (char *) echoed_udplite + ICMP_ECHO_BYTES);
+	return checksum;
+}
+
 #endif /* __PACKET_H__ */
diff --git a/gtests/net/packetdrill/packet_checksum.c b/gtests/net/packetdrill/packet_checksum.c
index d5164b3433fe808a2cd2b5f77d312dfdf4c46c58..f6498f6aa285edfdfe4462e6bfd56d8c6c838da3 100644
--- a/gtests/net/packetdrill/packet_checksum.c
+++ b/gtests/net/packetdrill/packet_checksum.c
@@ -57,12 +57,25 @@ static void checksum_ipv4_packet(struct packet *packet)
 		udp->check = tcp_udp_v4_checksum(ipv4->src_ip,
 						 ipv4->dst_ip,
 						 IPPROTO_UDP, udp, l4_bytes);
+	} else if (packet->udplite != NULL) {
+		struct udplite *udplite = packet->udplite;
+		u16 coverage;
+
+		coverage = ntohs(udplite->cov);
+		if ((coverage == 0) || (coverage > l4_bytes))
+			coverage = l4_bytes;
+		udplite->check = 0;
+		udplite->check = udplite_v4_checksum(ipv4->src_ip,
+						     ipv4->dst_ip,
+						     IPPROTO_UDPLITE,
+						     udplite,
+						     l4_bytes, coverage);
 	} else if (packet->icmpv4 != NULL) {
 		struct icmpv4 *icmpv4 = packet->icmpv4;
 		icmpv4->checksum = 0;
 		icmpv4->checksum = ipv4_checksum(icmpv4, l4_bytes);
 	} else {
-		assert(!"not TCP or ICMP");
+		assert(!"not TCP or UDP or UDPLite or ICMP");
 	}
 }
 
@@ -91,6 +104,19 @@ static void checksum_ipv6_packet(struct packet *packet)
 		udp->check = tcp_udp_v6_checksum(&ipv6->src_ip,
 						 &ipv6->dst_ip,
 						 IPPROTO_UDP, udp, l4_bytes);
+	} else if (packet->udplite != NULL) {
+		struct udplite *udplite = packet->udplite;
+		u16 coverage;
+
+		coverage = ntohs(udplite->cov);
+		if ((coverage == 0) || (coverage > l4_bytes))
+			coverage = l4_bytes;
+		udplite->check = 0;
+		udplite->check = udplite_v6_checksum(&ipv6->src_ip,
+						     &ipv6->dst_ip,
+						     IPPROTO_UDPLITE,
+						     udplite,
+						     l4_bytes, coverage);
 	} else if (packet->icmpv6 != NULL) {
 		/* IPv6 ICMP has a pseudo-header checksum, like TCP. */
 		struct icmpv6 *icmpv6 = packet->icmpv6;
@@ -100,7 +126,7 @@ static void checksum_ipv6_packet(struct packet *packet)
 					    &ipv6->dst_ip,
 					    IPPROTO_ICMPV6, icmpv6, l4_bytes);
 	} else {
-		assert(!"not TCP or ICMP");
+		assert(!"not TCP or UDP or UDPLite or ICMP");
 	}
 }
 
diff --git a/gtests/net/packetdrill/packet_parser.c b/gtests/net/packetdrill/packet_parser.c
index b1acd3feddc66ecf9766bbf50c7c42762fb1f37b..1bd6d0d30365b91600a372139d2147fb0a1a3e37 100644
--- a/gtests/net/packetdrill/packet_parser.c
+++ b/gtests/net/packetdrill/packet_parser.c
@@ -413,6 +413,46 @@ error_out:
 	return PACKET_BAD;
 }
 
+/* Parse the UDPLite header. Return a packet_parse_result_t. */
+static int parse_udplite(struct packet *packet, u8 *layer4_start,
+			 int layer4_bytes, u8 *packet_end, char **error)
+{
+	struct header *udplite_header = NULL;
+	u8 *p = layer4_start;
+
+	assert(layer4_bytes >= 0);
+	if (layer4_bytes < sizeof(struct udplite)) {
+		asprintf(error, "Truncated UDPLite header");
+		goto error_out;
+	}
+	packet->udplite = (struct udplite *) p;
+	const int udplite_header_len = sizeof(struct udplite);
+
+	if (layer4_bytes < udplite_header_len) {
+		asprintf(error,
+			 "UDPLITE datagram length too small for UDPLite header");
+		goto error_out;
+	}
+
+	udplite_header = packet_append_header(packet, HEADER_UDPLITE,
+					      udplite_header_len);
+	if (udplite_header == NULL) {
+		asprintf(error, "Too many nested headers at UDPLite header");
+		goto error_out;
+	}
+	udplite_header->total_bytes = layer4_bytes;
+
+	p += layer4_bytes;
+	assert(p <= packet_end);
+
+	DEBUGP("UDPLite src port: %d\n", ntohs(packet->udplite->src_port));
+	DEBUGP("UDPLite dst port: %d\n", ntohs(packet->udplite->dst_port));
+	return PACKET_OK;
+
+error_out:
+	return PACKET_BAD;
+}
+
 /* Parse the ICMPv4 header. Return a packet_parse_result_t. */
 static int parse_icmpv4(struct packet *packet, u8 *layer4_start,
 			int layer4_bytes, u8 *packet_end, char **error)
@@ -594,6 +634,10 @@ static int parse_layer4(struct packet *packet, u8 *layer4_start,
 		*is_inner = true;	/* found inner-most layer 4 */
 		return parse_udp(packet, layer4_start, layer4_bytes, packet_end,
 				 error);
+	} else if (layer4_protocol == IPPROTO_UDPLITE) {
+		*is_inner = true;	/* found inner-most layer 4 */
+		return parse_udplite(packet, layer4_start, layer4_bytes,
+				     packet_end, error);
 	} else if (layer4_protocol == IPPROTO_ICMP) {
 		*is_inner = true;	/* found inner-most layer 4 */
 		return parse_icmpv4(packet, layer4_start, layer4_bytes,
diff --git a/gtests/net/packetdrill/packet_parser.h b/gtests/net/packetdrill/packet_parser.h
index 160be514094ecc2e3bfafe1ad0fb5272fb6df9e8..07805553da28179d17db7a99196b7f1cd3677b16 100644
--- a/gtests/net/packetdrill/packet_parser.h
+++ b/gtests/net/packetdrill/packet_parser.h
@@ -30,7 +30,7 @@
 enum packet_parse_result_t {
 	PACKET_OK,		/* no errors detected */
 	PACKET_BAD,		/* illegal header */
-	PACKET_UNKNOWN_L4,	/* not TCP or UDP */
+	PACKET_UNKNOWN_L4,	/* not TCP or UDP or UDPLite */
 };
 
 /* Given an input packet of length 'in_bytes' stored in the buffer
diff --git a/gtests/net/packetdrill/packet_parser_test.c b/gtests/net/packetdrill/packet_parser_test.c
index c2545e7d62726b76eae3a0148660503042ea2153..45e49e08b6e33157ddc3a76f35dcc3d1ada56aae 100644
--- a/gtests/net/packetdrill/packet_parser_test.c
+++ b/gtests/net/packetdrill/packet_parser_test.c
@@ -66,6 +66,7 @@ static void test_parse_tcp_ipv4_packet(void)
 	assert(packet->ipv6		== NULL);
 	assert(packet->tcp		== expected_tcp);
 	assert(packet->udp		== NULL);
+	assert(packet->udplite		== NULL);
 	assert(packet->icmpv4		== NULL);
 	assert(packet->icmpv6		== NULL);
 
@@ -113,6 +114,7 @@ static void test_parse_tcp_ipv6_packet(void)
 	assert(packet->ipv6		== expected_ipv6);
 	assert(packet->tcp		== expected_tcp);
 	assert(packet->udp		== NULL);
+	assert(packet->udplite		== NULL);
 	assert(packet->icmpv4		== NULL);
 	assert(packet->icmpv6		== NULL);
 
@@ -127,7 +129,7 @@ static void test_parse_udp_ipv4_packet(void)
 {
 	/* A UDP/IPv4 packet. */
 	u8 data[] = {
-		/* 192.0.2.1.8080 > 192.168.0.1.57845: UDP, length 4 */
+		/* 192.0.2.1:8080 > 192.168.0.1:57845: UDP, length 4 */
 		0x45, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
 		0xff, 0x11, 0x39, 0x22, 0xc0, 0x00, 0x02, 0x01,
 		0xc0, 0xa8, 0x00, 0x01, 0x1f, 0x90, 0xe1, 0xf5,
@@ -153,6 +155,7 @@ static void test_parse_udp_ipv4_packet(void)
 	assert(packet->ipv6		== NULL);
 	assert(packet->tcp		== NULL);
 	assert(packet->udp		== expected_udp);
+	assert(packet->udplite		== NULL);
 	assert(packet->icmpv4		== NULL);
 	assert(packet->icmpv6		== NULL);
 
@@ -163,12 +166,11 @@ static void test_parse_udp_ipv4_packet(void)
 	packet_free(packet);
 }
 
-
 static void test_parse_udp_ipv6_packet(void)
 {
 	/* A UDP/IPv6 packet. */
 	u8 data[] = {
-		/* 2001:db8::1.8080 > fd3d:fa7b:d17d::1.51557: UDP, length 4 */
+		/* 2001:db8::1:8080 > fd3d:fa7b:d17d::1:51557: UDP, length 4 */
 		0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0xff,
 		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
@@ -197,6 +199,96 @@ static void test_parse_udp_ipv6_packet(void)
 	assert(packet->ipv6		== expected_ipv6);
 	assert(packet->tcp		== NULL);
 	assert(packet->udp		== expected_udp);
+	assert(packet->udplite		== NULL);
+	assert(packet->icmpv4		== NULL);
+	assert(packet->icmpv6		== NULL);
+
+	assert(packet->time_usecs	== 0);
+	assert(packet->flags		== 0);
+	assert(packet->ecn		== 0);
+
+	packet_free(packet);
+}
+
+static void test_parse_udplite_ipv4_packet(void)
+{
+	/* A UDPLite/IPv4 packet. */
+	u8 data[] = {
+		/* 1.1.1.1:1234 > 192.168.0.1:60213
+		 * UDPLite, length 3, coverage 9 */
+		0x45, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00,
+		0xff, 0x88, 0xf8, 0xab, 0x01, 0x01, 0x01, 0x01,
+		0xc0, 0xa8, 0x00, 0x01, 0x04, 0xd2, 0xeb, 0x35,
+		0x00, 0x09, 0x86, 0xaf, 0xc6, 0x45, 0x46
+	};
+
+	struct packet *packet = packet_new(sizeof(data));
+
+	/* Populate and parse a packet */
+	memcpy(packet->buffer, data, sizeof(data));
+	char *error = NULL;
+	enum packet_parse_result_t result =
+		parse_packet(packet, sizeof(data), PACKET_LAYER_3_IP,
+				     &error);
+	assert(result == PACKET_OK);
+	assert(error == NULL);
+
+	struct ipv4 *expected_ipv4 = (struct ipv4 *)(packet->buffer);
+	struct udplite *expected_udplite =
+		(struct udplite *)(expected_ipv4 + 1);
+
+	assert(packet->ip_bytes		== sizeof(data));
+	assert(packet->ipv4		== expected_ipv4);
+	assert(packet->ipv6		== NULL);
+	assert(packet->tcp		== NULL);
+	assert(packet->udp		== NULL);
+	assert(packet->udplite		== expected_udplite);
+	assert(packet->icmpv4		== NULL);
+	assert(packet->icmpv6		== NULL);
+
+	assert(packet->time_usecs	== 0);
+	assert(packet->flags		== 0);
+	assert(packet->ecn		== 0);
+
+	packet_free(packet);
+}
+
+static void test_parse_udplite_ipv6_packet(void)
+{
+	/* A UDPLite/IPv6 packet. */
+	u8 data[] = {
+		/* 2001:db8::1:54242 > fd3d:fa7b:d17d::1:8080
+		 * UDPLITE, length 3, coverage 9 */
+		0x60, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x88, 0xff,
+		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+		0xfd, 0x3d, 0xfa, 0x7b, 0xd1, 0x7d, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+		0xd3, 0xe2, 0x1f, 0x90, 0x00, 0x09, 0x4e, 0xfd,
+		0xc6, 0x45, 0x46
+	};
+
+	struct packet *packet = packet_new(sizeof(data));
+
+	/* Populate and parse a packet */
+	memcpy(packet->buffer, data, sizeof(data));
+	char *error = NULL;
+	enum packet_parse_result_t result =
+		parse_packet(packet, sizeof(data), PACKET_LAYER_3_IP,
+				     &error);
+	assert(result == PACKET_OK);
+	assert(error == NULL);
+
+	struct ipv6 *expected_ipv6 = (struct ipv6 *)(packet->buffer);
+	struct udplite *expected_udplite =
+		(struct udplite *)(expected_ipv6 + 1);
+
+	assert(packet->ip_bytes		== sizeof(data));
+	assert(packet->ipv4		== NULL);
+	assert(packet->ipv6		== expected_ipv6);
+	assert(packet->tcp		== NULL);
+	assert(packet->udp		== NULL);
+	assert(packet->udplite		== expected_udplite);
 	assert(packet->icmpv4		== NULL);
 	assert(packet->icmpv6		== NULL);
 
@@ -215,7 +307,7 @@ static void test_parse_ipv4_gre_ipv4_tcp_packet(void)
 	/* An IPv4/GRE/IPv4/TCP packet. */
 	u8 data[] = {
 		/* IP 2.2.2.2 > 1.1.1.1: GREv0, length 48:
-		   IP 192.0.2.1.47078 > 192.168.0.1.8080:
+		   IP 192.0.2.1:47078 > 192.168.0.1:8080:
 		   . 2:6(4) ack 1 win 123 */
 		0x45, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00,
 		0xff, 0x2f, 0xb5, 0x85, 0x02, 0x02, 0x02, 0x02,
@@ -275,6 +367,7 @@ static void test_parse_ipv4_gre_ipv4_tcp_packet(void)
 	assert(packet->ipv6		== NULL);
 	assert(packet->tcp		== expected_tcp);
 	assert(packet->udp		== NULL);
+	assert(packet->udplite		== NULL);
 	assert(packet->icmpv4		== NULL);
 	assert(packet->icmpv6		== NULL);
 
@@ -373,6 +466,7 @@ static void test_parse_ipv4_gre_mpls_ipv4_tcp_packet(void)
 	assert(packet->ipv6		== NULL);
 	assert(packet->tcp		== expected_tcp);
 	assert(packet->udp		== NULL);
+	assert(packet->udplite		== NULL);
 	assert(packet->icmpv4		== NULL);
 	assert(packet->icmpv6		== NULL);
 
@@ -415,6 +509,7 @@ static void test_parse_icmpv4_packet(void)
 	assert(packet->ipv6		== NULL);
 	assert(packet->tcp		== NULL);
 	assert(packet->udp		== NULL);
+	assert(packet->udplite		== NULL);
 	assert(packet->icmpv4		== expected_icmpv4);
 	assert(packet->icmpv6		== NULL);
 
@@ -468,6 +563,7 @@ static void test_parse_icmpv6_packet(void)
 	assert(packet->ipv6		== expected_ipv6);
 	assert(packet->tcp		== NULL);
 	assert(packet->udp		== NULL);
+	assert(packet->udplite		== NULL);
 	assert(packet->icmpv4		== NULL);
 	assert(packet->icmpv6		== expected_icmpv6);
 
@@ -484,6 +580,8 @@ int main(void)
 	test_parse_tcp_ipv6_packet();
 	test_parse_udp_ipv4_packet();
 	test_parse_udp_ipv6_packet();
+	test_parse_udplite_ipv4_packet();
+	test_parse_udplite_ipv6_packet();
 	test_parse_ipv4_gre_ipv4_tcp_packet();
 	test_parse_ipv4_gre_mpls_ipv4_tcp_packet();
 	test_parse_icmpv4_packet();
diff --git a/gtests/net/packetdrill/packet_to_string.c b/gtests/net/packetdrill/packet_to_string.c
index cfd503dcea22dab32406e13ea88fcde3efb80fcf..9fc5e5fb7cd69ded9ebe6d4664484291d8e5ad13 100644
--- a/gtests/net/packetdrill/packet_to_string.c
+++ b/gtests/net/packetdrill/packet_to_string.c
@@ -194,6 +194,25 @@ static int udp_packet_to_string(FILE *s, struct packet *packet,
 	return result;
 }
 
+static int udplite_packet_to_string(FILE *s, struct packet *packet,
+				    enum dump_format_t format, char **error)
+{
+	int result = STATUS_OK;       /* return value */
+
+	if ((format == DUMP_FULL) || (format == DUMP_VERBOSE)) {
+		endpoints_to_string(s, packet);
+		fputc(' ', s);
+	}
+
+	fprintf(s, "udplite (%u, %u)",
+		packet_payload_len(packet), ntohs(packet->udplite->cov));
+
+	if (format == DUMP_VERBOSE)
+		packet_buffer_to_string(s, packet);
+
+	return result;
+}
+
 static int icmpv4_packet_to_string(FILE *s, struct packet *packet,
 				   enum dump_format_t format, char **error)
 {
@@ -261,6 +280,9 @@ int packet_to_string(struct packet *packet,
 		} else if (packet->udp != NULL) {
 			if (udp_packet_to_string(s, packet, format, error))
 				goto out;
+		} else if (packet->udplite != NULL) {
+			if (udplite_packet_to_string(s, packet, format, error))
+				goto out;
 		} else if (packet->icmpv4 != NULL) {
 			if (icmpv4_packet_to_string(s, packet, format, error))
 				goto out;
@@ -268,7 +290,7 @@ int packet_to_string(struct packet *packet,
 			if (icmpv6_packet_to_string(s, packet, format, error))
 				goto out;
 		} else {
-			fprintf(s, "[NO TCP OR ICMP HEADER]");
+			fprintf(s, "[No TCP or UDP or UDPLite or ICMP header]");
 		}
 	}
 
diff --git a/gtests/net/packetdrill/packet_to_string_test.c b/gtests/net/packetdrill/packet_to_string_test.c
index 4d33971027e9e990bed42b70e472a1f7b6e5ec7c..31f7b27c43e17954a704abf36fbd55644e6f4115 100644
--- a/gtests/net/packetdrill/packet_to_string_test.c
+++ b/gtests/net/packetdrill/packet_to_string_test.c
@@ -114,7 +114,7 @@ static void test_tcp_ipv4_packet_to_string(void)
 
 static void test_tcp_ipv6_packet_to_string(void)
 {
-	/* An IPv6/GRE/TCP/IPv6 packet. */
+	/* An IPv6/GRE/IPv6/TCP packet. */
 	u8 data[] = {
 		/* IPv6: */
 		0x60, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x2f, 0xff,
@@ -246,10 +246,166 @@ static void test_gre_mpls_tcp_ipv4_packet_to_string(void)
 	free(dump);
 }
 
+static void test_udplite_ipv4_packet_to_string(void)
+{
+	/* An IPv4/GRE/IPv4/UDPLite packet. */
+	u8 data[] = {
+		/* IPv4: */
+		0x45, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00,
+		0xff, 0x2f, 0xb5, 0x92, 0x02, 0x02, 0x02, 0x02,
+		0x01, 0x01, 0x01, 0x01,
+		/* GRE: */
+		0x00, 0x00, 0x08, 0x00,
+		/* IPv4, UDPLite: */
+		0x45, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00,
+		0xff, 0x88, 0xf8, 0xab, 0x01, 0x01, 0x01, 0x01,
+		0xc0, 0xa8, 0x00, 0x01, 0x04, 0xd2, 0xeb, 0x35,
+		0x00, 0x09, 0x86, 0xaf, 0xc6, 0x45, 0x46
+	};
+
+	struct packet *packet = packet_new(sizeof(data));
+
+	/* Populate and parse a packet */
+	memcpy(packet->buffer, data, sizeof(data));
+	char *error = NULL;
+	enum packet_parse_result_t result =
+		parse_packet(packet, sizeof(data), PACKET_LAYER_3_IP,
+				     &error);
+	assert(result == PACKET_OK);
+	assert(error == NULL);
+
+	int status = 0;
+	char *dump = NULL, *expected = NULL;
+
+	/* Test a DUMP_SHORT dump */
+	status = packet_to_string(packet, DUMP_SHORT, &dump, &error);
+	assert(status == STATUS_OK);
+	assert(error == NULL);
+	printf("dump = '%s'\n", dump);
+	expected =
+		"ipv4 2.2.2.2 > 1.1.1.1: gre: "
+		"udplite (3, 9)";
+	assert(strcmp(dump, expected) == 0);
+	free(dump);
+
+	/* Test a DUMP_FULL dump */
+	status = packet_to_string(packet, DUMP_FULL, &dump, &error);
+	assert(status == STATUS_OK);
+	assert(error == NULL);
+	printf("dump = '%s'\n", dump);
+	expected =
+		"ipv4 2.2.2.2 > 1.1.1.1: gre: "
+		"1.1.1.1:1234 > 192.168.0.1:60213 "
+		"udplite (3, 9)";
+	assert(strcmp(dump, expected) == 0);
+	free(dump);
+
+	/* Test a DUMP_VERBOSE dump */
+	status = packet_to_string(packet, DUMP_VERBOSE, &dump, &error);
+	assert(status == STATUS_OK);
+	assert(error == NULL);
+	printf("dump = '%s'\n", dump);
+	expected =
+		"ipv4 2.2.2.2 > 1.1.1.1: gre: "
+		"1.1.1.1:1234 > 192.168.0.1:60213 "
+		"udplite (3, 9)"
+		"\n"
+		"0x0000: 45 00 00 37 00 00 00 00 ff 2f b5 92 02 02 02 02 " "\n"
+		"0x0010: 01 01 01 01 00 00 08 00 45 00 00 1f 00 00 00 00 " "\n"
+		"0x0020: ff 88 f8 ab 01 01 01 01 c0 a8 00 01 04 d2 eb 35 " "\n"
+		"0x0030: 00 09 86 af c6 45 46 " "\n";
+	assert(strcmp(dump, expected) == 0);
+	free(dump);
+
+	packet_free(packet);
+}
+
+static void test_udplite_ipv6_packet_to_string(void)
+{
+	/* An IPv6/GRE/IPv6/UDPLite packet. */
+	u8 data[] = {
+		/* IPv6: */
+		0x60, 0x00, 0x00, 0x00, 0x00, 0x37, 0x2f, 0xff,
+		0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22,
+		0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11,
+		/* GRE: */
+		0x00, 0x00, 0x86, 0xdd,
+		/* IPv6, UDPLite: */
+		0x60, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x88, 0xff,
+		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+		0xfd, 0x3d, 0xfa, 0x7b, 0xd1, 0x7d, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+		0xd3, 0xe2, 0x1f, 0x90, 0x00, 0x09, 0x4e, 0xfd,
+		0xc6, 0x45, 0x46
+	};
+
+	struct packet *packet = packet_new(sizeof(data));
+
+	/* Populate and parse a packet */
+	memcpy(packet->buffer, data, sizeof(data));
+	char *error = NULL;
+	enum packet_parse_result_t result =
+		parse_packet(packet, sizeof(data), PACKET_LAYER_3_IP,
+				     &error);
+	assert(result == PACKET_OK);
+	assert(error == NULL);
+
+	int status = 0;
+	char *dump = NULL, *expected = NULL;
+
+	/* Test a DUMP_SHORT dump */
+	status = packet_to_string(packet, DUMP_SHORT, &dump, &error);
+	assert(status == STATUS_OK);
+	assert(error == NULL);
+	printf("dump = '%s'\n", dump);
+	expected =
+		"ipv6 2::2222 > 1::1111: gre: "
+		"udplite (3, 9)";
+	assert(strcmp(dump, expected) == 0);
+	free(dump);
+
+	/* Test a DUMP_FULL dump */
+	status = packet_to_string(packet, DUMP_FULL, &dump, &error);
+	assert(status == STATUS_OK);
+	assert(error == NULL);
+	printf("dump = '%s'\n", dump);
+	expected =
+		"ipv6 2::2222 > 1::1111: gre: "
+		"2001:db8::1:54242 > fd3d:fa7b:d17d::1:8080 "
+		"udplite (3, 9)";
+	assert(strcmp(dump, expected) == 0);
+	free(dump);
+
+	/* Test a DUMP_VERBOSE dump */
+	status = packet_to_string(packet, DUMP_VERBOSE, &dump, &error);
+	assert(status == STATUS_OK);
+	assert(error == NULL);
+	printf("dump = '%s'\n", dump);
+	expected =
+		"ipv6 2::2222 > 1::1111: gre: "
+		"2001:db8::1:54242 > fd3d:fa7b:d17d::1:8080 "
+		"udplite (3, 9)\n"
+		"0x0000: 60 00 00 00 00 37 2f ff 00 02 00 00 00 00 00 00 " "\n"
+		"0x0010: 00 00 00 00 00 00 22 22 00 01 00 00 00 00 00 00 " "\n"
+		"0x0020: 00 00 00 00 00 00 11 11 00 00 86 dd 60 00 00 00 " "\n"
+		"0x0030: 00 0b 88 ff 20 01 0d b8 00 00 00 00 00 00 00 00 " "\n"
+		"0x0040: 00 00 00 01 fd 3d fa 7b d1 7d 00 00 00 00 00 00 " "\n"
+		"0x0050: 00 00 00 01 d3 e2 1f 90 00 09 4e fd c6 45 46 " "\n";
+	assert(strcmp(dump, expected) == 0);
+	free(dump);
+
+	packet_free(packet);
+}
+
 int main(void)
 {
 	test_tcp_ipv4_packet_to_string();
 	test_tcp_ipv6_packet_to_string();
 	test_gre_mpls_tcp_ipv4_packet_to_string();
+	test_udplite_ipv4_packet_to_string();
+	test_udplite_ipv6_packet_to_string();
 	return 0;
 }
diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y
index 6bee8a9d07f52c86a9f76eb035901b97b8e700e9..32f18e9c6f4be6c0f1b2836a6642e070564b1ffa 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -99,6 +99,7 @@
 #include "mpls_packet.h"
 #include "tcp_packet.h"
 #include "udp_packet.h"
+#include "udplite_packet.h"
 #include "parse.h"
 #include "script.h"
 #include "tcp.h"
@@ -449,10 +450,15 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 	s32 window;
 	u32 sequence_number;
 	struct {
-		int protocol;		/* IPPROTO_TCP or IPPROTO_UDP */
 		u32 start_sequence;
 		u16 payload_bytes;
 	} tcp_sequence_info;
+	struct {
+		int protocol;
+		u16 payload_bytes;
+		u32 start_sequence;	/* used for TCP */
+		u16 checksum_coverage;	/* used for UDPLite */
+	} transport_info;
 	struct option_list *option;
 	struct event *event;
 	struct packet *packet;
@@ -477,7 +483,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %token <reserved> ACK ECR EOL MSS NOP SACK SACKOK TIMESTAMP VAL WIN WSCALE PRO
 %token <reserved> FAST_OPEN
 %token <reserved> ECT0 ECT1 CE ECT01 NO_ECN
-%token <reserved> IPV4 IPV6 ICMP UDP GRE MTU
+%token <reserved> IPV4 IPV6 ICMP UDP UDPLITE GRE MTU
 %token <reserved> MPLS LABEL TC TTL
 %token <reserved> OPTION
 %token <reserved> SRTO_INITIAL SRTO_MAX SRTO_MIN
@@ -494,7 +500,8 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %type <option> option options opt_options
 %type <event> event events event_time action
 %type <time_usecs> time opt_end_time
-%type <packet> packet_spec tcp_packet_spec udp_packet_spec icmp_packet_spec
+%type <packet> packet_spec tcp_packet_spec udp_packet_spec udplite_packet_spec
+%type <packet> icmp_packet_spec
 %type <packet> packet_prefix
 %type <syscall> syscall_spec
 %type <command> command_spec
@@ -509,7 +516,8 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %type <string> option_flag option_value script
 %type <window> opt_window
 %type <sequence_number> opt_ack
-%type <tcp_sequence_info> seq opt_icmp_echoed
+%type <tcp_sequence_info> seq
+%type <transport_info> opt_icmp_echoed
 %type <tcp_options> opt_tcp_options tcp_option_list
 %type <tcp_option> tcp_option sack_block_list sack_block
 %type <string> function_name
@@ -676,9 +684,10 @@ action
 ;
 
 packet_spec
-: tcp_packet_spec  { $$ = $1; }
-| udp_packet_spec  { $$ = $1; }
-| icmp_packet_spec { $$ = $1; }
+: tcp_packet_spec     { $$ = $1; }
+| udp_packet_spec     { $$ = $1; }
+| udplite_packet_spec { $$ = $1; }
+| icmp_packet_spec    { $$ = $1; }
 ;
 
 tcp_packet_spec
@@ -730,6 +739,31 @@ udp_packet_spec
 }
 ;
 
+udplite_packet_spec
+: packet_prefix UDPLITE '(' INTEGER ',' INTEGER ')' {
+	char *error = NULL;
+	struct packet *outer = $1, *inner = NULL;
+	enum direction_t direction = outer->direction;
+
+	if (!is_valid_u16($4)) {
+		semantic_error("UDPLite payload size out of range");
+	}
+	if (!is_valid_u16($6)) {
+		semantic_error("UDPLite checksum coverage out of range");
+	}
+
+	inner = new_udplite_packet(in_config->wire_protocol, direction, $4, $6,
+				   &error);
+	if (inner == NULL) {
+		assert(error != NULL);
+		semantic_error(error);
+		free(error);
+	}
+
+	$$ = packet_encapsulate_and_free(outer, inner);
+}
+;
+
 icmp_packet_spec
 : packet_prefix opt_icmp_echoed ICMP icmp_type opt_icmp_code opt_icmp_mtu {
 	char *error = NULL;
@@ -737,8 +771,9 @@ icmp_packet_spec
 	enum direction_t direction = outer->direction;
 
 	inner = new_icmp_packet(in_config->wire_protocol, direction, $4, $5,
-				$2.protocol, $2.start_sequence,
-				$2.payload_bytes, $6, &error);
+				$2.protocol, $2.payload_bytes,
+				$2.start_sequence, $2.checksum_coverage,
+				$6, &error);
 	free($4);
 	free($5);
 	if (inner == NULL) {
@@ -847,17 +882,24 @@ opt_icmp_code
 /* This specifies the relevant details about the packet echoed by ICMP. */
 opt_icmp_echoed
 :			{
-	$$.start_sequence	= 0;
-	$$.payload_bytes	= 0;
 	$$.protocol		= IPPROTO_TCP;
+	$$.payload_bytes	= 0;
+	$$.start_sequence	= 0;
 }
 | '[' UDP '(' INTEGER ')' ']'	{
+	$$.protocol		= IPPROTO_UDP;
+	$$.payload_bytes	= $4;
 	$$.start_sequence	= 0;
+}
+| '[' UDPLITE '(' INTEGER ',' INTEGER ')' ']'	{
+	$$.protocol		= IPPROTO_UDPLITE;
 	$$.payload_bytes	= $4;
-	$$.protocol		= IPPROTO_UDP;
+	$$.checksum_coverage	= $6;
 }
 | '[' seq ']'		{
-	$$ = $2;
+	$$.protocol		= IPPROTO_TCP;
+	$$.payload_bytes	= $2.payload_bytes;
+	$$.start_sequence	= $2.start_sequence;
 }
 ;
 
@@ -908,7 +950,6 @@ seq
 	}
 	$$.start_sequence = $1;
 	$$.payload_bytes = $5;
-	$$.protocol = IPPROTO_TCP;
 }
 ;
 
diff --git a/gtests/net/packetdrill/platforms.h b/gtests/net/packetdrill/platforms.h
index 160869020876d97723f9110e66c742faa44c6e1c..6296fc9e79c1bc765773561d7f8321055d2c9bd4 100644
--- a/gtests/net/packetdrill/platforms.h
+++ b/gtests/net/packetdrill/platforms.h
@@ -46,17 +46,23 @@
 
 #include <netinet/sctp.h>
 #include <sys/param.h>
+#if __FreeBSD_version >= 1010000
+#include <netinet/udplite.h>
+#endif
 #define USE_LIBPCAP             1
 #define TUN_PATH                "/dev/tun0"
 #define TUN_DEV                 "tun0"
-
 #define HAVE_TCP_INFO           1
 #if (__FreeBSD_version < 1000000 && __FreeBSD_version > 902000) || __FreeBSD_version > 1000028
 #define HAVE_FMEMOPEN           1
+#else
+#include "fmemopen.h"
 #endif
-
+#if (__FreeBSD_version > 902000)
+#define HAVE_OPEN_MEMSTREAM	1
+#else
 #include "open_memstream.h"
-#include "fmemopen.h"
+#endif
 
 #endif  /* __FreeBSD__ */
 
diff --git a/gtests/net/packetdrill/run_packet.c b/gtests/net/packetdrill/run_packet.c
index 52d08d3fdc9807eb1b24637f0cde59eb862054ff..070a184bc831cb8e3084959d9085e83c9170cc4c 100644
--- a/gtests/net/packetdrill/run_packet.c
+++ b/gtests/net/packetdrill/run_packet.c
@@ -331,11 +331,15 @@ static struct socket *find_connect_for_live_packet(
 		(packet->udp &&
 		 (socket->protocol == IPPROTO_UDP) &&
 		 (socket->state == SOCKET_ACTIVE_CONNECTING));
+	bool is_udplite_match =
+		(packet->udplite &&
+		 (socket->protocol == IPPROTO_UDPLITE) &&
+		 (socket->state == SOCKET_ACTIVE_CONNECTING));
 	bool is_tcp_match =
 		(packet->tcp && packet->tcp->syn && !packet->tcp->ack &&
 		 (socket->protocol == IPPROTO_TCP) &&
 		 (socket->state == SOCKET_ACTIVE_SYN_SENT));
-	if (!is_udp_match && !is_tcp_match)
+	if (!is_udp_match && !is_tcp_match && !is_udplite_match)
 		return NULL;
 
 	if (!is_equal_ip(&tuple.dst.ip, &socket->live.remote.ip) ||
@@ -456,6 +460,13 @@ static int map_inbound_icmp_udp_packet(
 	return STATUS_OK;
 }
 
+/* UDPLite headers echoed by ICMP messages need no special rewriting. */
+static int map_inbound_icmp_udplite_packet(
+	struct socket *socket, struct packet *live_packet, char **error)
+{
+	return STATUS_OK;
+}
+
 static int map_inbound_icmp_packet(
 	struct socket *socket, struct packet *live_packet, char **error)
 {
@@ -463,6 +474,9 @@ static int map_inbound_icmp_packet(
 		return map_inbound_icmp_tcp_packet(socket, live_packet, error);
 	else if (packet_echoed_ip_protocol(live_packet) == IPPROTO_UDP)
 		return map_inbound_icmp_udp_packet(socket, live_packet, error);
+	else if (packet_echoed_ip_protocol(live_packet) == IPPROTO_UDPLITE)
+		return map_inbound_icmp_udplite_packet(socket, live_packet,
+						       error);
 	else
 		assert(!"unsupported layer 4 protocol echoed in ICMP packet");
 	return STATUS_ERR;
@@ -827,6 +841,24 @@ static int verify_udp(
 	return STATUS_OK;
 }
 
+/* Verify that required actual UDPLite header fields are as the script
+   expected. */
+static int verify_udplite(
+	const struct packet *actual_packet,
+	const struct packet *script_packet,
+	int layer, char **error)
+{
+	const struct udplite *actual_udplite =
+	    actual_packet->headers[layer].h.udplite;
+	const struct udplite *script_udplite =
+	    script_packet->headers[layer].h.udplite;
+	if (check_field("udplite_cov",
+			ntohs(script_udplite->cov),
+			ntohs(actual_udplite->cov), error))
+		return STATUS_ERR;
+	return STATUS_OK;
+}
+
 /* Verify that required actual GRE header fields are as the script expected. */
 static int verify_gre(
 	const struct packet *actual_packet,
@@ -886,12 +918,13 @@ static int verify_header(
 	int layer, char **error)
 {
 	verifier_func verifiers[HEADER_NUM_TYPES] = {
-		[HEADER_IPV4]	= verify_ipv4,
-		[HEADER_IPV6]	= verify_ipv6,
-		[HEADER_GRE]	= verify_gre,
-		[HEADER_MPLS]	= verify_mpls,
-		[HEADER_TCP]	= verify_tcp,
-		[HEADER_UDP]	= verify_udp,
+		[HEADER_IPV4]		= verify_ipv4,
+		[HEADER_IPV6]		= verify_ipv6,
+		[HEADER_GRE]		= verify_gre,
+		[HEADER_MPLS]		= verify_mpls,
+		[HEADER_TCP]		= verify_tcp,
+		[HEADER_UDP]		= verify_udp,
+		[HEADER_UDPLITE]	= verify_udplite,
 	};
 	verifier_func verifier = NULL;
 	const struct header *actual_header = &actual_packet->headers[layer];
@@ -924,7 +957,9 @@ static int verify_outbound_live_headers(
 	int i;
 
 	assert((actual_packet->ipv4 != NULL) || (actual_packet->ipv6 != NULL));
-	assert((actual_packet->tcp != NULL) || (actual_packet->udp != NULL));
+	assert((actual_packet->tcp != NULL) ||
+	       (actual_packet->udp != NULL) ||
+	       (actual_packet->udplite != NULL));
 
 	if (actual_headers != script_headers) {
 		asprintf(error, "live packet header layers: "
@@ -1154,6 +1189,8 @@ static bool is_script_packet_match_for_socket(
 		return packet->tcp || is_packet_icmp;
 	else if (socket->protocol == IPPROTO_UDP)
 		return packet->udp || is_packet_icmp;
+	else if (socket->protocol == IPPROTO_UDPLITE)
+		return packet->udplite || is_packet_icmp;
 	else
 		assert(!"unsupported layer 4 protocol in socket");
 	return false;
@@ -1260,8 +1297,9 @@ static int send_live_ip_packet(struct netdev *netdev,
 	assert(packet->ip_bytes > 0);
 	/* We do IPv4 and IPv6 */
 	assert(packet->ipv4 || packet->ipv6);
-	/* We only do TCP, UDP, and ICMP */
-	assert(packet->tcp || packet->udp || packet->icmpv4 || packet->icmpv6);
+	/* We only do TCP, UDP, UDPLite and ICMP */
+	assert(packet->tcp || packet->udp || packet->udplite ||
+	       packet->icmpv4 || packet->icmpv6);
 
 	/* Fill in layer 3 and layer 4 checksums */
 	checksum_packet(packet);
diff --git a/gtests/net/packetdrill/script.c b/gtests/net/packetdrill/script.c
index fbf150c9f7b59a11d5c69d68cd52ec6d80f2dd5b..9877c4eb263e91262014f072dc805f02c049bd4e 100644
--- a/gtests/net/packetdrill/script.c
+++ b/gtests/net/packetdrill/script.c
@@ -109,6 +109,7 @@ struct int_symbol cross_platform_symbols[] = {
 #endif
 	{ IPPROTO_TCP,                      "IPPROTO_TCP"                     },
 	{ IPPROTO_UDP,                      "IPPROTO_UDP"                     },
+	{ IPPROTO_UDPLITE,                  "IPPROTO_UDPLITE"                 },
 
 	{ SHUT_RD,                          "SHUT_RD"                         },
 	{ SHUT_WR,                          "SHUT_WR"                         },
diff --git a/gtests/net/packetdrill/socket.h b/gtests/net/packetdrill/socket.h
index 4fd8ea8ad832fa6e5243058aa6ac462edb78a7a8..0b14a7e0fe52c2776d2ecc6f17111d0e49954d00 100644
--- a/gtests/net/packetdrill/socket.h
+++ b/gtests/net/packetdrill/socket.h
@@ -172,6 +172,9 @@ static inline void get_packet_tuple(const struct packet *packet,
 	} else if (packet->udp != NULL) {
 		tuple->src.port	= packet->udp->src_port;
 		tuple->dst.port	= packet->udp->dst_port;
+	} else if (packet->udplite != NULL) {
+		tuple->src.port	= packet->udplite->src_port;
+		tuple->dst.port	= packet->udplite->dst_port;
 	}
 }
 
@@ -180,6 +183,7 @@ static inline void set_headers_tuple(struct ipv4 *ipv4,
 				     struct ipv6 *ipv6,
 				     struct tcp *tcp,
 				     struct udp *udp,
+				     struct udplite *udplite,
 				     const struct tuple *tuple)
 {
 	if (ipv4 != NULL) {
@@ -197,6 +201,9 @@ static inline void set_headers_tuple(struct ipv4 *ipv4,
 	} else if (udp != NULL) {
 		udp->src_port = tuple->src.port;
 		udp->dst_port = tuple->dst.port;
+	} else if (udplite != NULL) {
+		udplite->src_port = tuple->src.port;
+		udplite->dst_port = tuple->dst.port;
 	}
 }
 
@@ -221,6 +228,7 @@ static inline void set_icmp_echoed_tuple(struct packet *packet,
 			  packet_echoed_ipv6_header(packet),
 			  packet_echoed_tcp_header(packet),
 			  packet_echoed_udp_header(packet),
+			  packet_echoed_udplite_header(packet),
 			  &echoed_tuple);
 }
 
@@ -228,8 +236,8 @@ static inline void set_icmp_echoed_tuple(struct packet *packet,
 static inline void set_packet_tuple(struct packet *packet,
 				    const struct tuple *tuple)
 {
-	set_headers_tuple(packet->ipv4, packet->ipv6, packet->tcp, packet->udp,
-			  tuple);
+	set_headers_tuple(packet->ipv4, packet->ipv6, packet->tcp,
+			  packet->udp, packet->udplite, tuple);
 	if ((packet->icmpv4 != NULL) || (packet->icmpv6 != NULL))
 		set_icmp_echoed_tuple(packet, tuple);
 }
diff --git a/gtests/net/packetdrill/symbols_freebsd.c b/gtests/net/packetdrill/symbols_freebsd.c
index d78a73ec61bfc035309134f802cbd7413075db71..c760b465698006f6b51aab871885edba9a1b1452 100644
--- a/gtests/net/packetdrill/symbols_freebsd.c
+++ b/gtests/net/packetdrill/symbols_freebsd.c
@@ -95,6 +95,12 @@ struct int_symbol platform_symbols_table[] = {
 	{ TCP_INFO,                         "TCP_INFO"                        },
 	{ TCP_CONGESTION,                   "TCP_CONGESTION"                  },
 
+#if __FreeBSD_version >= 1100000
+	/* /usr/include/netinet/udplite.h */
+	{ UDPLITE_RECV_CSCOV,               "UDPLITE_RECV_CSCOV"              },
+	{ UDPLITE_SEND_CSCOV,               "UDPLITE_SEND_CSCOV"              },
+#endif
+
 	/* /usr/include/sys/fcntl.h */
 	{ O_RDONLY,                         "O_RDONLY"                        },
 	{ O_WRONLY,                         "O_WRONLY"                        },
diff --git a/gtests/net/packetdrill/symbols_linux.c b/gtests/net/packetdrill/symbols_linux.c
index 173474361d05d8623cecc1599e337c76ba8d85b9..518c5322e9ddb48b1009be0178651a9a35132ce9 100644
--- a/gtests/net/packetdrill/symbols_linux.c
+++ b/gtests/net/packetdrill/symbols_linux.c
@@ -53,6 +53,7 @@ struct int_symbol platform_symbols_table[] = {
 #endif
 	{ SOL_TCP,                          "SOL_TCP"                         },
 	{ SOL_UDP,                          "SOL_UDP"                         },
+	{ SOL_UDPLITE,                      "SOL_UDPLITE"                     },
 
 	{ SO_ACCEPTCONN,                    "SO_ACCEPTCONN"                   },
 	{ SO_ATTACH_FILTER,                 "SO_ATTACH_FILTER"                },
@@ -137,6 +138,9 @@ struct int_symbol platform_symbols_table[] = {
 	{ TCP_THIN_DUPACK,                  "TCP_THIN_DUPACK"                 },
 	{ TCP_USER_TIMEOUT,                 "TCP_USER_TIMEOUT"                },
 
+	{ UDPLITE_RECV_CSCOV,               "UDPLITE_RECV_CSCOV"              },
+	{ UDPLITE_SEND_CSCOV,               "UDPLITE_SEND_CSCOV"              },
+
 	{ O_RDONLY,                         "O_RDONLY"                        },
 	{ O_WRONLY,                         "O_WRONLY"                        },
 	{ O_RDWR,                           "O_RDWR"                          },
diff --git a/gtests/net/packetdrill/types.h b/gtests/net/packetdrill/types.h
index ffb24e001d4f384188acac1d36f71ea823be7f4c..c3b37cef43e6990e142a0ba09d53d1816227569a 100644
--- a/gtests/net/packetdrill/types.h
+++ b/gtests/net/packetdrill/types.h
@@ -53,6 +53,11 @@
 #define __aligned(x) __attribute__ ((aligned(x)))
 #endif
 
+/* Make sure we have the following constant on all platforms */
+#ifndef IPPROTO_UDPLITE
+#define IPPROTO_UDPLITE 136
+#endif
+
 /* We use kernel-style names for standard integer types. */
 typedef unsigned char u8;
 typedef unsigned short u16;