OpenFlow: Modernize packet parsing style.

Most OpenFlow functions operated on the (cp, ep, len) tuple, returned a
pointer to tell the calling function where to decode next (which was not
a good idea because the calling functions had the values of cp and len in
the first place) and set the pointer to ep to indicate truncated data.
Change them to use the (cp, len) tuple, to be void and to use nd_trunc().
Update the header comment to explain this new convention.

Now if a low-level function gets an offset wrong, as in the bug recently
fixed in of10_queue_props_print(), and manages to return, the error will
not propagate into the calling function, which will be able to continue
the decoding.

Since cp does not have to be current anymore when the [void] function
returns, remove a number of cp increments just before the return.
Introduce OF_FWD(), which modifies both cp and len in sync, and
OF_CHK_FWD(), which also does a bounds check, and use these macros
instead of incrementing cp directly in every function that has both cp
and len arguments. Update the code to account for len that is
decrementing as cp is incrementing.

Commit ad69daa2 was fine for its time, but the introduction of the GET
macros had made it obsolete. Instead of reintroducing ep back merge
of_header_body_print() into openflow_print() to simplify the handling of
pointers, sizes and error conditions, all in the style above.

While at it, subtract OF_HEADER_FIXLEN from the declared OpenFlow message
length before passing it to of10_header_body_print() and make the latter
the only function that has to know about the common header size (clarify
it in the comment). This and the sufficiently current cp and len
disencumber the following functions of type-specific length calculations:
of10_features_reply_print(), of10_flow_mod_print(),
of10_stats_request_print(), of10_stats_reply_print(),
of10_packet_out_print(), of10_packet_in_print() and of10_error_print().

The resulting code in most cases addresses the not yet processed part of
the declared structure with just cp and len: nextfunc(ndo, cp, len) and
ND_TCHECK_LEN(cp, len) do the right thing regardless of how far the
current decoder has made it into the current structure. This makes all
the cp0 and len0 snapshot variables obsolete, so remove them too.

Update a test case output: the missing closing quote now correctly means
that the snapshot end is inside the "data" field of a BSN_SHELL_COMMAND
vendor message, not after it.
diff --git a/netdissect.h b/netdissect.h
index da97f2d..3b9aaf4 100644
--- a/netdissect.h
+++ b/netdissect.h
@@ -642,7 +642,7 @@
 extern void ntp_print(netdissect_options *, const u_char *, u_int);
 extern void oam_print(netdissect_options *, const u_char *, u_int, u_int);
 extern void olsr_print(netdissect_options *, const u_char *, u_int, int);
-extern void openflow_print(netdissect_options *, const u_char *, const u_int);
+extern void openflow_print(netdissect_options *, const u_char *, u_int);
 extern void ospf6_print(netdissect_options *, const u_char *, u_int);
 extern void ospf_print(netdissect_options *, const u_char *, u_int, const u_char *);
 extern int ospf_grace_lsa_print(netdissect_options *, const u_char *, u_int);
diff --git a/openflow.h b/openflow.h
index 279a6ae..4c2d9a7 100644
--- a/openflow.h
+++ b/openflow.h
@@ -30,6 +30,17 @@
 /* for netdissect_options */
 #include "netdissect.h"
 
+#define OF_FWD(n) { \
+	cp += (n); \
+	len -= (n); \
+}
+
+#define OF_CHK_FWD(n) { \
+	ND_TCHECK_LEN(cp, (n)); \
+	cp += (n); \
+	len -= (n); \
+}
+
 #define OF_HEADER_FIXLEN 8
 
 #define ONF_EXP_ONF               0x4f4e4600
@@ -45,7 +56,6 @@
 /*
  * Routines to print packets for various versions of OpenFlow.
  */
-extern const u_char *of10_header_body_print(netdissect_options *ndo,
-	const u_char *, const u_char *,
-	const uint8_t, const uint16_t, const uint32_t);
+extern void of10_header_body_print(netdissect_options *ndo,
+	const u_char *, const uint8_t, uint16_t, const uint32_t);
 extern const char * of_vendor_name(const uint32_t);
diff --git a/print-openflow-1.0.c b/print-openflow-1.0.c
index da0289f..557d1e9 100644
--- a/print-openflow-1.0.c
+++ b/print-openflow-1.0.c
@@ -8,15 +8,16 @@
  *
  * [OF10] https://www.opennetworking.org/wp-content/uploads/2013/04/openflow-spec-v1.0.0.pdf
  *
- * Most functions in this file take 3 arguments into account:
+ * Most functions in this file take the following arguments:
  * * cp -- the pointer to the first octet to decode
- * * len -- the length of the current structure as declared on the wire
- * * ep -- the pointer to the end of the captured frame
- * They return either the pointer to the next not-yet-decoded part of the frame
- * or the value of ep, which means the current frame processing is over as it
- * has been fully decoded or is invalid or truncated. This way it is possible
- * to chain and nest such functions uniformly to decode an OF1.0 message, which
- * consists of several layers of nested structures.
+ * * len -- the declared length of the structure to decode
+ * The convention is that a printer function returns iff the given structure is
+ * completely within the packet buffer; otherwise it processes the part that is
+ * within the buffer, sooner of later takes the "truncated packet" shortcut via
+ * nd_trunc() and never returns. With that in mind, the function may return
+ * without printing the structure completely if it is invalid or the ndo_vflag
+ * value is not high enough. This way the calling function can try to decode
+ * the next data item.
  *
  * Decoding of Ethernet frames nested in OFPT_PACKET_IN and OFPT_PACKET_OUT
  * messages is done only when the verbosity level set by command-line argument
@@ -722,37 +723,35 @@
 	ND_PRINT((v & u) ? ") (bogus)" : ")");
 }
 
