CVE-2020-15173: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow') (CWE-120)
value: In ACCEL-PPP (an implementation of PPTP/PPPoE/L2TP/SSTP), there is a buffer overflow when receiving an l2tp control packet ith an AVP which type is a string and no hidden flags, length set to less than 6. If your application is used in open networks or there are untrusted nodes in the network it is highly recommended to apply the patch. The problem was patched with commit 2324bcd5ba12cf28f47357a8f03cd41b7c04c52b As a workaround changes of commit 2324bcd5ba12cf28f47357a8f03cd41b7c04c52b can be applied to older versions.
CWE description: The product copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer, leading to a buffer overflow.
CVSS v3 base score: 9.8
Published: 2020-09-09T23:15Z
Repository: https://github.com/accel-ppp/accel-ppp
Language: C
Files changed: accel-pppd/ctrl/l2tp/l2tp_prot.h, accel-pppd/ctrl/l2tp/packet.c
Commit date: 2020-09-06 02:38:35 +0500
Commit message: l2tp: fix RCE through buffer overflow & fix LE/BE compatibility

Unsufficent checks of valid l2tp header & avp length cause possible
RCE through buffer overflow, reported by https://github.com/WinMin
swings & leommxj, Chaitin Security Research Lab. Add missed header
length and avp length validation to fix the issue.

Order of struct bitfields is implementation-defined so current code
doesn't play well with big-endian arch. switch to explicit flag bit
checking/gathering to fix the issue.

RFC 2661 and 3931 requires that length, seqeuence flags must be set
and offset flag must not be set, so avp-premissive can't help in
this cases.

Diff: diff --git a/accel-pppd/ctrl/l2tp/l2tp_prot.h b/accel-pppd/ctrl/l2tp/l2tp_prot.h
index 559b29c..75cc87f 100644
--- a/accel-pppd/ctrl/l2tp/l2tp_prot.h
+++ b/accel-pppd/ctrl/l2tp/l2tp_prot.h
@@ -3,21 +3,20 @@
 
 #include <stdint.h>
 
 #define L2TP_PORT 1701
 
+#define L2TP_FLAG_T   0x8000
+#define L2TP_FLAG_L   0x4000
+#define L2TP_FLAG_S   0x0800
+#define L2TP_FLAG_O   0x0200
+#define L2TP_FLAG_P   0x0100
+#define L2TP_VER_MASK 0x000f
+
 struct l2tp_hdr_t
 {
-	uint8_t P:1;
-	uint8_t O:1;
-	uint8_t reserved2:1;
-	uint8_t S:1;
-	uint8_t reserved1:2;
-	uint8_t L:1;
-	uint8_t T:1;
-	uint8_t ver:4;
-	uint8_t reserved3:4;
+	uint16_t flags;
 	uint16_t length;
 	union {
 		struct {
 			uint16_t tid;
 			uint16_t sid;
@@ -26,22 +25,17 @@ struct l2tp_hdr_t
 	};
 	uint16_t Ns;
 	uint16_t Nr;
 } __attribute__((packed));
 
-/*#define L2TP_T(hdr) (hdr->flags >> 15)
-#define L2TP_L(hdr) ((hdr->flags >> 14) & 1)
-#define L2TP_S(hdr) ((hdr->flags >> 10) & 1)
-#define L2TP_O(hdr) ((hdr->flags >> 8) & 1)
-#define L2TP_VER(hdr) (hdr->flags & 0xf)*/
+#define L2TP_AVP_FLAG_M   0x8000
+#define L2TP_AVP_FLAG_H   0x4000
+#define L2TP_AVP_LEN_MASK 0x03ff
 
 struct l2tp_avp_t
 {
-	uint16_t length:10;
-	uint16_t reserved:4;
-	uint16_t H:1;
-	uint16_t M:1;
+	uint16_t flags;
 	uint16_t vendor;
 	uint16_t type;
 	uint8_t val[0];
 } __attribute__((packed));
 
@@ -51,6 +45,5 @@ struct l2tp_avp_result_code
 	uint16_t error_code;
 	char error_msg[0];
 } __attribute__((packed));
 
 #endif