-static const u_char *
+static void
 of10_data_print(netdissect_options *ndo,
-                const u_char *cp, const u_char *ep, const u_int len)
+                const u_char *cp, const u_int len)
 {
 	if (len == 0)
-		return cp;
+		return;
 	/* data */
 	ND_PRINT("\n\t data (%u octets)", len);
 	if (ndo->ndo_vflag >= 2)
 		hex_and_ascii_print(ndo, "\n\t  ", cp, len);
 	else
 		ND_TCHECK_LEN(cp, len);
-	return cp + len;
+	return;
 
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
-static const u_char *
+static void
 of10_bsn_message_print(netdissect_options *ndo,
-                       const u_char *cp, const u_char *ep, const u_int len)
+                       const u_char *cp, u_int len)
 {
-	const u_char *cp0 = cp;
 	uint32_t subtype;
 
 	if (len < 4)
 		goto invalid;
 	/* subtype */
 	subtype = GET_BE_U_4(cp);
-	cp += 4;
+	OF_FWD(4);
 	ND_PRINT("\n\t subtype %s", tok2str(bsn_subtype_str, "unknown (0x%08x)", subtype));
 	switch (subtype) {
 	case BSN_GET_IP_MASK_REQUEST:
@@ -768,15 +767,14 @@
 		 * +---------------+---------------+---------------+---------------+
 		 *
 		 */
-		if (len != 12)
+		if (len != 8)
 			goto invalid;
 		/* index */
 		ND_PRINT(", index %u", GET_U_1(cp));
-		cp += 1;
+		OF_FWD(1);
 		/* pad */
 		/* Always the last field, check bounds. */
 		ND_TCHECK_7(cp);
-		cp += 7;
 		break;
 	case BSN_SET_IP_MASK:
 	case BSN_GET_IP_MASK_REPLY:
@@ -792,16 +790,15 @@
 		 * +---------------+---------------+---------------+---------------+
 		 *
 		 */
-		if (len != 12)
+		if (len != 8)
 			goto invalid;
 		/* index */
 		ND_PRINT(", index %u", GET_U_1(cp));
-		cp += 1;
+		OF_FWD(1);
 		/* pad */
-		cp += 3;
+		OF_FWD(3);
 		/* mask */
 		ND_PRINT(", mask %s", GET_IPADDR_STRING(cp));
-		cp += 4;
 		break;
 	case BSN_SET_MIRRORING:
 	case BSN_GET_MIRRORING_REQUEST:
@@ -816,16 +813,15 @@
 		 * +---------------+---------------+---------------+---------------+
 		 *
 		 */
-		if (len != 8)
+		if (len != 4)
 			goto invalid;
 		/* report_mirror_ports */
 		ND_PRINT(", report_mirror_ports %s",
 			 tok2str(bsn_onoff_str, "bogus (%u)", GET_U_1(cp)));
-		cp += 1;
+		OF_FWD(1);
 		/* pad */
 		/* Always the last field, check bounds. */
 		ND_TCHECK_3(cp);
-		cp += 3;
 		break;
 	case BSN_GET_INTERFACES_REQUEST:
 	case BSN_GET_L2_TABLE_REQUEST:
@@ -840,7 +836,7 @@
 		 * +---------------+---------------+---------------+---------------+
 		 *
 		 */
-		if (len != 4)
+		if (len)
 			goto invalid;
 		break;
 	case BSN_VIRTUAL_PORT_REMOVE_REQUEST:
@@ -854,11 +850,10 @@
 		 * +---------------+---------------+---------------+---------------+
 		 *
 		 */
-		if (len != 8)
+		if (len != 4)
 			goto invalid;
 		/* vport_no */
 		ND_PRINT(", vport_no %u", GET_BE_U_4(cp));
-		cp += 4;
 		break;
 	case BSN_SHELL_COMMAND:
 		/*
@@ -873,19 +868,15 @@
 		 * +---------------+---------------+--------
 		 *
 		 */
-		if (len < 8)
+		if (len < 4)
 			goto invalid;
 		/* service */
 		ND_PRINT(", service %u", GET_BE_U_4(cp));
-		cp += 4;
+		OF_FWD(4);
 		/* data */
 		ND_PRINT(", data '");
-		if (nd_printn(ndo, cp, len - 8, ep)) {
-			ND_PRINT("'");
-			goto trunc;
-		}
+		nd_printn(ndo, cp, len, NULL);
 		ND_PRINT("'");
-		cp += len - 8;
 		break;
 	case BSN_SHELL_OUTPUT:
 		/*
@@ -901,12 +892,8 @@
 		/* already checked that len >= 4 */
 		/* data */
 		ND_PRINT(", data '");
-		if (nd_printn(ndo, cp, len - 4, ep)) {
-			ND_PRINT("'");
-			goto trunc;
-		}
+		nd_printn(ndo, cp, len, NULL);
 		ND_PRINT("'");
-		cp += len - 4;
 		break;
 	case BSN_SHELL_STATUS:
 		/*
@@ -919,39 +906,35 @@
 		 * +---------------+---------------+---------------+---------------+
 		 *
 		 */
-		if (len != 8)
+		if (len != 4)
 			goto invalid;
 		/* status */
 		ND_PRINT(", status 0x%08x", GET_BE_U_4(cp));
-		cp += 4;
 		break;
 	default:
-		ND_TCHECK_LEN(cp, len - 4);
-		cp += len - 4;
+		ND_TCHECK_LEN(cp, len);
 	}
-	return cp;
+	return;
 
 invalid: /* skip the undersized data */
 	nd_print_invalid(ndo);
-	ND_TCHECK_LEN(cp0, len);
-	return cp0 + len;
+	ND_TCHECK_LEN(cp, len);
+	return;
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
-static const u_char *
+static void
 of10_bsn_actions_print(netdissect_options *ndo,
-                       const u_char *cp, const u_char *ep, const u_int len)
+                       const u_char *cp, u_int len)
 {
-	const u_char *cp0 = cp;
 	uint32_t subtype, vlan_tag;
 
 	if (len < 4)
 		goto invalid;
 	/* subtype */
 	subtype = GET_BE_U_4(cp);
-	cp += 4;
+	OF_FWD(4);
 	ND_PRINT("\n\t  subtype %s", tok2str(bsn_action_subtype_str, "unknown (0x%08x)", subtype));
 	switch (subtype) {
 	case BSN_ACTION_MIRROR:
@@ -969,14 +952,14 @@
 		 * +---------------+---------------+---------------+---------------+
 		 *
 		 */
-		if (len != 16)
+		if (len != 12)
 			goto invalid;
 		/* dest_port */
 		ND_PRINT(", dest_port %u", GET_BE_U_4(cp));
-		cp += 4;
+		OF_FWD(4);
 		/* vlan_tag */
 		vlan_tag = GET_BE_U_4(cp);
-		cp += 4;
+		OF_FWD(4);
 		switch (vlan_tag >> 16) {
 		case 0:
 			ND_PRINT(", vlan_tag none");
@@ -990,88 +973,84 @@
 		/* copy_stage */
 		ND_PRINT(", copy_stage %s",
 			 tok2str(bsn_mirror_copy_stage_str, "unknown (%u)", GET_U_1(cp)));
-		cp += 1;
+		OF_FWD(1);
 		/* pad */
 		/* Always the last field, check bounds. */
 		ND_TCHECK_3(cp);
-		cp += 3;
 		break;
 	default:
-		ND_TCHECK_LEN(cp, len - 4);
-		cp += len - 4;
+		ND_TCHECK_LEN(cp, len);
 	}
-
-	return cp;
+	return;
 
 invalid:
 	nd_print_invalid(ndo);
-	ND_TCHECK_LEN(cp0, len);
-	return cp0 + len;
+	ND_TCHECK_LEN(cp, len);
+	return;
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
-static const u_char *
+static void
 of10_vendor_action_print(netdissect_options *ndo,
-                         const u_char *cp, const u_char *ep, const u_int len)
+                         const u_char *cp, u_int len)
 {
 	uint32_t vendor;
-	const u_char *(*decoder)(netdissect_options *, const u_char *, const u_char *, const u_int);
+	void (*decoder)(netdissect_options *, const u_char *, u_int);
 
 	if (len < 4)
 		goto invalid;
 	/* vendor */
 	vendor = GET_BE_U_4(cp);
-	cp += 4;
+	OF_FWD(4);
 	ND_PRINT(", vendor 0x%08x (%s)", vendor, of_vendor_name(vendor));
 	/* data */
 	decoder =
 		vendor == OUI_BSN         ? of10_bsn_actions_print         :
 		of10_data_print;
-	return decoder(ndo, cp, ep, len - 4);
+	decoder(ndo, cp, len);
+	return;
 
 invalid: /* skip the undersized data */
 	nd_print_invalid(ndo);
 	ND_TCHECK_LEN(cp, len);
-	return cp + len;
+	return;
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
-static const u_char *
+static void
 of10_vendor_message_print(netdissect_options *ndo,
-                          const u_char *cp, const u_char *ep, const u_int len)
+                          const u_char *cp, u_int len)
 {
 	uint32_t vendor;
-	const u_char *(*decoder)(netdissect_options *, const u_char *, const u_char *, u_int);
+	void (*decoder)(netdissect_options *, const u_char *, u_int);
 
 	if (len < 4)
 		goto invalid;
 	/* vendor */
 	vendor = GET_BE_U_4(cp);
-	cp += 4;
+	OF_FWD(4);
 	ND_PRINT(", vendor 0x%08x (%s)", vendor, of_vendor_name(vendor));
 	/* data */
 	decoder =
 		vendor == OUI_BSN         ? of10_bsn_message_print         :
 		of10_data_print;
-	return decoder(ndo, cp, ep, len - 4);
+	decoder(ndo, cp, len);
+	return;
 
 invalid: /* skip the undersized data */
 	nd_print_invalid(ndo);
 	ND_TCHECK_LEN(cp, len);
-	return cp + len;
+	return;
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
 /* Vendor ID is mandatory, data is optional. */
-static const u_char *
+static void
 of10_vendor_data_print(netdissect_options *ndo,
-                       const u_char *cp, const u_char *ep, const u_int len)
+                       const u_char *cp, u_int len)
 {
 	uint32_t vendor;
 
@@ -1079,118 +1058,107 @@
 		goto invalid;
 	/* vendor */
 	vendor = GET_BE_U_4(cp);
-	cp += 4;
+	OF_FWD(4);
 	ND_PRINT(", vendor 0x%08x (%s)", vendor, of_vendor_name(vendor));
 	/* data */
-	return of10_data_print(ndo, cp, ep, len - 4);
+	of10_data_print(ndo, cp, len);
+	return;
 
 invalid: /* skip the undersized data */
 	nd_print_invalid(ndo);
 	ND_TCHECK_LEN(cp, len);
-	return cp + len;
+	return;
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
-static const u_char *
+static void
 of10_packet_data_print(netdissect_options *ndo,
-                       const u_char *cp, const u_char *ep, const u_int len)
+                       const u_char *cp, const u_int len)
 {
 	if (len == 0)
-		return cp;
+		return;
 	/* data */
 	ND_PRINT("\n\t data (%u octets)", len);
 	if (ndo->ndo_vflag < 3) {
 		ND_TCHECK_LEN(cp, len);
-		return cp + len;
+		return;
 	}
 	ndo->ndo_vflag -= 3;
 	ND_PRINT(", frame decoding below\n");
 	ether_print(ndo, cp, len, ND_BYTES_AVAILABLE_AFTER(cp), NULL, NULL);
 	ndo->ndo_vflag += 3;
-	return cp + len;
+	return;
 
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
 /* [OF10] Section 5.2.1 */
-static const u_char *
+static void
 of10_phy_ports_print(netdissect_options *ndo,
-                     const u_char *cp, const u_char *ep, u_int len)
+                     const u_char *cp, u_int len)
 {
-	const u_char *cp0 = cp;
-	const u_int len0 = len;
-
 	while (len) {
 		if (len < OF_PHY_PORT_FIXLEN)
 			goto invalid;
 		/* port_no */
 		ND_PRINT("\n\t  port_no %s",
 			 tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
-		cp += 2;
+		OF_FWD(2);
 		/* hw_addr */
 		ND_PRINT(", hw_addr %s", GET_ETHERADDR_STRING(cp));
-		cp += MAC_ADDR_LEN;
+		OF_FWD(MAC_ADDR_LEN);
 		/* name */
 		ND_PRINT(", name '");
 		(void)nd_print(ndo, cp, cp + OFP_MAX_PORT_NAME_LEN);
 		ND_PRINT("'");
-		cp += OFP_MAX_PORT_NAME_LEN;
+		OF_FWD(OFP_MAX_PORT_NAME_LEN);
 
 		if (ndo->ndo_vflag < 2) {
-			ND_TCHECK_LEN(cp, 24);
-			cp += 24;
-			goto next_port;
+			OF_CHK_FWD(24);
+			continue;
 		}
 		/* config */
 		ND_PRINT("\n\t   config 0x%08x", GET_BE_U_4(cp));
 		of10_bitmap_print(ndo, ofppc_bm, GET_BE_U_4(cp), OFPPC_U);
-		cp += 4;
+		OF_FWD(4);
 		/* state */
 		ND_PRINT("\n\t   state 0x%08x", GET_BE_U_4(cp));
 		of10_bitmap_print(ndo, ofpps_bm, GET_BE_U_4(cp), OFPPS_U);
-		cp += 4;
+		OF_FWD(4);
 		/* curr */
 		ND_PRINT("\n\t   curr 0x%08x", GET_BE_U_4(cp));
 		of10_bitmap_print(ndo, ofppf_bm, GET_BE_U_4(cp), OFPPF_U);
-		cp += 4;
+		OF_FWD(4);
 		/* advertised */
 		ND_PRINT("\n\t   advertised 0x%08x", GET_BE_U_4(cp));
 		of10_bitmap_print(ndo, ofppf_bm, GET_BE_U_4(cp), OFPPF_U);
-		cp += 4;
+		OF_FWD(4);
 		/* supported */
 		ND_PRINT("\n\t   supported 0x%08x", GET_BE_U_4(cp));
 		of10_bitmap_print(ndo, ofppf_bm, GET_BE_U_4(cp), OFPPF_U);
-		cp += 4;
+		OF_FWD(4);
 		/* peer */
 		ND_PRINT("\n\t   peer 0x%08x", GET_BE_U_4(cp));
 		of10_bitmap_print(ndo, ofppf_bm, GET_BE_U_4(cp), OFPPF_U);
-		cp += 4;
-next_port:
-		len -= OF_PHY_PORT_FIXLEN;
+		OF_FWD(4);
 	} /* while */
-	return cp;
+	return;
 
 invalid: /* skip the undersized trailing data */
 	nd_print_invalid(ndo);
-	ND_TCHECK_LEN(cp0, len0);
-	return cp0 + len0;
+	ND_TCHECK_LEN(cp, len);
+	return;
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
 /* [OF10] Section 5.2.2 */
-static const u_char *
+static void
 of10_queue_props_print(netdissect_options *ndo,
-                       const u_char *cp, const u_char *ep, u_int len)
+                       const u_char *cp, u_int len)
 {
-	const u_char *cp0 = cp;
-	const u_int len0 = len;
-
 	while (len) {
 		uint16_t property, plen;
 		u_char plen_bogus = 0, skip = 0;
@@ -1199,18 +1167,17 @@
 			goto invalid;
 		/* property */
 		property = GET_BE_U_2(cp);
-		cp += 2;
+		OF_FWD(2);
 		ND_PRINT("\n\t   property %s", tok2str(ofpqt_str, "invalid (0x%04x)", property));
 		/* len */
 		plen = GET_BE_U_2(cp);
-		cp += 2;
+		OF_FWD(2);
 		ND_PRINT(", len %u", plen);
-		if (plen < OF_QUEUE_PROP_MINLEN || plen > len)
+		if (plen < OF_QUEUE_PROP_MINLEN || plen > len + 4)
 			goto invalid;
 		/* pad */
 		/* Sometimes the last field, check bounds. */
-		ND_TCHECK_4(cp);
-		cp += 4;
+		OF_CHK_FWD(4);
 		/* property-specific constraints and decoding */
 		switch (property) {
 		case OFPQT_NONE:
@@ -1231,45 +1198,37 @@
 			 * plen >= OF_QUEUE_PROP_MINLEN
 			 * cp is OF_QUEUE_PROP_MINLEN bytes in
 			 */
-			ND_TCHECK_LEN(cp, plen - OF_QUEUE_PROP_MINLEN);
-			cp += plen - OF_QUEUE_PROP_MINLEN;
-			goto next_property;
+			OF_CHK_FWD(plen - OF_QUEUE_PROP_MINLEN);
+			continue;
 		}
 		if (property == OFPQT_MIN_RATE) { /* the only case of property decoding */
 			/* rate */
 			uint16_t rate = GET_BE_U_2(cp);
-			cp += 2;
+			OF_FWD(2);
 			if (rate > 1000)
 				ND_PRINT(", rate disabled");
 			else
 				ND_PRINT(", rate %u.%u%%", rate / 10, rate % 10);
 			/* pad */
 			/* Sometimes the last field, check bounds. */
-			ND_TCHECK_6(cp);
-			cp += 6;
+			OF_CHK_FWD(6);
 		}
-next_property:
-		len -= plen;
 	} /* while */
-	return cp;
+	return;
 
 invalid: /* skip the rest of queue properties */
 	nd_print_invalid(ndo);
-	ND_TCHECK_LEN(cp0, len0);
-	return cp0 + len0;
+	ND_TCHECK_LEN(cp, len);
+	return;
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
 /* ibid */
-static const u_char *
+static void
 of10_queues_print(netdissect_options *ndo,
-                  const u_char *cp, const u_char *ep, u_int len)
+                  const u_char *cp, u_int len)
 {
-	const u_char *cp0 = cp;
-	const u_int len0 = len;
-
 	while (len) {
 		uint16_t desclen;
 
@@ -1277,43 +1236,37 @@
 			goto invalid;
 		/* queue_id */
 		ND_PRINT("\n\t  queue_id %u", GET_BE_U_4(cp));
-		cp += 4;
+		OF_FWD(4);
 		/* len */
 		desclen = GET_BE_U_2(cp);
-		cp += 2;
+		OF_FWD(2);
 		ND_PRINT(", len %u", desclen);
-		if (desclen < OF_PACKET_QUEUE_MINLEN || desclen > len)
+		if (desclen < OF_PACKET_QUEUE_MINLEN || desclen > len + 6)
 			goto invalid;
 		/* pad */
 		/* Sometimes the last field, check bounds. */
-		ND_TCHECK_2(cp);
-		cp += 2;
+		OF_CHK_FWD(2);
 		/* properties */
-		if (ndo->ndo_vflag < 2) {
+		if (ndo->ndo_vflag >= 2)
+			of10_queue_props_print(ndo, cp, desclen - OF_PACKET_QUEUE_MINLEN);
+		else
 			ND_TCHECK_LEN(cp, desclen - OF_PACKET_QUEUE_MINLEN);
-			cp += desclen - OF_PACKET_QUEUE_MINLEN;
-			goto next_queue;
-		}
-		if (ep == (cp = of10_queue_props_print(ndo, cp, ep, desclen - OF_PACKET_QUEUE_MINLEN)))
-			return ep; /* end of snapshot */
-next_queue:
-		len -= desclen;
+		OF_FWD(desclen - OF_PACKET_QUEUE_MINLEN);
 	} /* while */
-	return cp;
+	return;
 
 invalid: /* skip the rest of queues */
 	nd_print_invalid(ndo);
-	ND_TCHECK_LEN(cp0, len0);
-	return cp0 + len0;
+	ND_TCHECK_LEN(cp, len);
+	return;
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
 /* [OF10] Section 5.2.3 */
-static const u_char *
+static void
 of10_match_print(netdissect_options *ndo,
-                 const char *pfx, const u_char *cp, const u_char *ep)
+                 const char *pfx, const u_char *cp)
 {
 	uint32_t wildcards;
 	uint16_t dl_type;
@@ -1396,22 +1349,17 @@
 	}
 	else
 		ND_TCHECK_2(cp);
-	return cp + 2;
+	return;
 
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
 /* [OF10] Section 5.2.4 */
-static const u_char *
+static void
 of10_actions_print(netdissect_options *ndo,
-                   const char *pfx, const u_char *cp, const u_char *ep,
-                   u_int len)
+                   const char *pfx, const u_char *cp, u_int len)
 {
-	const u_char *cp0 = cp;
-	const u_int len0 = len;
-
 	while (len) {
 		uint16_t type, alen, output_port;
 		u_char alen_bogus = 0, skip = 0;
@@ -1420,11 +1368,11 @@
 			goto invalid;
 		/* type */
 		type = GET_BE_U_2(cp);
-		cp += 2;
+		OF_FWD(2);
 		ND_PRINT("%saction type %s", pfx, tok2str(ofpat_str, "invalid (0x%04x)", type));
 		/* length */
 		alen = GET_BE_U_2(cp);
-		cp += 2;
+		OF_FWD(2);
 		ND_PRINT(", len %u", alen);
 		/*
 		 * The 4-byte "pad" in the specification is not a field of the
@@ -1434,7 +1382,7 @@
 		 */
 
 		/* On action size underrun/overrun skip the rest of the action list. */
-		if (alen < OF_ACTION_MINLEN || alen > len)
+		if (alen < OF_ACTION_MINLEN || alen > len + 4)
 			goto invalid;
 		/*
 		 * After validating the basic length constraint it will be safe
@@ -1473,195 +1421,184 @@
 			 * alen >= OF_ACTION_MINLEN
 			 * cp is 4 bytes in
 			 */
-			ND_TCHECK_LEN(cp, alen - 4);
-			cp += alen - 4;
-			goto next_action;
+			OF_CHK_FWD(alen - 4);
+			continue;
 		}
 		/* OK to decode the rest of the action structure */
 		switch (type) {
 		case OFPAT_OUTPUT:
 			/* port */
 			output_port = GET_BE_U_2(cp);
-			cp += 2;
+			OF_FWD(2);
 			ND_PRINT(", port %s", tok2str(ofpp_str, "%u", output_port));
 			/* max_len */
 			if (output_port == OFPP_CONTROLLER)
 				ND_PRINT(", max_len %u", GET_BE_U_2(cp));
 			else
 				ND_TCHECK_2(cp);
-			cp += 2;
+			OF_FWD(2);
 			break;
 		case OFPAT_SET_VLAN_VID:
 			/* vlan_vid */
 			ND_PRINT(", vlan_vid %s", vlan_str(GET_BE_U_2(cp)));
-			cp += 2;
+			OF_FWD(2);
 			/* pad */
 			/* Sometimes the last field, check bounds. */
-			ND_TCHECK_2(cp);
-			cp += 2;
+			OF_CHK_FWD(2);
 			break;
 		case OFPAT_SET_VLAN_PCP:
 			/* vlan_pcp */
 			ND_PRINT(", vlan_pcp %s", pcp_str(GET_U_1(cp)));
-			cp += 1;
+			OF_FWD(1);
 			/* pad */
 			/* Sometimes the last field, check bounds. */
-			ND_TCHECK_3(cp);
-			cp += 3;
+			OF_CHK_FWD(3);
 			break;
 		case OFPAT_SET_DL_SRC:
 		case OFPAT_SET_DL_DST:
 			/* dl_addr */
 			ND_PRINT(", dl_addr %s", GET_ETHERADDR_STRING(cp));
-			cp += MAC_ADDR_LEN;
+			OF_FWD(MAC_ADDR_LEN);
 			/* pad */
 			/* Sometimes the last field, check bounds. */
-			ND_TCHECK_6(cp);
-			cp += 6;
+			OF_CHK_FWD(6);
 			break;
 		case OFPAT_SET_NW_SRC:
 		case OFPAT_SET_NW_DST:
 			/* nw_addr */
 			ND_PRINT(", nw_addr %s", GET_IPADDR_STRING(cp));
-			cp += 4;
+			OF_FWD(4);
 			break;
 		case OFPAT_SET_NW_TOS:
 			/* nw_tos */
 			ND_PRINT(", nw_tos 0x%02x", GET_U_1(cp));
-			cp += 1;
+			OF_FWD(1);
 			/* pad */
 			/* Sometimes the last field, check bounds. */
-			ND_TCHECK_3(cp);
-			cp += 3;
+			OF_CHK_FWD(3);
 			break;
 		case OFPAT_SET_TP_SRC:
 		case OFPAT_SET_TP_DST:
 			/* nw_tos */
 			ND_PRINT(", tp_port %u", GET_BE_U_2(cp));
-			cp += 2;
+			OF_FWD(2);
 			/* pad */
 			/* Sometimes the last field, check bounds. */
-			ND_TCHECK_2(cp);
-			cp += 2;
+			OF_CHK_FWD(2);
 			break;
 		case OFPAT_ENQUEUE:
 			/* port */
 			ND_PRINT(", port %s",
 				 tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
-			cp += 2;
+			OF_FWD(2);
 			/* pad */
-			ND_TCHECK_6(cp);
-			cp += 6;
+			OF_FWD(6);
 			/* queue_id */
 			ND_PRINT(", queue_id %s",
 				 tok2str(ofpq_str, "%u", GET_BE_U_4(cp)));
-			cp += 4;
+			OF_FWD(4);
 			break;
 		case OFPAT_VENDOR:
-			if (ep == (cp = of10_vendor_action_print(ndo, cp, ep, alen - 4)))
-				return ep; /* end of snapshot */
+			of10_vendor_action_print(ndo, cp, alen - 4);
+			OF_FWD(alen - 4);
 			break;
 		case OFPAT_STRIP_VLAN:
 			/* pad */
 			/* Sometimes the last field, check bounds. */
-			ND_TCHECK_4(cp);
-			cp += 4;
+			OF_CHK_FWD(4);
 			break;
 		} /* switch */
-next_action:
-		len -= alen;
 	} /* while */
-	return cp;
+	return;
 
 invalid: /* skip the rest of actions */
 	nd_print_invalid(ndo);
-	ND_TCHECK_LEN(cp0, len0);
-	return cp0 + len0;
+	ND_TCHECK_LEN(cp, len);
+	return;
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
 /* [OF10] Section 5.3.1 */
-static const u_char *
+static void
 of10_features_reply_print(netdissect_options *ndo,
-                          const u_char *cp, const u_char *ep, const u_int len)
+                          const u_char *cp, u_int len)
 {
 	/* datapath_id */
 	ND_PRINT("\n\t dpid 0x%016" PRIx64, GET_BE_U_8(cp));
-	cp += 8;
+	OF_FWD(8);
 	/* n_buffers */
 	ND_PRINT(", n_buffers %u", GET_BE_U_4(cp));
-	cp += 4;
+	OF_FWD(4);
 	/* n_tables */
 	ND_PRINT(", n_tables %u", GET_U_1(cp));
-	cp += 1;
+	OF_FWD(1);
 	/* pad */
-	cp += 3;
+	OF_FWD(3);
 	/* capabilities */
 	ND_PRINT("\n\t capabilities 0x%08x", GET_BE_U_4(cp));
 	of10_bitmap_print(ndo, ofp_capabilities_bm, GET_BE_U_4(cp), OFPCAP_U);
-	cp += 4;
+	OF_FWD(4);
 	/* actions */
 	ND_PRINT("\n\t actions 0x%08x", GET_BE_U_4(cp));
 	of10_bitmap_print(ndo, ofpat_bm, GET_BE_U_4(cp), OFPAT_U);
-	cp += 4;
+	OF_FWD(4);
 	/* ports */
-	return of10_phy_ports_print(ndo, cp, ep, len - OF_FEATURES_REPLY_MINLEN);
+	of10_phy_ports_print(ndo, cp, len);
 }
 
 /* [OF10] Section 5.3.3 */
-static const u_char *
+static void
 of10_flow_mod_print(netdissect_options *ndo,
-                    const u_char *cp, const u_char *ep, const u_int len)
+                    const u_char *cp, u_int len)
 {
 	uint16_t command;
 
 	/* match */
-	if (ep == (cp = of10_match_print(ndo, "\n\t ", cp, ep)))
-		return ep; /* end of snapshot */
+	of10_match_print(ndo, "\n\t ", cp);
+	OF_FWD(OF_MATCH_FIXLEN);
 	/* cookie */
 	ND_PRINT("\n\t cookie 0x%016" PRIx64, GET_BE_U_8(cp));
-	cp += 8;
+	OF_FWD(8);
 	/* command */
 	command = GET_BE_U_2(cp);
 	ND_PRINT(", command %s", tok2str(ofpfc_str, "invalid (0x%04x)", command));
-	cp += 2;
+	OF_FWD(2);
 	/* idle_timeout */
 	if (GET_BE_U_2(cp))
 		ND_PRINT(", idle_timeout %u", GET_BE_U_2(cp));
-	cp += 2;
+	OF_FWD(2);
 	/* hard_timeout */
 	if (GET_BE_U_2(cp))
 		ND_PRINT(", hard_timeout %u", GET_BE_U_2(cp));
-	cp += 2;
+	OF_FWD(2);
 	/* priority */
 	if (GET_BE_U_2(cp))
 		ND_PRINT(", priority %u", GET_BE_U_2(cp));
-	cp += 2;
+	OF_FWD(2);
 	/* buffer_id */
 	if (command == OFPFC_ADD || command == OFPFC_MODIFY ||
 	    command == OFPFC_MODIFY_STRICT)
 		ND_PRINT(", buffer_id %s",
 		         tok2str(bufferid_str, "0x%08x", GET_BE_U_4(cp)));
-	cp += 4;
+	OF_FWD(4);
 	/* out_port */
 	if (command == OFPFC_DELETE || command == OFPFC_DELETE_STRICT)
 		ND_PRINT(", out_port %s",
 		         tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
-	cp += 2;
+	OF_FWD(2);
 	/* flags */
 	ND_PRINT(", flags 0x%04x", GET_BE_U_2(cp));
 	of10_bitmap_print(ndo, ofpff_bm, GET_BE_U_2(cp), OFPFF_U);
-	cp += 2;
+	OF_FWD(2);
 	/* actions */
-	return of10_actions_print(ndo, "\n\t ", cp, ep, len - OF_FLOW_MOD_MINLEN);
+	of10_actions_print(ndo, "\n\t ", cp, len);
 }
 
 /* ibid */
-static const u_char *
+static void
 of10_port_mod_print(netdissect_options *ndo,
-                    const u_char *cp, const u_char *ep)
+                    const u_char *cp)
 {
 	/* port_no */
 	ND_PRINT("\n\t port_no %s", tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
@@ -1684,98 +1621,94 @@
 	/* pad */
 	/* Always the last field, check bounds. */
 	ND_TCHECK_4(cp);
-	return cp + 4;
+	return;
 
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
 /* [OF10] Section 5.3.5 */
-static const u_char *
+static void
 of10_stats_request_print(netdissect_options *ndo,
-                         const u_char *cp, const u_char *ep, u_int len)
+                         const u_char *cp, u_int len)
 {
-	const u_char *cp0 = cp;
-	const u_int len0 = len;
 	uint16_t type;
 
 	/* type */
 	type = GET_BE_U_2(cp);
-	cp += 2;
+	OF_FWD(2);
 	ND_PRINT("\n\t type %s", tok2str(ofpst_str, "invalid (0x%04x)", type));
 	/* flags */
 	ND_PRINT(", flags 0x%04x", GET_BE_U_2(cp));
 	if (GET_BE_U_2(cp))
 		ND_PRINT(" (bogus)");
-	cp += 2;
+	OF_FWD(2);
 	/* type-specific body of one of fixed lengths */
-	len -= OF_STATS_REQUEST_MINLEN;
 	switch(type) {
 	case OFPST_DESC:
 	case OFPST_TABLE:
 		if (len)
 			goto invalid;
-		return cp;
+		return;
 	case OFPST_FLOW:
 	case OFPST_AGGREGATE:
 		if (len != OF_FLOW_STATS_REQUEST_FIXLEN)
 			goto invalid;
 		/* match */
-		if (ep == (cp = of10_match_print(ndo, "\n\t ", cp, ep)))
-			return ep; /* end of snapshot */
+		of10_match_print(ndo, "\n\t ", cp);
+		OF_FWD(OF_MATCH_FIXLEN);
 		/* table_id */
 		ND_PRINT("\n\t table_id %s",
 			 tok2str(tableid_str, "%u", GET_U_1(cp)));
-		cp += 1;
+		OF_FWD(1);
 		/* pad */
-		cp += 1;
+		OF_FWD(1);
 		/* out_port */
 		ND_PRINT(", out_port %s",
 			 tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
-		return cp + 2;
+		return;
 	case OFPST_PORT:
 		if (len != OF_PORT_STATS_REQUEST_FIXLEN)
 			goto invalid;
 		/* port_no */
 		ND_PRINT("\n\t port_no %s",
 			 tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
-		cp += 2;
+		OF_FWD(2);
 		/* pad */
 		/* Always the last field, check bounds. */
-		ND_TCHECK_6(cp);
-		return cp + 6;
+		OF_CHK_FWD(6);
+		return;
 	case OFPST_QUEUE:
 		if (len != OF_QUEUE_STATS_REQUEST_FIXLEN)
 			goto invalid;
 		/* port_no */
 		ND_PRINT("\n\t port_no %s",
 			 tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
-		cp += 2;
+		OF_FWD(2);
 		/* pad */
-		cp += 2;
+		OF_FWD(2);
 		/* queue_id */
 		ND_PRINT(", queue_id %s",
 			 tok2str(ofpq_str, "%u", GET_BE_U_4(cp)));
-		return cp + 4;
+		return;
 	case OFPST_VENDOR:
-		return of10_vendor_data_print(ndo, cp, ep, len);
+		of10_vendor_data_print(ndo, cp, len);
+		return;
 	}
-	return cp;
+	return;
 
 invalid: /* skip the message body */
 	nd_print_invalid(ndo);
-	ND_TCHECK_LEN(cp0, len0);
-	return cp0 + len0;
+	ND_TCHECK_LEN(cp, len);
+	return;
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
 /* ibid */
-static const u_char *
+static void
 of10_desc_stats_reply_print(netdissect_options *ndo,
-                            const u_char *cp, const u_char *ep, const u_int len)
+                            const u_char *cp, u_int len)
 {
 	if (len != OF_DESC_STATS_REPLY_FIXLEN)
 		goto invalid;
@@ -1783,45 +1716,41 @@
 	ND_PRINT("\n\t  mfr_desc '");
 	(void)nd_print(ndo, cp, cp + DESC_STR_LEN);
 	ND_PRINT("'");
-	cp += DESC_STR_LEN;
+	OF_FWD(DESC_STR_LEN);
 	/* hw_desc */
 	ND_PRINT("\n\t  hw_desc '");
 	(void)nd_print(ndo, cp, cp + DESC_STR_LEN);
 	ND_PRINT("'");
-	cp += DESC_STR_LEN;
+	OF_FWD(DESC_STR_LEN);
 	/* sw_desc */
 	ND_PRINT("\n\t  sw_desc '");
 	(void)nd_print(ndo, cp, cp + DESC_STR_LEN);
 	ND_PRINT("'");
-	cp += DESC_STR_LEN;
+	OF_FWD(DESC_STR_LEN);
 	/* serial_num */
 	ND_PRINT("\n\t  serial_num '");
 	(void)nd_print(ndo, cp, cp + SERIAL_NUM_LEN);
 	ND_PRINT("'");
-	cp += SERIAL_NUM_LEN;
+	OF_FWD(SERIAL_NUM_LEN);
 	/* dp_desc */
 	ND_PRINT("\n\t  dp_desc '");
 	(void)nd_print(ndo, cp, cp + DESC_STR_LEN);
 	ND_PRINT("'");
-	return cp + DESC_STR_LEN;
+	return;
 
 invalid: /* skip the message body */
 	nd_print_invalid(ndo);
 	ND_TCHECK_LEN(cp, len);
-	return cp + len;
+	return;
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
 /* ibid */
-static const u_char *
+static void
 of10_flow_stats_reply_print(netdissect_options *ndo,
-                            const u_char *cp, const u_char *ep, u_int len)
+                            const u_char *cp, u_int len)
 {
-	const u_char *cp0 = cp;
-	const u_int len0 = len;
-
 	while (len) {
 		uint16_t entry_len;
 
@@ -1832,276 +1761,251 @@
 		ND_PRINT("\n\t length %u", entry_len);
 		if (entry_len < OF_FLOW_STATS_REPLY_MINLEN || entry_len > len)
 			goto invalid;
-		cp += 2;
+		OF_FWD(2);
 		/* table_id */
 		ND_PRINT(", table_id %s",
 			 tok2str(tableid_str, "%u", GET_U_1(cp)));
-		cp += 1;
+		OF_FWD(1);
 		/* pad */
-		cp += 1;
+		OF_FWD(1);
 		/* match */
-		if (ep == (cp = of10_match_print(ndo, "\n\t  ", cp, ep)))
-			return ep; /* end of snapshot */
+		of10_match_print(ndo, "\n\t  ", cp);
+		OF_FWD(OF_MATCH_FIXLEN);
 		/* duration_sec */
 		ND_PRINT("\n\t  duration_sec %u", GET_BE_U_4(cp));
-		cp += 4;
+		OF_FWD(4);
 		/* duration_nsec */
 		ND_PRINT(", duration_nsec %u", GET_BE_U_4(cp));
-		cp += 4;
+		OF_FWD(4);
 		/* priority */
 		ND_PRINT(", priority %u", GET_BE_U_2(cp));
-		cp += 2;
+		OF_FWD(2);
 		/* idle_timeout */
 		ND_PRINT(", idle_timeout %u", GET_BE_U_2(cp));
-		cp += 2;
+		OF_FWD(2);
 		/* hard_timeout */
 		ND_PRINT(", hard_timeout %u", GET_BE_U_2(cp));
-		cp += 2;
+		OF_FWD(2);
 		/* pad2 */
-		cp += 6;
+		OF_FWD(6);
 		/* cookie */
 		ND_PRINT(", cookie 0x%016" PRIx64, GET_BE_U_8(cp));
-		cp += 8;
+		OF_FWD(8);
 		/* packet_count */
 		ND_PRINT(", packet_count %" PRIu64, GET_BE_U_8(cp));
-		cp += 8;
+		OF_FWD(8);
 		/* byte_count */
 		ND_PRINT(", byte_count %" PRIu64, GET_BE_U_8(cp));
-		cp += 8;
+		OF_FWD(8);
 		/* actions */
-		if (ep == (cp = of10_actions_print(ndo, "\n\t  ", cp, ep, entry_len - OF_FLOW_STATS_REPLY_MINLEN)))
-			return ep; /* end of snapshot */
-
-		len -= entry_len;
+		of10_actions_print(ndo, "\n\t  ", cp, entry_len - OF_FLOW_STATS_REPLY_MINLEN);
+		OF_FWD(entry_len - OF_FLOW_STATS_REPLY_MINLEN);
 	} /* while */
-	return cp;
+	return;
 
 invalid: /* skip the rest of flow statistics entries */
 	nd_print_invalid(ndo);
-	ND_TCHECK_LEN(cp0, len0);
-	return cp0 + len0;
+	ND_TCHECK_LEN(cp, len);
+	return;
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
 /* ibid */
-static const u_char *
+static void
 of10_aggregate_stats_reply_print(netdissect_options *ndo,
-                                 const u_char *cp, const u_char *ep,
-                                 const u_int len)
+                                 const u_char *cp, u_int len)
 {
 	if (len != OF_AGGREGATE_STATS_REPLY_FIXLEN)
 		goto invalid;
 	/* packet_count */
 	ND_PRINT("\n\t packet_count %" PRIu64, GET_BE_U_8(cp));
-	cp += 8;
+	OF_FWD(8);
 	/* byte_count */
 	ND_PRINT(", byte_count %" PRIu64, GET_BE_U_8(cp));
-	cp += 8;
+	OF_FWD(8);
 	/* flow_count */
 	ND_PRINT(", flow_count %u", GET_BE_U_4(cp));
-	cp += 4;
+	OF_FWD(4);
 	/* pad */
 	/* Always the last field, check bounds. */
 	ND_TCHECK_4(cp);
-	return cp + 4;
+	return;
 
 invalid: /* skip the message body */
 	nd_print_invalid(ndo);
 	ND_TCHECK_LEN(cp, len);
-	return cp + len;
+	return;
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
 /* ibid */
-static const u_char *
+static void
 of10_table_stats_reply_print(netdissect_options *ndo,
-                             const u_char *cp, const u_char *ep, u_int len)
+                             const u_char *cp, u_int len)
 {
-	const u_char *cp0 = cp;
-	const u_int len0 = len;
-
 	while (len) {
 		if (len < OF_TABLE_STATS_REPLY_FIXLEN)
 			goto invalid;
 		/* table_id */
 		ND_PRINT("\n\t table_id %s",
 		         tok2str(tableid_str, "%u", GET_U_1(cp)));
-		cp += 1;
+		OF_FWD(1);
 		/* pad */
-		cp += 3;
+		OF_FWD(3);
 		/* name */
 		ND_PRINT(", name '");
 		(void)nd_print(ndo, cp, cp + OFP_MAX_TABLE_NAME_LEN);
 		ND_PRINT("'");
-		cp += OFP_MAX_TABLE_NAME_LEN;
+		OF_FWD(OFP_MAX_TABLE_NAME_LEN);
 		/* wildcards */
 		ND_PRINT("\n\t  wildcards 0x%08x", GET_BE_U_4(cp));
 		of10_bitmap_print(ndo, ofpfw_bm, GET_BE_U_4(cp), OFPFW_U);
-		cp += 4;
+		OF_FWD(4);
 		/* max_entries */
 		ND_PRINT("\n\t  max_entries %u", GET_BE_U_4(cp));
-		cp += 4;
+		OF_FWD(4);
 		/* active_count */
 		ND_PRINT(", active_count %u", GET_BE_U_4(cp));
-		cp += 4;
+		OF_FWD(4);
 		/* lookup_count */
 		ND_PRINT(", lookup_count %" PRIu64, GET_BE_U_8(cp));
-		cp += 8;
+		OF_FWD(8);
 		/* matched_count */
 		ND_PRINT(", matched_count %" PRIu64, GET_BE_U_8(cp));
-		cp += 8;
-
-		len -= OF_TABLE_STATS_REPLY_FIXLEN;
+		OF_FWD(8);
 	} /* while */
-	return cp;
+	return;
 
 invalid: /* skip the undersized trailing data */
 	nd_print_invalid(ndo);
-	ND_TCHECK_LEN(cp0, len0);
-	return cp0 + len0;
+	ND_TCHECK_LEN(cp, len);
+	return;
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
 /* ibid */
-static const u_char *
+static void
 of10_port_stats_reply_print(netdissect_options *ndo,
-                            const u_char *cp, const u_char *ep, u_int len)
+                            const u_char *cp, u_int len)
 {
-	const u_char *cp0 = cp;
-	const u_int len0 = len;
-
 	while (len) {
 		if (len < OF_PORT_STATS_REPLY_FIXLEN)
 			goto invalid;
 		/* port_no */
 		ND_PRINT("\n\t  port_no %s",
 			 tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
-		cp += 2;
+		OF_FWD(2);
 		if (ndo->ndo_vflag < 2) {
-			ND_TCHECK_LEN(cp, OF_PORT_STATS_REPLY_FIXLEN - 2);
-			cp += OF_PORT_STATS_REPLY_FIXLEN - 2;
-			goto next_port;
+			OF_CHK_FWD(OF_PORT_STATS_REPLY_FIXLEN - 2);
+			continue;
 		}
 		/* pad */
-		cp += 6;
+		OF_FWD(6);
 		/* rx_packets */
 		ND_PRINT(", rx_packets %" PRIu64, GET_BE_U_8(cp));
-		cp += 8;
+		OF_FWD(8);
 		/* tx_packets */
 		ND_PRINT(", tx_packets %" PRIu64, GET_BE_U_8(cp));
-		cp += 8;
+		OF_FWD(8);
 		/* rx_bytes */
 		ND_PRINT(", rx_bytes %" PRIu64, GET_BE_U_8(cp));
-		cp += 8;
+		OF_FWD(8);
 		/* tx_bytes */
 		ND_PRINT(", tx_bytes %" PRIu64, GET_BE_U_8(cp));
-		cp += 8;
+		OF_FWD(8);
 		/* rx_dropped */
 		ND_PRINT(", rx_dropped %" PRIu64, GET_BE_U_8(cp));
-		cp += 8;
+		OF_FWD(8);
 		/* tx_dropped */
 		ND_PRINT(", tx_dropped %" PRIu64, GET_BE_U_8(cp));
-		cp += 8;
+		OF_FWD(8);
 		/* rx_errors */
 		ND_PRINT(", rx_errors %" PRIu64, GET_BE_U_8(cp));
-		cp += 8;
+		OF_FWD(8);
 		/* tx_errors */
 		ND_PRINT(", tx_errors %" PRIu64, GET_BE_U_8(cp));
-		cp += 8;
+		OF_FWD(8);
 		/* rx_frame_err */
 		ND_PRINT(", rx_frame_err %" PRIu64, GET_BE_U_8(cp));
-		cp += 8;
+		OF_FWD(8);
 		/* rx_over_err */
 		ND_PRINT(", rx_over_err %" PRIu64, GET_BE_U_8(cp));
-		cp += 8;
+		OF_FWD(8);
 		/* rx_crc_err */
 		ND_PRINT(", rx_crc_err %" PRIu64, GET_BE_U_8(cp));
-		cp += 8;
+		OF_FWD(8);
 		/* collisions */
 		ND_PRINT(", collisions %" PRIu64, GET_BE_U_8(cp));
-		cp += 8;
-next_port:
-		len -= OF_PORT_STATS_REPLY_FIXLEN;
+		OF_FWD(8);
 	} /* while */
-	return cp;
+	return;
 
 invalid: /* skip the undersized trailing data */
 	nd_print_invalid(ndo);
-	ND_TCHECK_LEN(cp0, len0);
-	return cp0 + len0;
+	ND_TCHECK_LEN(cp, len);
+	return;
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
 /* ibid */
-static const u_char *
+static void
 of10_queue_stats_reply_print(netdissect_options *ndo,
-                             const u_char *cp, const u_char *ep, u_int len)
+                             const u_char *cp, u_int len)
 {
-	const u_char *cp0 = cp;
-	const u_int len0 = len;
-
 	while (len) {
 		if (len < OF_QUEUE_STATS_REPLY_FIXLEN)
 			goto invalid;
 		/* port_no */
 		ND_PRINT("\n\t  port_no %s",
 		         tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
-		cp += 2;
+		OF_FWD(2);
 		/* pad */
-		cp += 2;
+		OF_FWD(2);
 		/* queue_id */
 		ND_PRINT(", queue_id %u", GET_BE_U_4(cp));
-		cp += 4;
+		OF_FWD(4);
 		/* tx_bytes */
 		ND_PRINT(", tx_bytes %" PRIu64, GET_BE_U_8(cp));
-		cp += 8;
+		OF_FWD(8);
 		/* tx_packets */
 		ND_PRINT(", tx_packets %" PRIu64, GET_BE_U_8(cp));
-		cp += 8;
+		OF_FWD(8);
 		/* tx_errors */
 		ND_PRINT(", tx_errors %" PRIu64, GET_BE_U_8(cp));
-		cp += 8;
-
-		len -= OF_QUEUE_STATS_REPLY_FIXLEN;
+		OF_FWD(8);
 	} /* while */
-	return cp;
+	return;
 
 invalid: /* skip the undersized trailing data */
 	nd_print_invalid(ndo);
-	ND_TCHECK_LEN(cp0, len0);
-	return cp0 + len0;
+	ND_TCHECK_LEN(cp, len);
+	return;
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
 /* ibid */
-static const u_char *
+static void
 of10_stats_reply_print(netdissect_options *ndo,
-                       const u_char *cp, const u_char *ep, const u_int len)
+                       const u_char *cp, u_int len)
 {
-	const u_char *cp0 = cp;
 	uint16_t type;
 
 	/* type */
 	type = GET_BE_U_2(cp);
 	ND_PRINT("\n\t type %s", tok2str(ofpst_str, "invalid (0x%04x)", type));
-	cp += 2;
+	OF_FWD(2);
 	/* flags */
 	ND_PRINT(", flags 0x%04x", GET_BE_U_2(cp));
 	of10_bitmap_print(ndo, ofpsf_reply_bm, GET_BE_U_2(cp), OFPSF_REPLY_U);
-	cp += 2;
+	OF_FWD(2);
 
 	if (ndo->ndo_vflag > 0) {
-		const u_char *(*decoder)(netdissect_options *, const u_char *, const u_char *, u_int) =
+		void (*decoder)(netdissect_options *, const u_char *, u_int) =
 			type == OFPST_DESC      ? of10_desc_stats_reply_print      :
 			type == OFPST_FLOW      ? of10_flow_stats_reply_print      :
 			type == OFPST_AGGREGATE ? of10_aggregate_stats_reply_print :
@@ -2110,92 +2014,89 @@
 			type == OFPST_QUEUE     ? of10_queue_stats_reply_print     :
 			type == OFPST_VENDOR    ? of10_vendor_data_print           :
 			NULL;
-		if (decoder != NULL)
-			return decoder(ndo, cp, ep, len - OF_STATS_REPLY_MINLEN);
+		if (decoder != NULL) {
+			decoder(ndo, cp, len);
+			return;
+		}
 	}
-	ND_TCHECK_LEN(cp0, len);
-	return cp0 + len;
+	ND_TCHECK_LEN(cp, len);
+	return;
 
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
 /* [OF10] Section 5.3.6 */
-static const u_char *
+static void
 of10_packet_out_print(netdissect_options *ndo,
-                      const u_char *cp, const u_char *ep, const u_int len)
+                      const u_char *cp, u_int len)
 {
-	const u_char *cp0 = cp;
-	const u_int len0 = len;
 	uint16_t actions_len;
 
 	/* buffer_id */
 	ND_PRINT("\n\t buffer_id 0x%08x", GET_BE_U_4(cp));
-	cp += 4;
+	OF_FWD(4);
 	/* in_port */
 	ND_PRINT(", in_port %s", tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
-	cp += 2;
+	OF_FWD(2);
 	/* actions_len */
 	actions_len = GET_BE_U_2(cp);
-	cp += 2;
-	if (actions_len > len - OF_PACKET_OUT_MINLEN)
+	OF_FWD(2);
+	if (actions_len > len)
 		goto invalid;
 	/* actions */
-	if (ep == (cp = of10_actions_print(ndo, "\n\t ", cp, ep, actions_len)))
-		return ep; /* end of snapshot */
+	of10_actions_print(ndo, "\n\t ", cp, actions_len);
+	OF_FWD(actions_len);
 	/* data */
-	return of10_packet_data_print(ndo, cp, ep, len - OF_PACKET_OUT_MINLEN - actions_len);
+	of10_packet_data_print(ndo, cp, len);
+	return;
 
 invalid: /* skip the rest of the message body */
 	nd_print_invalid(ndo);
-	ND_TCHECK_LEN(cp0, len0);
-	return cp0 + len0;
+	ND_TCHECK_LEN(cp, len);
+	return;
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
 /* [OF10] Section 5.4.1 */
-static const u_char *
+static void
 of10_packet_in_print(netdissect_options *ndo,
-                     const u_char *cp, const u_char *ep, const u_int len)
+                     const u_char *cp, u_int len)
 {
 	/* buffer_id */
 	ND_PRINT("\n\t buffer_id %s",
 	         tok2str(bufferid_str, "0x%08x", GET_BE_U_4(cp)));
-	cp += 4;
+	OF_FWD(4);
 	/* total_len */
 	ND_PRINT(", total_len %u", GET_BE_U_2(cp));
-	cp += 2;
+	OF_FWD(2);
 	/* in_port */
 	ND_PRINT(", in_port %s", tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
-	cp += 2;
+	OF_FWD(2);
 	/* reason */
 	ND_PRINT(", reason %s",
 		 tok2str(ofpr_str, "invalid (0x%02x)", GET_U_1(cp)));
-	cp += 1;
+	OF_FWD(1);
 	/* pad */
 	/* Sometimes the last field, check bounds. */
-	ND_TCHECK_1(cp);
-	cp += 1;
+	OF_CHK_FWD(1);
 	/* data */
-	/* 2 mock octets count in OF_PACKET_IN_MINLEN but not in len */
-	return of10_packet_data_print(ndo, cp, ep, len - (OF_PACKET_IN_MINLEN - 2));
+	of10_packet_data_print(ndo, cp, len);
+	return;
 
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
 
 /* [OF10] Section 5.4.2 */
-static const u_char *
+static void
 of10_flow_removed_print(netdissect_options *ndo,
-                        const u_char *cp, const u_char *ep)
+                        const u_char *cp)
 {
 	/* match */
-	if (ep == (cp = of10_match_print(ndo, "\n\t ", cp, ep)))
-		return ep; /* end of snapshot */
+	of10_match_print(ndo, "\n\t ", cp);
+	cp += OF_MATCH_FIXLEN;
 	/* cookie */
 	ND_PRINT("\n\t cookie 0x%016" PRIx64, GET_BE_U_8(cp));
 	cp += 8;
@@ -2226,20 +2127,19 @@
 	cp += 8;
 	/* byte_count */
 	ND_PRINT(", byte_count %" PRIu64, GET_BE_U_8(cp));
-	return cp + 8;
 }
 
 /* [OF10] Section 5.4.4 */
-static const u_char *
+static void
 of10_error_print(netdissect_options *ndo,
-                 const u_char *cp, const u_char *ep, const u_int len)
+                 const u_char *cp, u_int len)
 {
 	uint16_t type;
 	const struct tok *code_str;
 
 	/* type */
 	type = GET_BE_U_2(cp);
-	cp += 2;
+	OF_FWD(2);
 	ND_PRINT("\n\t type %s", tok2str(ofpet_str, "invalid (0x%04x)", type));
 	/* code */
 	code_str =
@@ -2252,42 +2152,49 @@
 		empty_str;
 	ND_PRINT(", code %s",
 	         tok2str(code_str, "invalid (0x%04x)", GET_BE_U_2(cp)));
-	cp += 2;
+	OF_FWD(2);
 	/* data */
-	return of10_data_print(ndo, cp, ep, len - OF_ERROR_MSG_MINLEN);
+	of10_data_print(ndo, cp, len);
 }
 
-const u_char *
+void
 of10_header_body_print(netdissect_options *ndo,
-                       const u_char *cp, const u_char *ep, const uint8_t type,
-                       const uint16_t len, const uint32_t xid)
+                       const u_char *cp, const uint8_t type,
+                       uint16_t len, const uint32_t xid)
 {
-	const u_char *cp0 = cp;
-	const u_int len0 = len;
-	/* Thus far message length is not less than the basic header size, but most
-	 * message types have additional assorted constraints on the length. Wherever
-	 * possible, check that message length meets the constraint, in remaining
-	 * cases check that the length is OK to begin decoding and leave any final
-	 * verification up to a lower-layer function. When the current message is
-	 * invalid, proceed to the next message. */
+	/*
+	 * Here "cp" and "len" stand for the message part beyond the common
+	 * OpenFlow 1.0 header, if any. Add OF_HEADER_FIXLEN when printing the
+	 * message header. Subtract OF_HEADER_FIXLEN from the type-specific
+	 * lengths, which include the header length, when (and only when)
+	 * validating the length in this function, which is the only one in
+	 * this file that needs to know about OF_HEADER_FIXLEN.
+	 *
+	 * Most message types are longer than just the header, and the length
+	 * constraints may be complex. When possible, validate the constraint
+	 * completely here, otherwise check that the message is long enough to
+	 * begin the decoding and let the lower-layer function do any remaining
+	 * validation.
+	 */
 
 	/* [OF10] Section 5.1 */
 	ND_PRINT("\n\tversion 1.0, type %s, length %u, xid 0x%08x",
-	         tok2str(ofpt_str, "invalid (0x%02x)", type), len, xid);
+	         tok2str(ofpt_str, "invalid (0x%02x)", type),
+	         OF_HEADER_FIXLEN + len, xid);
 	switch (type) {
 	/* OpenFlow header only. */
 	case OFPT_FEATURES_REQUEST: /* [OF10] Section 5.3.1 */
 	case OFPT_GET_CONFIG_REQUEST: /* [OF10] Section 5.3.2 */
 	case OFPT_BARRIER_REQUEST: /* [OF10] Section 5.3.7 */
 	case OFPT_BARRIER_REPLY: /* ibid */
-		if (len != OF_HEADER_FIXLEN)
+		if (len)
 			goto invalid;
 		break;
 
 	/* OpenFlow header and fixed-size message body. */
 	case OFPT_SET_CONFIG: /* [OF10] Section 5.3.2 */
 	case OFPT_GET_CONFIG_REPLY: /* ibid */
-		if (len != OF_SWITCH_CONFIG_FIXLEN)
+		if (len != OF_SWITCH_CONFIG_FIXLEN - OF_HEADER_FIXLEN)
 			goto invalid;
 		if (ndo->ndo_vflag < 1)
 			goto next_message;
@@ -2295,57 +2202,61 @@
 		ND_PRINT("\n\t flags %s",
 		         tok2str(ofp_config_str, "invalid (0x%04x)",
 		         GET_BE_U_2(cp)));
-		cp += 2;
+		OF_FWD(2);
 		/* miss_send_len */
 		ND_PRINT(", miss_send_len %u", GET_BE_U_2(cp));
-		return cp + 2;
+		return;
 	case OFPT_PORT_MOD:
-		if (len != OF_PORT_MOD_FIXLEN)
+		if (len != OF_PORT_MOD_FIXLEN - OF_HEADER_FIXLEN)
 			goto invalid;
 		if (ndo->ndo_vflag < 1)
 			goto next_message;
-		return of10_port_mod_print(ndo, cp, ep);
+		of10_port_mod_print(ndo, cp);
+		return;
 	case OFPT_QUEUE_GET_CONFIG_REQUEST: /* [OF10] Section 5.3.4 */
-		if (len != OF_QUEUE_GET_CONFIG_REQUEST_FIXLEN)
+		if (len != OF_QUEUE_GET_CONFIG_REQUEST_FIXLEN - OF_HEADER_FIXLEN)
 			goto invalid;
 		if (ndo->ndo_vflag < 1)
 			goto next_message;
 		/* port */
 		ND_PRINT("\n\t port_no %s",
 		         tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
-		cp += 2;
+		OF_FWD(2);
 		/* pad */
 		/* Always the last field, check bounds. */
 		ND_TCHECK_2(cp);
-		return cp + 2;
+		return;
 	case OFPT_FLOW_REMOVED:
-		if (len != OF_FLOW_REMOVED_FIXLEN)
+		if (len != OF_FLOW_REMOVED_FIXLEN - OF_HEADER_FIXLEN)
 			goto invalid;
 		if (ndo->ndo_vflag < 1)
 			goto next_message;
-		return of10_flow_removed_print(ndo, cp, ep);
+		of10_flow_removed_print(ndo, cp);
+		return;
 	case OFPT_PORT_STATUS: /* [OF10] Section 5.4.3 */
-		if (len != OF_PORT_STATUS_FIXLEN)
+		if (len != OF_PORT_STATUS_FIXLEN - OF_HEADER_FIXLEN)
 			goto invalid;
 		if (ndo->ndo_vflag < 1)
 			goto next_message;
 		/* reason */
 		ND_PRINT("\n\t reason %s",
 		         tok2str(ofppr_str, "invalid (0x%02x)", GET_U_1(cp)));
-		cp += 1;
+		OF_FWD(1);
 		/* pad */
 		/* No need to check bounds, more data follows. */
-		cp += 7;
+		OF_FWD(7);
 		/* desc */
-		return of10_phy_ports_print(ndo, cp, ep, OF_PHY_PORT_FIXLEN);
+		of10_phy_ports_print(ndo, cp, len);
+		return;
 
 	/* OpenFlow header, fixed-size message body and n * fixed-size data units. */
 	case OFPT_FEATURES_REPLY:
-		if (len < OF_FEATURES_REPLY_MINLEN)
+		if (len < OF_FEATURES_REPLY_MINLEN - OF_HEADER_FIXLEN)
 			goto invalid;
 		if (ndo->ndo_vflag < 1)
 			goto next_message;
-		return of10_features_reply_print(ndo, cp, ep, len);
+		of10_features_reply_print(ndo, cp, len);
+		return;
 
 	/* OpenFlow header and variable-size data. */
 	case OFPT_HELLO: /* [OF10] Section 5.5.1 */
@@ -2353,92 +2264,99 @@
 	case OFPT_ECHO_REPLY: /* [OF10] Section 5.5.3 */
 		if (ndo->ndo_vflag < 1)
 			goto next_message;
-		return of10_data_print(ndo, cp, ep, len - OF_HEADER_FIXLEN);
+		of10_data_print(ndo, cp, len);
+		return;
 
 	/* OpenFlow header, fixed-size message body and variable-size data. */
 	case OFPT_ERROR:
-		if (len < OF_ERROR_MSG_MINLEN)
+		if (len < OF_ERROR_MSG_MINLEN - OF_HEADER_FIXLEN)
 			goto invalid;
 		if (ndo->ndo_vflag < 1)
 			goto next_message;
-		return of10_error_print(ndo, cp, ep, len);
+		of10_error_print(ndo, cp, len);
+		return;
 	case OFPT_VENDOR:
 		/* [OF10] Section 5.5.4 */
-		if (len < OF_VENDOR_MINLEN)
+		if (len < OF_VENDOR_MINLEN - OF_HEADER_FIXLEN)
 			goto invalid;
 		if (ndo->ndo_vflag < 1)
 			goto next_message;
-		return of10_vendor_message_print(ndo, cp, ep, len - OF_HEADER_FIXLEN);
+		of10_vendor_message_print(ndo, cp, len);
+		return;
 	case OFPT_PACKET_IN:
 		/* 2 mock octets count in OF_PACKET_IN_MINLEN but not in len */
-		if (len < OF_PACKET_IN_MINLEN - 2)
+		if (len < OF_PACKET_IN_MINLEN - 2 - OF_HEADER_FIXLEN)
 			goto invalid;
 		if (ndo->ndo_vflag < 1)
 			goto next_message;
-		return of10_packet_in_print(ndo, cp, ep, len);
+		of10_packet_in_print(ndo, cp, len);
+		return;
 
 	/* a. OpenFlow header. */
 	/* b. OpenFlow header and one of the fixed-size message bodies. */
 	/* c. OpenFlow header, fixed-size message body and variable-size data. */
 	case OFPT_STATS_REQUEST:
-		if (len < OF_STATS_REQUEST_MINLEN)
+		if (len < OF_STATS_REQUEST_MINLEN - OF_HEADER_FIXLEN)
 			goto invalid;
 		if (ndo->ndo_vflag < 1)
 			goto next_message;
-		return of10_stats_request_print(ndo, cp, ep, len);
+		of10_stats_request_print(ndo, cp, len);
+		return;
 
 	/* a. OpenFlow header and fixed-size message body. */
 	/* b. OpenFlow header and n * fixed-size data units. */
 	/* c. OpenFlow header and n * variable-size data units. */
 	/* d. OpenFlow header, fixed-size message body and variable-size data. */
 	case OFPT_STATS_REPLY:
-		if (len < OF_STATS_REPLY_MINLEN)
+		if (len < OF_STATS_REPLY_MINLEN - OF_HEADER_FIXLEN)
 			goto invalid;
 		if (ndo->ndo_vflag < 1)
 			goto next_message;
-		return of10_stats_reply_print(ndo, cp, ep, len);
+		of10_stats_reply_print(ndo, cp, len);
+		return;
 
 	/* OpenFlow header and n * variable-size data units and variable-size data. */
 	case OFPT_PACKET_OUT:
-		if (len < OF_PACKET_OUT_MINLEN)
+		if (len < OF_PACKET_OUT_MINLEN - OF_HEADER_FIXLEN)
 			goto invalid;
 		if (ndo->ndo_vflag < 1)
 			goto next_message;
-		return of10_packet_out_print(ndo, cp, ep, len);
+		of10_packet_out_print(ndo, cp, len);
+		return;
 
 	/* OpenFlow header, fixed-size message body and n * variable-size data units. */
 	case OFPT_FLOW_MOD:
-		if (len < OF_FLOW_MOD_MINLEN)
+		if (len < OF_FLOW_MOD_MINLEN - OF_HEADER_FIXLEN)
 			goto invalid;
 		if (ndo->ndo_vflag < 1)
 			goto next_message;
-		return of10_flow_mod_print(ndo, cp, ep, len);
+		of10_flow_mod_print(ndo, cp, len);
+		return;
 
 	/* OpenFlow header, fixed-size message body and n * variable-size data units. */
 	case OFPT_QUEUE_GET_CONFIG_REPLY: /* [OF10] Section 5.3.4 */
-		if (len < OF_QUEUE_GET_CONFIG_REPLY_MINLEN)
+		if (len < OF_QUEUE_GET_CONFIG_REPLY_MINLEN - OF_HEADER_FIXLEN)
 			goto invalid;
 		if (ndo->ndo_vflag < 1)
 			goto next_message;
 		/* port */
 		ND_PRINT("\n\t port_no %s",
 			 tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
-		cp += 2;
+		OF_FWD(2);
 		/* pad */
 		/* Sometimes the last field, check bounds. */
-		ND_TCHECK_6(cp);
-		cp += 6;
+		OF_CHK_FWD(6);
 		/* queues */
-		return of10_queues_print(ndo, cp, ep, len - OF_QUEUE_GET_CONFIG_REPLY_MINLEN);
+		of10_queues_print(ndo, cp, len);
+		return;
 	} /* switch (type) */
 	goto next_message;
 
 invalid: /* skip the message body */
 	nd_print_invalid(ndo);
 next_message:
-	ND_TCHECK_LEN(cp0, len0 - OF_HEADER_FIXLEN);
-	return cp0 + len0 - OF_HEADER_FIXLEN;
+	ND_TCHECK_LEN(cp, len);
+	return;
 trunc:
-	nd_print_trunc(ndo);
-	return ep;
+	nd_trunc(ndo);
 }
diff --git a/print-openflow.c b/print-openflow.c
index c013ea4..87a68a4 100644
--- a/print-openflow.c
+++ b/print-openflow.c
@@ -73,70 +73,72 @@
 	       version, type, length, xid);
 }
 
-/* Print a single OpenFlow message. */
-static const u_char *
-of_header_body_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
-{
-	uint8_t version, type;
-	uint16_t length;
-	uint32_t xid;
-
-	if (ep < cp + OF_HEADER_FIXLEN)
-		goto invalid;
-	/* version */
-	version = GET_U_1(cp);
-	cp += 1;
-	/* type */
-	type = GET_U_1(cp);
-	cp += 1;
-	/* length */
-	length = GET_BE_U_2(cp);
-	cp += 2;
-	/* xid */
-	xid = GET_BE_U_4(cp);
-	cp += 4;
-	/* Message length includes the header length and a message always includes
-	 * the basic header. A message length underrun fails decoding of the rest of
-	 * the current packet. At the same time, try decoding as much of the current
-	 * message as possible even when it does not end within the current TCP
-	 * segment.
-	 *
-	 * That is, do NOT require the header "length" to be small enough for
-	 * the full declared OpenFlow message to fit into the remainder of the
-	 * declared TCP segment given to this function, same as the full declared
-	 * TCP segment is not required to fit into the captured packet buffer.
-	 */
-	if (length < OF_HEADER_FIXLEN) {
-		of_header_print(ndo, version, type, length, xid);
-		goto invalid;
-	}
-	/* Decode known protocol versions further without printing the header (the
-	 * type decoding is version-specific. */
-	switch (version) {
-	case OF_VER_1_0:
-		return of10_header_body_print(ndo, cp, ep, type, length, xid);
-	default:
-		of_header_print(ndo, version, type, length, xid);
-		ND_TCHECK_LEN(cp, length - OF_HEADER_FIXLEN);
-		return cp + length - OF_HEADER_FIXLEN; /* done with current message */
-	}
-
-invalid: /* fail current packet */
-	nd_print_invalid(ndo);
-	ND_TCHECK_LEN(cp, ep - cp);
-	return ep;
-trunc:
-	nd_print_trunc(ndo);
-	return ep;
-}
-
 /* Print a TCP segment worth of OpenFlow messages presuming the segment begins
  * on a message boundary. */
 void
-openflow_print(netdissect_options *ndo, const u_char *cp, const u_int len _U_)
+openflow_print(netdissect_options *ndo, const u_char *cp, u_int len)
 {
 	ndo->ndo_protocol = "openflow";
 	ND_PRINT(": OpenFlow");
-	while (cp < ndo->ndo_snapend)
-		cp = of_header_body_print(ndo, cp, ndo->ndo_snapend);
+	while (len) {
+		/* Print a single OpenFlow message. */
+		uint8_t version, type;
+		uint16_t length;
+		uint32_t xid;
+
+		if (len < OF_HEADER_FIXLEN)
+			goto invalid;
+		/* version */
+		version = GET_U_1(cp);
+		OF_FWD(1);
+		/* type */
+		type = GET_U_1(cp);
+		OF_FWD(1);
+		/* length */
+		length = GET_BE_U_2(cp);
+		OF_FWD(2);
+		/* xid */
+		xid = GET_BE_U_4(cp);
+		OF_FWD(4);
+		/*
+		 * Message length includes the header length and a message
+		 * always includes the basic header. A message length underrun
+		 * fails decoding of the rest of the current packet. At the
+		 * same time, try decoding as much of the current message as
+		 * possible even when it does not end within the current TCP
+		 * segment.
+		 *
+		 * That is, do NOT require the header "length" to be small
+		 * enough for the full declared OpenFlow message to fit into
+		 * the remainder of the declared TCP segment, same as the full
+		 * declared TCP segment is not required to fit into the
+		 * captured packet buffer.
+		 */
+		if (length < OF_HEADER_FIXLEN) {
+			of_header_print(ndo, version, type, length, xid);
+			goto invalid;
+		}
+		/*
+		 * Decode known protocol versions further without printing the
+		 * header (the type decoding is version-specific).
+		 */
+		switch (version) {
+		case OF_VER_1_0:
+			of10_header_body_print(ndo, cp, type,
+			                       length - OF_HEADER_FIXLEN, xid);
+			break;
+		default:
+			of_header_print(ndo, version, type, length, xid);
+			ND_TCHECK_LEN(cp, length - OF_HEADER_FIXLEN);
+		}
+		OF_FWD(length - OF_HEADER_FIXLEN);
+	} /* while (len) */
+	return;
+
+invalid: /* fail the current packet */
+	nd_print_invalid(ndo);
+	ND_TCHECK_LEN(cp, len);
+	return;
+trunc:
+	nd_trunc(ndo);
 }
diff --git a/tests/of10_7050sx_bsn-oobr.out b/tests/of10_7050sx_bsn-oobr.out
index 93d897a..15507e9 100644
--- a/tests/of10_7050sx_bsn-oobr.out
+++ b/tests/of10_7050sx_bsn-oobr.out
@@ -13,4 +13,4 @@
 	version 1.0, type VENDOR, length 24, xid 0x0000001f, vendor 0x005c16c7 (Big Switch Networks)
 	 subtype GET_IP_MASK_REQUEST, index 3
 	version 1.0, type VENDOR, length 30, xid 0x00000020, vendor 0x005c16c7 (Big Switch Networks)
-	 subtype SHELL_COMMAND, service 0, data 'show cloc' [|openflow]
+	 subtype SHELL_COMMAND, service 0, data 'show cloc [|openflow]