-
diff --git a/accel-pppd/ctrl/l2tp/packet.c b/accel-pppd/ctrl/l2tp/packet.c
index 455c486..97e205f 100644
--- a/accel-pppd/ctrl/l2tp/packet.c
+++ b/accel-pppd/ctrl/l2tp/packet.c
@@ -26,16 +26,22 @@ void l2tp_packet_print(const struct l2tp_packet_t *pack,
 		       void (*print)(const char *fmt, ...))
 {
 	const struct l2tp_attr_t *attr;
 	const struct l2tp_dict_value_t *val;
 
-	if (pack->hdr.ver == 2) {
+	switch (pack->hdr.flags & L2TP_VER_MASK) {
+	case 2:
 		print("[L2TP tid=%u sid=%u", ntohs(pack->hdr.tid), ntohs(pack->hdr.sid));
 		log_ppp_debug(" Ns=%u Nr=%u", ntohs(pack->hdr.Ns), ntohs(pack->hdr.Nr));
-	} else {
+		break;
+	case 3:
 		print("[L2TP cid=%u", pack->hdr.cid);
 		log_ppp_debug(" Ns=%u Nr=%u", ntohs(pack->hdr.Ns), ntohs(pack->hdr.Nr));
+		break;
+	default:
+		print("[L2TP unknown version]\n");
+		return;
 	}
 
 	list_for_each_entry(attr, &pack->attrs, entry) {
 		print(" <%s", attr->attr->name);
 		val = l2tp_dict_find_value(attr->attr, attr->val);
@@ -70,14 +76,11 @@ struct l2tp_packet_t *l2tp_packet_alloc(int ver, int msg_type,
 	if (!pack)
 		return NULL;
 
 	memset(pack, 0, sizeof(*pack));
 	INIT_LIST_HEAD(&pack->attrs);
-	pack->hdr.ver = ver;
-	pack->hdr.T = 1;
-	pack->hdr.L = 1;
-	pack->hdr.S = 1;
+	pack->hdr.flags = L2TP_FLAG_T | L2TP_FLAG_L | L2TP_FLAG_S | (ver & L2TP_VER_MASK);
 	memcpy(&pack->addr, addr, sizeof(*addr));
 	pack->hide_avps = H;
 	pack->secret = secret;
 	pack->secret_len = secret_len;
 
@@ -148,25 +151,27 @@ static int decode_avp(struct l2tp_avp_t *avp, const struct l2tp_attr_t *RV,
 {
 	MD5_CTX md5_ctx;
 	uint8_t md5[MD5_DIGEST_LENGTH];
 	uint8_t p1[MD5_DIGEST_LENGTH];
 	uint8_t *prev_block = NULL;
+	uint16_t avp_len;
 	uint16_t attr_len;
 	uint16_t orig_attr_len;
 	uint16_t bytes_left;
 	uint16_t blocks_left;
 	uint16_t last_block_len;
 
-	if (avp->length < sizeof(struct l2tp_avp_t) + 2) {
+	avp_len = avp->flags & L2TP_AVP_LEN_MASK;
+	if (avp_len < sizeof(struct l2tp_avp_t) + 2) {
 		/* Hidden AVPs must contain at least two bytes
 		   for storing original attribute length */
 		log_warn("l2tp: incorrect hidden avp received (type %hu):"
 			 " length too small (%hu bytes)\n",
-			 ntohs(avp->type), avp->length);
+			 ntohs(avp->type), avp_len);
 		return -1;
 	}
-	attr_len = avp->length - sizeof(struct l2tp_avp_t);
+	attr_len = avp_len - sizeof(struct l2tp_avp_t);
 
 	/* Decode first block */
 	MD5_Init(&md5_ctx);
 	MD5_Update(&md5_ctx, &avp->type, sizeof(avp->type));
 	MD5_Update(&md5_ctx, secret, secret_len);
@@ -226,35 +231,33 @@ static int decode_avp(struct l2tp_avp_t *avp, const struct l2tp_attr_t *RV,
 }
 
 int l2tp_recv(int fd, struct l2tp_packet_t **p, struct in_pktinfo *pkt_info,
 	      const char *secret, size_t secret_len)
 {
-	int n, length;
-	uint8_t *buf;
+	struct l2tp_packet_t *pack;
 	struct l2tp_hdr_t *hdr;
 	struct l2tp_avp_t *avp;
-	struct l2tp_dict_attr_t *da;
-	struct l2tp_attr_t *attr, *RV = NULL;
-	uint8_t *ptr;
-	struct l2tp_packet_t *pack;
+	struct l2tp_attr_t *RV = NULL;
 	struct sockaddr_in addr;
-	socklen_t len = sizeof(addr);
-	struct msghdr msg;
-	char msg_control[128];
-	struct cmsghdr *cmsg;
+	socklen_t addr_len;
 	uint16_t orig_avp_len;
 	void *orig_avp_val;
+	uint8_t *buf, *ptr;
+	int n, length;
 
-  *p = NULL;
+	*p = NULL;
 
 	if (pkt_info) {
+		struct msghdr msg;
+		struct cmsghdr *cmsg;
+		char msg_control[128];
+
 		memset(&msg, 0, sizeof(msg));
 		msg.msg_control = msg_control;
-		msg.msg_controllen = 128;
+		msg.msg_controllen = sizeof(msg_control);
 
 		n = recvmsg(fd, &msg, MSG_PEEK);
-
 		if (n < 0) {
 			if (errno == EAGAIN)
 				return -1;
 			log_error("l2tp: recvmsg: %s\n", strerror(errno));
 			return 0;
@@ -274,12 +277,12 @@ int l2tp_recv(int fd, struct l2tp_packet_t **p, struct in_pktinfo *pkt_info,
 		return 0;
 	}
 	hdr = (struct l2tp_hdr_t *)buf;
 	ptr = (uint8_t *)(hdr + 1);
 
-	n = recvfrom(fd, buf, L2TP_MAX_PACKET_SIZE, 0, &addr, &len);
-
+	addr_len = sizeof(addr);
+	n = recvfrom(fd, buf, L2TP_MAX_PACKET_SIZE, 0, &addr, &addr_len);
 	if (n < 0) {
 		mempool_free(buf);
 		if (errno == EAGAIN) {
 			return -1;
 		} else if (errno == ECONNREFUSED) {
@@ -287,49 +290,54 @@ int l2tp_recv(int fd, struct l2tp_packet_t **p, struct in_pktinfo *pkt_info,
 		}
 		log_error("l2tp: recv: %s\n", strerror(errno));
 		return 0;
 	}
 
-	if (n < 6) {
+	if (n < sizeof(*hdr)) {
 		if (conf_verbose)
 			log_warn("l2tp: short packet received (%i/%zu)\n", n, sizeof(*hdr));
 		goto out_err_hdr;
 	}
 
-	if (hdr->T == 0)
+	hdr->flags = ntohs(hdr->flags);
+	if (!(hdr->flags & L2TP_FLAG_T))
 		goto out_err_hdr;
-
-	if (n < ntohs(hdr->length)) {
+	if (!(hdr->flags & L2TP_FLAG_L)) {
 		if (conf_verbose)
-			log_warn("l2tp: short packet received (%i/%i)\n", n, ntohs(hdr->length));
+			log_warn("l2tp: incorrect control message received (L=0)\n");
 		goto out_err_hdr;
 	}
-
-	if (hdr->ver == 2) {
-		if (hdr->L == 0) {
-			if (conf_verbose)
-				log_warn("l2tp: incorrect message received (L=0)\n");
-			if (!conf_avp_permissive)
-			    goto out_err_hdr;
-		}
-
-		if (hdr->S == 0) {
+	if (!(hdr->flags & L2TP_FLAG_S)) {
+		if (conf_verbose)
+			log_warn("l2tp: incorrect control message received (S=0)\n");
+		goto out_err_hdr;
+	}
+	switch (hdr->flags & L2TP_VER_MASK) {
+	case 2:
+		if (hdr->flags & L2TP_FLAG_O) {
 			if (conf_verbose)
-				log_warn("l2tp: incorrect message received (S=0)\n");
-			if (!conf_avp_permissive)
-			    goto out_err_hdr;
+				log_warn("l2tp: incorrect control message received (O=1)\n");
+			goto out_err_hdr;
 		}
+		break;
+	case 3:
+		break;
+	default:
+		if (conf_verbose)
+			log_warn("l2tp: protocol version %i is not supported\n",
+				 hdr->flags & L2TP_VER_MASK);
+		goto out_err_hdr;
+	}
 
-		if (hdr->O == 1) {
-			if (conf_verbose)
-				log_warn("l2tp: incorrect message received (O=1)\n");
-			if (!conf_avp_permissive)
-			    goto out_err_hdr;
-		}
-	} else if (hdr->ver != 3) {
+	length = ntohs(hdr->length);
+	if (length < sizeof(*hdr)) {
 		if (conf_verbose)
-			log_warn("l2tp: protocol version %i is not supported\n", hdr->ver);
+			log_warn("l2tp: short packet received (%i/%zu)\n", length, sizeof(*hdr));
+		goto out_err_hdr;
+	} else if (n < length) {
+		if (conf_verbose)
+			log_warn("l2tp: short packet received (%i/%i)\n", n, length);
 		goto out_err_hdr;
 	}
 
 	pack = mempool_alloc(pack_pool);
 	if (!pack) {
@@ -340,47 +348,67 @@ int l2tp_recv(int fd, struct l2tp_packet_t **p, struct in_pktinfo *pkt_info,
 	memset(pack, 0, sizeof(*pack));
 	INIT_LIST_HEAD(&pack->attrs);
 
 	memcpy(&pack->addr, &addr, sizeof(addr));
 	memcpy(&pack->hdr, hdr, sizeof(*hdr));
-	length = ntohs(hdr->length) - sizeof(*hdr);
+	length -= sizeof(*hdr);
 
-	while (length) {
-		*(uint16_t *)ptr = ntohs(*(uint16_t *)ptr);
-		avp = (struct l2tp_avp_t *)ptr;
+	while (length > 0) {
+		struct l2tp_dict_attr_t *da;
+		struct l2tp_attr_t *attr;
+		uint16_t avp_len;
 
-		if (avp->length > length) {
+		if (length < sizeof(*avp)) {
+			if (conf_verbose)
+				log_warn("l2tp: short avp received\n");
+			goto out_err;
+		}
+
+		avp = (struct l2tp_avp_t *)ptr;
+		avp->flags = ntohs(avp->flags);
+		avp_len = avp->flags & L2TP_AVP_LEN_MASK;
+		if (avp_len < sizeof(*avp)) {
+			if (conf_verbose)
+				log_warn("l2tp: short avp received\n");
+			goto out_err;
+		} else if (length < avp_len) {
 			if (conf_verbose)
 				log_warn("l2tp: incorrect avp received (exceeds message length)\n");
 			goto out_err;
 		}
 
 		if (avp->vendor)
 			goto skip;
 
 		da = l2tp_dict_find_attr_by_id(ntohs(avp->type));
 		if (!da) {
-			if (conf_verbose)
-				log_warn("l2tp: unknown avp received (type=%i, M=%u)\n", ntohs(avp->type), avp->M);
-			if (avp->M && !conf_avp_permissive)
+			if (conf_verbose) {
+				log_warn("l2tp: unknown avp received (type=%i, M=%u)\n",
+					 ntohs(avp->type), !!(avp->flags & L2TP_AVP_FLAG_M));
+			}
+			if ((avp->flags & L2TP_AVP_FLAG_M) && !conf_avp_permissive)
 				goto out_err;
 		} else {
-			if (da->M != -1 && da->M != avp->M) {
-				if (conf_verbose)
-					log_warn("l2tp: incorrect avp received (type=%i, M=%i, must be %i)\n", ntohs(avp->type), avp->M, da->M);
+			if (da->M != -1 && !da->M != !(avp->flags & L2TP_AVP_FLAG_M)) {
+				if (conf_verbose) {
+					log_warn("l2tp: incorrect avp received (type=%i, M=%i, must be %i)\n",
+						 ntohs(avp->type), !!(avp->flags & L2TP_AVP_FLAG_M), da->M);
+				}
 				if (!conf_avp_permissive)
 				    goto out_err;
 			}
 
-			if (da->H != -1 && da->H != avp->H) {
-				if (conf_verbose)
-					log_warn("l2tp: incorrect avp received (type=%i, H=%i, must be %i)\n", ntohs(avp->type), avp->H, da->H);
+			if (da->H != -1 && !da->H != !(avp->flags & L2TP_AVP_FLAG_H)) {
+				if (conf_verbose) {
+					log_warn("l2tp: incorrect avp received (type=%i, H=%i, must be %i)\n",
+						 ntohs(avp->type), !!(avp->flags & L2TP_AVP_FLAG_H), da->H);
+				}
 				if (!conf_avp_permissive)
 				    goto out_err;
 			}
 
-			if (avp->H) {
+			if (avp->flags & L2TP_AVP_FLAG_H) {
 				if (!RV) {
 					if (conf_verbose)
 						log_warn("l2tp: incorrect avp received (type=%i, H=1, but Random-Vector is not received)\n", ntohs(avp->type));
 					goto out_err;
 				}
@@ -391,28 +419,25 @@ int l2tp_recv(int fd, struct l2tp_packet_t **p, struct in_pktinfo *pkt_info,
 						  ntohs(avp->type));
 					goto out_err;
 				}
 				if (decode_avp(avp, RV, secret, secret_len) < 0)
 					goto out_err;
-			}
 
-			attr = mempool_alloc(attr_pool);
-			memset(attr, 0, sizeof(*attr));
-			list_add_tail(&attr->entry, &pack->attrs);
-
-			if (avp->H) {
 				orig_avp_len = ntohs(*(uint16_t *)avp->val) + sizeof(*avp);
 				orig_avp_val = avp->val + sizeof(uint16_t);
 			} else {
-				orig_avp_len = avp->length;
+				orig_avp_len = avp_len;
 				orig_avp_val = avp->val;
 			}
 
+			attr = mempool_alloc(attr_pool);
+			memset(attr, 0, sizeof(*attr));
 			attr->attr = da;
-			attr->M = avp->M;
+			attr->M = !!(avp->flags & L2TP_AVP_FLAG_M);
 			attr->H = 0;
 			attr->length = orig_avp_len - sizeof(*avp);
+			list_add_tail(&attr->entry, &pack->attrs);
 
 			if (attr->attr->id == Random_Vector)
 				RV = attr;
 
 			switch (da->type) {
@@ -445,12 +470,12 @@ int l2tp_recv(int fd, struct l2tp_packet_t **p, struct in_pktinfo *pkt_info,
 					attr->val.string[attr->length] = 0;
 					break;
 			}
 		}
 skip:
-		ptr += avp->length;
-		length -= avp->length;
+		ptr += avp_len;
+		length -= avp_len;
 	}
 
 	*p = pack;
 
 	mempool_free(buf);
@@ -471,38 +496,38 @@ out_err_mem:
 	goto out_err;
 }
 
 int l2tp_packet_send(int sock, struct l2tp_packet_t *pack)
 {
-	uint8_t *buf = mempool_alloc(buf_pool);
+	struct l2tp_hdr_t *hdr;
 	struct l2tp_avp_t *avp;
 	struct l2tp_attr_t *attr;
-	uint8_t *ptr;
-	int n;
-	int len = sizeof(pack->hdr);
+	uint8_t *buf, *ptr;
+	int n, len;
 
+	buf = mempool_alloc(buf_pool);
 	if (!buf) {
 		log_emerg("l2tp: out of memory\n");
 		return -1;
 	}
 
 	memset(buf, 0, L2TP_MAX_PACKET_SIZE);
-
-	ptr = buf + sizeof(pack->hdr);
+	hdr = (struct l2tp_hdr_t *)buf;
+	ptr = (uint8_t *)(hdr + 1);
+	len = sizeof(pack->hdr);
 
 	list_for_each_entry(attr, &pack->attrs, entry) {
 		if (len + sizeof(*avp) + attr->length >= L2TP_MAX_PACKET_SIZE) {
 			log_error("l2tp: cann't send packet (exceeds maximum size)\n");
 			mempool_free(buf);
 			return -1;
 		}
 		avp = (struct l2tp_avp_t *)ptr;
 		avp->type = htons(attr->attr->id);
-		avp->M = attr->M;
-		avp->H = attr->H;
-		avp->length = sizeof(*avp) + attr->length;
-		*(uint16_t *)ptr = htons(*(uint16_t *)ptr);
+		avp->flags = htons((attr->M ? L2TP_AVP_FLAG_M : 0) |
+				   (attr->H ? L2TP_AVP_FLAG_H : 0) |
+				   ((sizeof(*avp) + attr->length) & L2TP_AVP_LEN_MASK));
 		if (attr->H)
 			memcpy(avp->val, attr->val.octets, attr->length);
 		else
 			switch (attr->attr->type) {
 			case ATTR_TYPE_INT16:
@@ -523,13 +548,13 @@ int l2tp_packet_send(int sock, struct l2tp_packet_t *pack)
 		len += sizeof(*avp) + attr->length;
 	}
 
 	pack->hdr.length = htons(len);
 	memcpy(buf, &pack->hdr, sizeof(pack->hdr));
+	hdr->flags = htons(pack->hdr.flags);
 
-	n = sendto(sock, buf, ntohs(pack->hdr.length), 0,
-		   &pack->addr, sizeof(pack->addr));
+	n = sendto(sock, buf, len, 0, &pack->addr, sizeof(pack->addr));
 	mempool_free(buf);
 
 	if (n < 0) {
 		if (errno == EAGAIN) {
 			if (conf_verbose)
@@ -539,13 +564,13 @@ int l2tp_packet_send(int sock, struct l2tp_packet_t *pack)
 				log_warn("l2tp: sendto: %s\n", strerror(errno));
 			return -1;
 		}
 	}
 
-	if (n != ntohs(pack->hdr.length)) {
+	if (n != len) {
 		if (conf_verbose)
-			log_warn("l2tp: short write (%i/%i)\n", n, ntohs(pack->hdr.length));
+			log_warn("l2tp: short write (%i/%i)\n", n, len);
 	}
 
 	return 0;
 }
 

Vulnerable code: 	uint8_t P:1;
	uint8_t O:1;
	uint8_t reserved2:1;
	uint8_t S:1;
	uint8_t reserved1:2;
	uint8_t L:1;
	uint8_t T:1;
	uint8_t ver:4;
	uint8_t reserved3:4;
/*#define L2TP_T(hdr) (hdr->flags >> 15)
#define L2TP_L(hdr) ((hdr->flags >> 14) & 1)
#define L2TP_S(hdr) ((hdr->flags >> 10) & 1)
#define L2TP_O(hdr) ((hdr->flags >> 8) & 1)
#define L2TP_VER(hdr) (hdr->flags & 0xf)*/
	uint16_t length:10;
	uint16_t reserved:4;
	uint16_t H:1;
	uint16_t M:1;

	if (pack->hdr.ver == 2) {
	} else {
	pack->hdr.ver = ver;
	pack->hdr.T = 1;
	pack->hdr.L = 1;
	pack->hdr.S = 1;
	if (avp->length < sizeof(struct l2tp_avp_t) + 2) {
			 ntohs(avp->type), avp->length);
	attr_len = avp->length - sizeof(struct l2tp_avp_t);
	int n, length;
	uint8_t *buf;
	struct l2tp_dict_attr_t *da;
	struct l2tp_attr_t *attr, *RV = NULL;
	uint8_t *ptr;
	struct l2tp_packet_t *pack;
	socklen_t len = sizeof(addr);
	struct msghdr msg;
	char msg_control[128];
	struct cmsghdr *cmsg;
  *p = NULL;
		msg.msg_controllen = 128;

	n = recvfrom(fd, buf, L2TP_MAX_PACKET_SIZE, 0, &addr, &len);

	if (n < 6) {
	if (hdr->T == 0)

	if (n < ntohs(hdr->length)) {
			log_warn("l2tp: short packet received (%i/%i)\n", n, ntohs(hdr->length));

	if (hdr->ver == 2) {
		if (hdr->L == 0) {
			if (conf_verbose)
				log_warn("l2tp: incorrect message received (L=0)\n");
			if (!conf_avp_permissive)
			    goto out_err_hdr;
		}

		if (hdr->S == 0) {
				log_warn("l2tp: incorrect message received (S=0)\n");
			if (!conf_avp_permissive)
			    goto out_err_hdr;
		if (hdr->O == 1) {
			if (conf_verbose)
				log_warn("l2tp: incorrect message received (O=1)\n");
			if (!conf_avp_permissive)
			    goto out_err_hdr;
		}
	} else if (hdr->ver != 3) {
			log_warn("l2tp: protocol version %i is not supported\n", hdr->ver);
	length = ntohs(hdr->length) - sizeof(*hdr);
	while (length) {
		*(uint16_t *)ptr = ntohs(*(uint16_t *)ptr);
		avp = (struct l2tp_avp_t *)ptr;
		if (avp->length > length) {
			if (conf_verbose)
				log_warn("l2tp: unknown avp received (type=%i, M=%u)\n", ntohs(avp->type), avp->M);
			if (avp->M && !conf_avp_permissive)
			if (da->M != -1 && da->M != avp->M) {
				if (conf_verbose)
					log_warn("l2tp: incorrect avp received (type=%i, M=%i, must be %i)\n", ntohs(avp->type), avp->M, da->M);
			if (da->H != -1 && da->H != avp->H) {
				if (conf_verbose)
					log_warn("l2tp: incorrect avp received (type=%i, H=%i, must be %i)\n", ntohs(avp->type), avp->H, da->H);
			if (avp->H) {
			}
			attr = mempool_alloc(attr_pool);
			memset(attr, 0, sizeof(*attr));
			list_add_tail(&attr->entry, &pack->attrs);

			if (avp->H) {
				orig_avp_len = avp->length;
			attr->M = avp->M;
		ptr += avp->length;
		length -= avp->length;
	uint8_t *buf = mempool_alloc(buf_pool);
	uint8_t *ptr;
	int n;
	int len = sizeof(pack->hdr);

	ptr = buf + sizeof(pack->hdr);
		avp->M = attr->M;
		avp->H = attr->H;
		avp->length = sizeof(*avp) + attr->length;
		*(uint16_t *)ptr = htons(*(uint16_t *)ptr);
	n = sendto(sock, buf, ntohs(pack->hdr.length), 0,
		   &pack->addr, sizeof(pack->addr));
	if (n != ntohs(pack->hdr.length)) {
			log_warn("l2tp: short write (%i/%i)\n", n, ntohs(pack->hdr.length));
Fixed code: #define L2TP_FLAG_T   0x8000
#define L2TP_FLAG_L   0x4000
#define L2TP_FLAG_S   0x0800
#define L2TP_FLAG_O   0x0200
#define L2TP_FLAG_P   0x0100
#define L2TP_VER_MASK 0x000f

	uint16_t flags;
#define L2TP_AVP_FLAG_M   0x8000
#define L2TP_AVP_FLAG_H   0x4000
#define L2TP_AVP_LEN_MASK 0x03ff
	uint16_t flags;
	switch (pack->hdr.flags & L2TP_VER_MASK) {
	case 2:
		break;
	case 3:
		break;
	default:
		print("[L2TP unknown version]\n");
		return;
	pack->hdr.flags = L2TP_FLAG_T | L2TP_FLAG_L | L2TP_FLAG_S | (ver & L2TP_VER_MASK);
	uint16_t avp_len;
	avp_len = avp->flags & L2TP_AVP_LEN_MASK;
	if (avp_len < sizeof(struct l2tp_avp_t) + 2) {
			 ntohs(avp->type), avp_len);
	attr_len = avp_len - sizeof(struct l2tp_avp_t);
	struct l2tp_packet_t *pack;
	struct l2tp_attr_t *RV = NULL;
	socklen_t addr_len;
	uint8_t *buf, *ptr;
	int n, length;
	*p = NULL;
		struct msghdr msg;
		struct cmsghdr *cmsg;
		char msg_control[128];

		msg.msg_controllen = sizeof(msg_control);
	addr_len = sizeof(addr);
	n = recvfrom(fd, buf, L2TP_MAX_PACKET_SIZE, 0, &addr, &addr_len);
	if (n < sizeof(*hdr)) {
	hdr->flags = ntohs(hdr->flags);
	if (!(hdr->flags & L2TP_FLAG_T))
	if (!(hdr->flags & L2TP_FLAG_L)) {
			log_warn("l2tp: incorrect control message received (L=0)\n");
	if (!(hdr->flags & L2TP_FLAG_S)) {
		if (conf_verbose)
			log_warn("l2tp: incorrect control message received (S=0)\n");
		goto out_err_hdr;
	}
	switch (hdr->flags & L2TP_VER_MASK) {
	case 2:
		if (hdr->flags & L2TP_FLAG_O) {
				log_warn("l2tp: incorrect control message received (O=1)\n");
			goto out_err_hdr;
		break;
	case 3:
		break;
	default:
		if (conf_verbose)
			log_warn("l2tp: protocol version %i is not supported\n",
				 hdr->flags & L2TP_VER_MASK);
		goto out_err_hdr;
	}
	length = ntohs(hdr->length);
	if (length < sizeof(*hdr)) {
			log_warn("l2tp: short packet received (%i/%zu)\n", length, sizeof(*hdr));
		goto out_err_hdr;
	} else if (n < length) {
		if (conf_verbose)
			log_warn("l2tp: short packet received (%i/%i)\n", n, length);
	length -= sizeof(*hdr);
	while (length > 0) {
		struct l2tp_dict_attr_t *da;
		struct l2tp_attr_t *attr;
		uint16_t avp_len;
		if (length < sizeof(*avp)) {
			if (conf_verbose)
				log_warn("l2tp: short avp received\n");
			goto out_err;
		}

		avp = (struct l2tp_avp_t *)ptr;
		avp->flags = ntohs(avp->flags);
		avp_len = avp->flags & L2TP_AVP_LEN_MASK;
		if (avp_len < sizeof(*avp)) {
			if (conf_verbose)
				log_warn("l2tp: short avp received\n");
			goto out_err;
		} else if (length < avp_len) {
			if (conf_verbose) {
				log_warn("l2tp: unknown avp received (type=%i, M=%u)\n",
					 ntohs(avp->type), !!(avp->flags & L2TP_AVP_FLAG_M));
			}
			if ((avp->flags & L2TP_AVP_FLAG_M) && !conf_avp_permissive)
			if (da->M != -1 && !da->M != !(avp->flags & L2TP_AVP_FLAG_M)) {
				if (conf_verbose) {
					log_warn("l2tp: incorrect avp received (type=%i, M=%i, must be %i)\n",
						 ntohs(avp->type), !!(avp->flags & L2TP_AVP_FLAG_M), da->M);
				}
			if (da->H != -1 && !da->H != !(avp->flags & L2TP_AVP_FLAG_H)) {
				if (conf_verbose) {
					log_warn("l2tp: incorrect avp received (type=%i, H=%i, must be %i)\n",
						 ntohs(avp->type), !!(avp->flags & L2TP_AVP_FLAG_H), da->H);
				}
			if (avp->flags & L2TP_AVP_FLAG_H) {
				orig_avp_len = avp_len;
			attr = mempool_alloc(attr_pool);
			memset(attr, 0, sizeof(*attr));
			attr->M = !!(avp->flags & L2TP_AVP_FLAG_M);
			list_add_tail(&attr->entry, &pack->attrs);
		ptr += avp_len;
		length -= avp_len;
	struct l2tp_hdr_t *hdr;
	uint8_t *buf, *ptr;
	int n, len;
	buf = mempool_alloc(buf_pool);
	hdr = (struct l2tp_hdr_t *)buf;
	ptr = (uint8_t *)(hdr + 1);
	len = sizeof(pack->hdr);
		avp->flags = htons((attr->M ? L2TP_AVP_FLAG_M : 0) |
				   (attr->H ? L2TP_AVP_FLAG_H : 0) |
				   ((sizeof(*avp) + attr->length) & L2TP_AVP_LEN_MASK));
	hdr->flags = htons(pack->hdr.flags);
	n = sendto(sock, buf, len, 0, &pack->addr, sizeof(pack->addr));
	if (n != len) {
			log_warn("l2tp: short write (%i/%i)\n", n, len);