xref: /dragonfly/contrib/tcpdump/print-forces.c (revision d19ef5a2)
1 /*
2  * Redistribution and use in source and binary forms, with or without
3  * modification, are permitted provided that: (1) source code
4  * distributions retain the above copyright notice and this paragraph
5  * in its entirety, and (2) distributions including binary code include
6  * the above copyright notice and this paragraph in its entirety in
7  * the documentation or other materials provided with the distribution.
8  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
9  * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
10  * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
11  * FOR A PARTICULAR PURPOSE.
12  *
13  * Copyright (c) 2009 Mojatatu Networks, Inc
14  *
15  */
16 
17 /* \summary: Forwarding and Control Element Separation (ForCES) Protocol printer */
18 
19 /* specification: RFC 5810 */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <netdissect-stdinc.h>
26 
27 #include "netdissect.h"
28 #include "extract.h"
29 
30 static const char tstr[] = "[|forces]";
31 
32 #define	ForCES_VERS	1
33 #define	ForCES_HDRL	24
34 #define	ForCES_ALNL	4U
35 #define TLV_HDRL	4
36 #define ILV_HDRL	8
37 
38 #define TOM_RSVD 	0x0
39 #define TOM_ASSNSETUP 	0x1
40 #define TOM_ASSNTEARD 	0x2
41 #define TOM_CONFIG 	0x3
42 #define TOM_QUERY 	0x4
43 #define TOM_EVENTNOT 	0x5
44 #define TOM_PKTREDIR 	0x6
45 #define TOM_HEARTBT 	0x0F
46 #define TOM_ASSNSETREP 	0x11
47 #define TOM_CONFIGREP 	0x13
48 #define TOM_QUERYREP 	0x14
49 
50 /*
51  * tom_h Flags: resv1(8b):maxtlvs(4b):resv2(2b):mintlv(2b)
52 */
53 #define ZERO_TTLV	0x01
54 #define ZERO_MORE_TTLV	0x02
55 #define ONE_MORE_TTLV	0x04
56 #define ZERO_TLV	0x00
57 #define ONE_TLV		0x10
58 #define TWO_TLV		0x20
59 #define MAX_TLV		0xF0
60 
61 #define TTLV_T1		(ONE_MORE_TTLV|ONE_TLV)
62 #define TTLV_T2		(ONE_MORE_TTLV|MAX_TLV)
63 
64 struct tom_h {
65 	uint32_t v;
66 	uint16_t flags;
67 	uint16_t op_msk;
68 	const char *s;
69 	int (*print) (netdissect_options *ndo, register const u_char * pptr, register u_int len,
70 		      uint16_t op_msk, int indent);
71 };
72 
73 enum {
74 	TOM_RSV_I,
75 	TOM_ASS_I,
76 	TOM_AST_I,
77 	TOM_CFG_I,
78 	TOM_QRY_I,
79 	TOM_EVN_I,
80 	TOM_RED_I,
81 	TOM_HBT_I,
82 	TOM_ASR_I,
83 	TOM_CNR_I,
84 	TOM_QRR_I,
85 	_TOM_RSV_MAX
86 };
87 #define TOM_MAX_IND (_TOM_RSV_MAX - 1)
88 
89 static inline int tom_valid(uint8_t tom)
90 {
91 	if (tom > 0) {
92 		if (tom >= 0x7 && tom <= 0xe)
93 			return 0;
94 		if (tom == 0x10)
95 			return 0;
96 		if (tom > 0x14)
97 			return 0;
98 		return 1;
99 	} else
100 		return 0;
101 }
102 
103 static inline const char *ForCES_node(uint32_t node)
104 {
105 	if (node <= 0x3FFFFFFF)
106 		return "FE";
107 	if (node >= 0x40000000 && node <= 0x7FFFFFFF)
108 		return "CE";
109 	if (node >= 0xC0000000 && node <= 0xFFFFFFEF)
110 		return "AllMulticast";
111 	if (node == 0xFFFFFFFD)
112 		return "AllCEsBroadcast";
113 	if (node == 0xFFFFFFFE)
114 		return "AllFEsBroadcast";
115 	if (node == 0xFFFFFFFF)
116 		return "AllBroadcast";
117 
118 	return "ForCESreserved";
119 
120 }
121 
122 static const struct tok ForCES_ACKs[] = {
123 	{0x0, "NoACK"},
124 	{0x1, "SuccessACK"},
125 	{0x2, "FailureACK"},
126 	{0x3, "AlwaysACK"},
127 	{0, NULL}
128 };
129 
130 static const struct tok ForCES_EMs[] = {
131 	{0x0, "EMReserved"},
132 	{0x1, "execute-all-or-none"},
133 	{0x2, "execute-until-failure"},
134 	{0x3, "continue-execute-on-failure"},
135 	{0, NULL}
136 };
137 
138 static const struct tok ForCES_ATs[] = {
139 	{0x0, "Standalone"},
140 	{0x1, "2PCtransaction"},
141 	{0, NULL}
142 };
143 
144 static const struct tok ForCES_TPs[] = {
145 	{0x0, "StartofTransaction"},
146 	{0x1, "MiddleofTransaction"},
147 	{0x2, "EndofTransaction"},
148 	{0x3, "abort"},
149 	{0, NULL}
150 };
151 
152 /*
153  * Structure of forces header, naked of TLVs.
154  */
155 struct forcesh {
156 	nd_uint8_t fm_vrsvd;	/* version and reserved */
157 #define ForCES_V(forcesh)	((forcesh)->fm_vrsvd >> 4)
158 	nd_uint8_t fm_tom;	/* type of message */
159 	nd_uint16_t fm_len;	/* total length * 4 bytes */
160 #define ForCES_BLN(forcesh)	((uint32_t)(EXTRACT_16BITS(&(forcesh)->fm_len) << 2))
161 	nd_uint32_t fm_sid;	/* Source ID */
162 #define ForCES_SID(forcesh)	EXTRACT_32BITS(&(forcesh)->fm_sid)
163 	nd_uint32_t fm_did;	/* Destination ID */
164 #define ForCES_DID(forcesh)	EXTRACT_32BITS(&(forcesh)->fm_did)
165 	nd_uint8_t fm_cor[8];	/* correlator */
166 	nd_uint32_t fm_flags;	/* flags */
167 #define ForCES_ACK(forcesh)	((EXTRACT_32BITS(&(forcesh)->fm_flags)&0xC0000000) >> 30)
168 #define ForCES_PRI(forcesh)	((EXTRACT_32BITS(&(forcesh)->fm_flags)&0x38000000) >> 27)
169 #define ForCES_RS1(forcesh)	((EXTRACT_32BITS(&(forcesh)->fm_flags)&0x07000000) >> 24)
170 #define ForCES_EM(forcesh)	((EXTRACT_32BITS(&(forcesh)->fm_flags)&0x00C00000) >> 22)
171 #define ForCES_AT(forcesh)	((EXTRACT_32BITS(&(forcesh)->fm_flags)&0x00200000) >> 21)
172 #define ForCES_TP(forcesh)	((EXTRACT_32BITS(&(forcesh)->fm_flags)&0x00180000) >> 19)
173 #define ForCES_RS2(forcesh)	((EXTRACT_32BITS(&(forcesh)->fm_flags)&0x0007FFFF) >> 0)
174 };
175 
176 #define ForCES_HLN_VALID(fhl,tlen) ((tlen) >= ForCES_HDRL && \
177 				   (fhl) >= ForCES_HDRL && \
178 				   (fhl) == (tlen))
179 
180 #define F_LFB_RSVD 0x0
181 #define F_LFB_FEO 0x1
182 #define F_LFB_FEPO 0x2
183 static const struct tok ForCES_LFBs[] = {
184 	{F_LFB_RSVD, "Invalid TLV"},
185 	{F_LFB_FEO, "FEObj LFB"},
186 	{F_LFB_FEPO, "FEProtoObj LFB"},
187 	{0, NULL}
188 };
189 
190 /* this is defined in RFC5810 section A.2 */
191 /*   http://www.iana.org/assignments/forces/forces.xhtml#oper-tlv-types */
192 enum {
193 	F_OP_RSV        = 0,
194 	F_OP_SET        = 1,
195 	F_OP_SETPROP    = 2,
196 	F_OP_SETRESP    = 3,
197 	F_OP_SETPRESP   = 4,
198 	F_OP_DEL        = 5,
199 	F_OP_DELRESP    = 6,
200 	F_OP_GET        = 7,
201 	F_OP_GETPROP    = 8,
202 	F_OP_GETRESP    = 9,
203 	F_OP_GETPRESP   = 10,
204 	F_OP_REPORT     = 11,
205 	F_OP_COMMIT     = 12,
206 	F_OP_RCOMMIT    = 13,
207 	F_OP_RTRCOMP    = 14,
208 	_F_OP_MAX
209 };
210 #define F_OP_MAX	(_F_OP_MAX - 1)
211 
212 enum {
213 	B_OP_SET = 1 << (F_OP_SET - 1),
214 	B_OP_SETPROP = 1 << (F_OP_SETPROP - 1),
215 	B_OP_SETRESP = 1 << (F_OP_SETRESP - 1),
216 	B_OP_SETPRESP = 1 << (F_OP_SETPRESP - 1),
217 	B_OP_DEL = 1 << (F_OP_DEL - 1),
218 	B_OP_DELRESP = 1 << (F_OP_DELRESP - 1),
219 	B_OP_GET = 1 << (F_OP_GET - 1),
220 	B_OP_GETPROP = 1 << (F_OP_GETPROP - 1),
221 	B_OP_GETRESP = 1 << (F_OP_GETRESP - 1),
222 	B_OP_GETPRESP = 1 << (F_OP_GETPRESP - 1),
223 	B_OP_REPORT = 1 << (F_OP_REPORT - 1),
224 	B_OP_COMMIT = 1 << (F_OP_COMMIT - 1),
225 	B_OP_RCOMMIT = 1 << (F_OP_RCOMMIT - 1),
226 	B_OP_RTRCOMP = 1 << (F_OP_RTRCOMP - 1)
227 };
228 
229 struct optlv_h {
230 	uint16_t flags;
231 	uint16_t op_msk;
232 	const char *s;
233 	int (*print) (netdissect_options *ndo, register const u_char * pptr, register u_int len,
234 		      uint16_t op_msk, int indent);
235 };
236 
237 static int genoptlv_print(netdissect_options *, register const u_char * pptr, register u_int len,
238 			 uint16_t op_msk, int indent);
239 static int recpdoptlv_print(netdissect_options *, register const u_char * pptr, register u_int len,
240 			    uint16_t op_msk, int indent);
241 static int invoptlv_print(netdissect_options *, register const u_char * pptr, register u_int len,
242 			  uint16_t op_msk, int indent);
243 
244 #define OP_MIN_SIZ 8
245 struct pathdata_h {
246 	nd_uint16_t pflags;
247 	nd_uint16_t pIDcnt;
248 };
249 
250 #define	B_FULLD		0x1
251 #define	B_SPARD 	0x2
252 #define B_RESTV		0x4
253 #define B_KEYIN		0x8
254 #define B_APPND		0x10
255 #define B_TRNG		0x20
256 
257 static const struct optlv_h OPTLV_msg[F_OP_MAX + 1] = {
258 	/* F_OP_RSV */ {ZERO_TTLV, 0, "Invalid OPTLV", invoptlv_print},
259 	/* F_OP_SET */ {TTLV_T2, B_FULLD | B_SPARD, " Set", recpdoptlv_print},
260 	/* F_OP_SETPROP */
261 	    {TTLV_T2, B_FULLD | B_SPARD, " SetProp", recpdoptlv_print},
262 	/* F_OP_SETRESP */ {TTLV_T2, B_RESTV, " SetResp", recpdoptlv_print},
263 	/* F_OP_SETPRESP */ {TTLV_T2, B_RESTV, " SetPropResp", recpdoptlv_print},
264 	/* F_OP_DEL */ {ZERO_TTLV, 0, " Del", recpdoptlv_print},
265 	/* F_OP_DELRESP */ {TTLV_T2, B_RESTV, " DelResp", recpdoptlv_print},
266 	/* F_OP_GET */ {ZERO_TTLV, 0, " Get", recpdoptlv_print},
267 	/* F_OP_GETPROP */ {ZERO_TTLV, 0, " GetProp", recpdoptlv_print},
268 	/* F_OP_GETRESP */
269 	    {TTLV_T2, B_FULLD | B_SPARD | B_RESTV, " GetResp", recpdoptlv_print},
270 	/* F_OP_GETPRESP */
271 	    {TTLV_T2, B_FULLD | B_RESTV, " GetPropResp", recpdoptlv_print},
272 	/* F_OP_REPORT */
273 	    {TTLV_T2, B_FULLD | B_SPARD, " Report", recpdoptlv_print},
274 	/* F_OP_COMMIT */ {ZERO_TTLV, 0, " Commit", NULL},
275 	/* F_OP_RCOMMIT */ {TTLV_T1, B_RESTV, " RCommit", genoptlv_print},
276 	/* F_OP_RTRCOMP */ {ZERO_TTLV, 0, " RTRCOMP", NULL},
277 };
278 
279 static inline const struct optlv_h *get_forces_optlv_h(uint16_t opt)
280 {
281 	if (opt > F_OP_MAX || opt <= F_OP_RSV)
282 		return &OPTLV_msg[F_OP_RSV];
283 
284 	return &OPTLV_msg[opt];
285 }
286 
287 #define IND_SIZE 256
288 #define IND_CHR ' '
289 #define IND_PREF '\n'
290 #define IND_SUF 0x0
291 static char ind_buf[IND_SIZE];
292 
293 static inline char *indent_pr(int indent, int nlpref)
294 {
295 	int i = 0;
296 	char *r = ind_buf;
297 
298 	if (indent > (IND_SIZE - 1))
299 		indent = IND_SIZE - 1;
300 
301 	if (nlpref) {
302 		r[i] = IND_PREF;
303 		i++;
304 		indent--;
305 	}
306 
307 	while (--indent >= 0)
308 		r[i++] = IND_CHR;
309 
310 	r[i] = IND_SUF;
311 	return r;
312 }
313 
314 static inline int op_valid(uint16_t op, uint16_t mask)
315 {
316 	int opb = 1 << (op - 1);
317 
318 	if (op == 0)
319 		return 0;
320 	if (opb & mask)
321 		return 1;
322 	/* I guess we should allow vendor operations? */
323 	if (op >= 0x8000)
324 		return 1;
325 	return 0;
326 }
327 
328 #define F_TLV_RSVD	0x0000
329 #define F_TLV_REDR	0x0001
330 #define F_TLV_ASRS	0x0010
331 #define F_TLV_ASRT	0x0011
332 #define F_TLV_LFBS	0x1000
333 #define F_TLV_PDAT	0x0110
334 #define F_TLV_KEYI	0x0111
335 #define F_TLV_FULD	0x0112
336 #define F_TLV_SPAD	0x0113
337 #define F_TLV_REST	0x0114
338 #define F_TLV_METD	0x0115
339 #define F_TLV_REDD	0x0116
340 #define F_TLV_TRNG	0x0117
341 
342 
343 #define F_TLV_VNST	0x8000
344 
345 static const struct tok ForCES_TLV[] = {
346 	{F_TLV_RSVD, "Invalid TLV"},
347 	{F_TLV_REDR, "REDIRECT TLV"},
348 	{F_TLV_ASRS, "ASResult TLV"},
349 	{F_TLV_ASRT, "ASTreason TLV"},
350 	{F_TLV_LFBS, "LFBselect TLV"},
351 	{F_TLV_PDAT, "PATH-DATA TLV"},
352 	{F_TLV_KEYI, "KEYINFO TLV"},
353 	{F_TLV_FULD, "FULLDATA TLV"},
354 	{F_TLV_SPAD, "SPARSEDATA TLV"},
355 	{F_TLV_REST, "RESULT TLV"},
356 	{F_TLV_METD, "METADATA TLV"},
357 	{F_TLV_REDD, "REDIRECTDATA TLV"},
358 	{0, NULL}
359 };
360 
361 #define TLV_HLN	4
362 static inline int ttlv_valid(uint16_t ttlv)
363 {
364 	if (ttlv > 0) {
365 		if (ttlv == 1 || ttlv == 0x1000)
366 			return 1;
367 		if (ttlv >= 0x10 && ttlv <= 0x11)
368 			return 1;
369 		if (ttlv >= 0x110 && ttlv <= 0x116)
370 			return 1;
371 		if (ttlv >= 0x8000)
372 			return 0;	/* XXX: */
373 	}
374 
375 	return 0;
376 }
377 
378 struct forces_ilv {
379 	nd_uint32_t type;
380 	nd_uint32_t length;
381 };
382 
383 struct forces_tlv {
384 	nd_uint16_t type;
385 	nd_uint16_t length;
386 };
387 
388 #define F_ALN_LEN(len) ( ((len)+ForCES_ALNL-1) & ~(ForCES_ALNL-1) )
389 #define	GET_TOP_TLV(fhdr) ((const struct forces_tlv *)((fhdr) + sizeof (struct forcesh)))
390 #define TLV_SET_LEN(len)  (F_ALN_LEN(TLV_HDRL) + (len))
391 #define TLV_ALN_LEN(len)  F_ALN_LEN(TLV_SET_LEN(len))
392 #define TLV_RDAT_LEN(tlv) ((int)(EXTRACT_16BITS(&(tlv)->length) - TLV_SET_LEN(0))
393 #define TLV_DATA(tlvp)   ((const void*)(((const char*)(tlvp)) + TLV_SET_LEN(0)))
394 #define GO_NXT_TLV(tlv,rlen) ((rlen) -= F_ALN_LEN(EXTRACT_16BITS(&(tlv)->length)), \
395 		              (const struct forces_tlv*)(((const char*)(tlv)) \
396 				      + F_ALN_LEN(EXTRACT_16BITS(&(tlv)->length))))
397 #define ILV_SET_LEN(len)  (F_ALN_LEN(ILV_HDRL) + (len))
398 #define ILV_ALN_LEN(len)  F_ALN_LEN(ILV_SET_LEN(len))
399 #define ILV_RDAT_LEN(ilv) ((int)(EXTRACT_32BITS(&(ilv)->length)) - ILV_SET_LEN(0))
400 #define ILV_DATA(ilvp)   ((const void*)(((const char*)(ilvp)) + ILV_SET_LEN(0)))
401 #define GO_NXT_ILV(ilv,rlen) ((rlen) -= F_ALN_LEN(EXTRACT_32BITS(&(ilv)->length)), \
402 		              (const struct forces_ilv *)(((const char*)(ilv)) \
403 				      + F_ALN_LEN(EXTRACT_32BITS(&(ilv)->length))))
404 #define INVALID_RLEN 1
405 #define INVALID_STLN 2
406 #define INVALID_LTLN 3
407 #define INVALID_ALEN 4
408 
409 static const struct tok ForCES_TLV_err[] = {
410 	{INVALID_RLEN, "Invalid total length"},
411 	{INVALID_STLN, "xLV too short"},
412 	{INVALID_LTLN, "xLV too long"},
413 	{INVALID_ALEN, "data padding missing"},
414 	{0, NULL}
415 };
416 
417 static inline u_int tlv_valid(const struct forces_tlv *tlv, u_int rlen)
418 {
419 	if (rlen < TLV_HDRL)
420 		return INVALID_RLEN;
421 	if (EXTRACT_16BITS(&tlv->length) < TLV_HDRL)
422 		return INVALID_STLN;
423 	if (EXTRACT_16BITS(&tlv->length) > rlen)
424 		return INVALID_LTLN;
425 	if (rlen < F_ALN_LEN(EXTRACT_16BITS(&tlv->length)))
426 		return INVALID_ALEN;
427 
428 	return 0;
429 }
430 
431 static inline int ilv_valid(const struct forces_ilv *ilv, u_int rlen)
432 {
433 	if (rlen < ILV_HDRL)
434 		return INVALID_RLEN;
435 	if (EXTRACT_32BITS(&ilv->length) < ILV_HDRL)
436 		return INVALID_STLN;
437 	if (EXTRACT_32BITS(&ilv->length) > rlen)
438 		return INVALID_LTLN;
439 	if (rlen < F_ALN_LEN(EXTRACT_32BITS(&ilv->length)))
440 		return INVALID_ALEN;
441 
442 	return 0;
443 }
444 
445 static int lfbselect_print(netdissect_options *, register const u_char * pptr, register u_int len,
446 			   uint16_t op_msk, int indent);
447 static int redirect_print(netdissect_options *, register const u_char * pptr, register u_int len,
448 			  uint16_t op_msk, int indent);
449 static int asrtlv_print(netdissect_options *, register const u_char * pptr, register u_int len,
450 			uint16_t op_msk, int indent);
451 static int asttlv_print(netdissect_options *, register const u_char * pptr, register u_int len,
452 			uint16_t op_msk, int indent);
453 
454 struct forces_lfbsh {
455 	nd_uint32_t class;
456 	nd_uint32_t instance;
457 };
458 
459 #define ASSNS_OPS (B_OP_REPORT)
460 #define CFG_OPS	(B_OP_SET|B_OP_SETPROP|B_OP_DEL|B_OP_COMMIT|B_OP_RTRCOMP)
461 #define CFG_ROPS (B_OP_SETRESP|B_OP_SETPRESP|B_OP_DELRESP|B_OP_RCOMMIT)
462 #define CFG_QY (B_OP_GET|B_OP_GETPROP)
463 #define CFG_QYR (B_OP_GETRESP|B_OP_GETPRESP)
464 #define CFG_EVN (B_OP_REPORT)
465 
466 static const struct tom_h ForCES_msg[TOM_MAX_IND + 1] = {
467 	/* TOM_RSV_I */ {TOM_RSVD, ZERO_TTLV, 0, "Invalid message", NULL},
468 	/* TOM_ASS_I */ {TOM_ASSNSETUP, ZERO_MORE_TTLV | TWO_TLV, ASSNS_OPS,
469 		       "Association Setup", lfbselect_print},
470 	/* TOM_AST_I */
471 	    {TOM_ASSNTEARD, TTLV_T1, 0, "Association TearDown", asttlv_print},
472 	/* TOM_CFG_I */ {TOM_CONFIG, TTLV_T2, CFG_OPS, "Config", lfbselect_print},
473 	/* TOM_QRY_I */ {TOM_QUERY, TTLV_T2, CFG_QY, "Query", lfbselect_print},
474 	/* TOM_EVN_I */ {TOM_EVENTNOT, TTLV_T1, CFG_EVN, "Event Notification",
475 		       lfbselect_print},
476 	/* TOM_RED_I */
477 	    {TOM_PKTREDIR, TTLV_T2, 0, "Packet Redirect", redirect_print},
478 	/* TOM_HBT_I */ {TOM_HEARTBT, ZERO_TTLV, 0, "HeartBeat", NULL},
479 	/* TOM_ASR_I */
480 	    {TOM_ASSNSETREP, TTLV_T1, 0, "Association Response", asrtlv_print},
481 	/* TOM_CNR_I */ {TOM_CONFIGREP, TTLV_T2, CFG_ROPS, "Config Response",
482 		       lfbselect_print},
483 	/* TOM_QRR_I */
484 	    {TOM_QUERYREP, TTLV_T2, CFG_QYR, "Query Response", lfbselect_print},
485 };
486 
487 static inline const struct tom_h *get_forces_tom(uint8_t tom)
488 {
489 	int i;
490 	for (i = TOM_RSV_I; i <= TOM_MAX_IND; i++) {
491 		const struct tom_h *th = &ForCES_msg[i];
492 		if (th->v == tom)
493 			return th;
494 	}
495 	return &ForCES_msg[TOM_RSV_I];
496 }
497 
498 struct pdata_ops {
499 	uint32_t v;
500 	uint16_t flags;
501 	uint16_t op_msk;
502 	const char *s;
503 	int (*print) (netdissect_options *, register const u_char * pptr, register u_int len,
504 		      uint16_t op_msk, int indent);
505 };
506 
507 enum {
508 	PD_RSV_I,
509 	PD_SEL_I,
510 	PD_FDT_I,
511 	PD_SDT_I,
512 	PD_RES_I,
513 	PD_PDT_I,
514 	_PD_RSV_MAX
515 };
516 #define PD_MAX_IND (_TOM_RSV_MAX - 1)
517 
518 static inline int pd_valid(uint16_t pd)
519 {
520 	if (pd >= F_TLV_PDAT && pd <= F_TLV_REST)
521 		return 1;
522 	return 0;
523 }
524 
525 static inline void
526 chk_op_type(netdissect_options *ndo,
527             uint16_t type, uint16_t msk, uint16_t omsk)
528 {
529 	if (type != F_TLV_PDAT) {
530 		if (msk & B_KEYIN) {
531 			if (type != F_TLV_KEYI) {
532 				ND_PRINT((ndo, "Based on flags expected KEYINFO TLV!\n"));
533 			}
534 		} else {
535 			if (!(msk & omsk)) {
536 				ND_PRINT((ndo, "Illegal DATA encoding for type 0x%x programmed %x got %x \n",
537 				          type, omsk, msk));
538 			}
539 		}
540 	}
541 
542 }
543 
544 #define F_SELKEY 1
545 #define F_SELTABRANGE 2
546 #define F_TABAPPEND 4
547 
548 struct res_val {
549 	nd_uint8_t result;
550 	nd_uint8_t resv1;
551 	nd_uint16_t resv2;
552 };
553 
554 static int prestlv_print(netdissect_options *, register const u_char * pptr, register u_int len,
555 			 uint16_t op_msk, int indent);
556 static int pkeyitlv_print(netdissect_options *, register const u_char * pptr, register u_int len,
557 			  uint16_t op_msk, int indent);
558 static int fdatatlv_print(netdissect_options *, register const u_char * pptr, register u_int len,
559 			  uint16_t op_msk, int indent);
560 static int sdatatlv_print(netdissect_options *, register const u_char * pptr, register u_int len,
561 			  uint16_t op_msk, int indent);
562 
563 static const struct pdata_ops ForCES_pdata[PD_MAX_IND + 1] = {
564 	/* PD_RSV_I */ {0, 0, 0, "Invalid message", NULL},
565 	/* PD_SEL_I */ {F_TLV_KEYI, 0, 0, "KEYINFO TLV", pkeyitlv_print},
566 	/* PD_FDT_I */ {F_TLV_FULD, 0, B_FULLD, "FULLDATA TLV", fdatatlv_print},
567 	/* PD_SDT_I */ {F_TLV_SPAD, 0, B_SPARD, "SPARSEDATA TLV", sdatatlv_print},
568 	/* PD_RES_I */ {F_TLV_REST, 0, B_RESTV, "RESULT TLV", prestlv_print},
569 	/* PD_PDT_I */
570 	    {F_TLV_PDAT, 0, 0, "Inner PATH-DATA TLV", recpdoptlv_print},
571 };
572 
573 static inline const struct pdata_ops *get_forces_pd(uint16_t pd)
574 {
575 	int i;
576 	for (i = PD_RSV_I + 1; i <= PD_MAX_IND; i++) {
577 		const struct pdata_ops *pdo = &ForCES_pdata[i];
578 		if (pdo->v == pd)
579 			return pdo;
580 	}
581 	return &ForCES_pdata[TOM_RSV_I];
582 }
583 
584 enum {
585 	E_SUCCESS,
586 	E_INVALID_HEADER,
587 	E_LENGTH_MISMATCH,
588 	E_VERSION_MISMATCH,
589 	E_INVALID_DESTINATION_PID,
590 	E_LFB_UNKNOWN,
591 	E_LFB_NOT_FOUND,
592 	E_LFB_INSTANCE_ID_NOT_FOUND,
593 	E_INVALID_PATH,
594 	E_COMPONENT_DOES_NOT_EXIST,
595 	E_EXISTS,
596 	E_NOT_FOUND,
597 	E_READ_ONLY,
598 	E_INVALID_ARRAY_CREATION,
599 	E_VALUE_OUT_OF_RANGE,
600 	E_CONTENTS_TOO_LONG,
601 	E_INVALID_PARAMETERS,
602 	E_INVALID_MESSAGE_TYPE,
603 	E_INVALID_FLAGS,
604 	E_INVALID_TLV,
605 	E_EVENT_ERROR,
606 	E_NOT_SUPPORTED,
607 	E_MEMORY_ERROR,
608 	E_INTERNAL_ERROR,
609 	/* 0x18-0xFE are reserved .. */
610 	E_UNSPECIFIED_ERROR = 0XFF
611 };
612 
613 static const struct tok ForCES_errs[] = {
614 	{E_SUCCESS, "SUCCESS"},
615 	{E_INVALID_HEADER, "INVALID HEADER"},
616 	{E_LENGTH_MISMATCH, "LENGTH MISMATCH"},
617 	{E_VERSION_MISMATCH, "VERSION MISMATCH"},
618 	{E_INVALID_DESTINATION_PID, "INVALID DESTINATION PID"},
619 	{E_LFB_UNKNOWN, "LFB UNKNOWN"},
620 	{E_LFB_NOT_FOUND, "LFB NOT FOUND"},
621 	{E_LFB_INSTANCE_ID_NOT_FOUND, "LFB INSTANCE ID NOT FOUND"},
622 	{E_INVALID_PATH, "INVALID PATH"},
623 	{E_COMPONENT_DOES_NOT_EXIST, "COMPONENT DOES NOT EXIST"},
624 	{E_EXISTS, "EXISTS ALREADY"},
625 	{E_NOT_FOUND, "NOT FOUND"},
626 	{E_READ_ONLY, "READ ONLY"},
627 	{E_INVALID_ARRAY_CREATION, "INVALID ARRAY CREATION"},
628 	{E_VALUE_OUT_OF_RANGE, "VALUE OUT OF RANGE"},
629 	{E_CONTENTS_TOO_LONG, "CONTENTS TOO LONG"},
630 	{E_INVALID_PARAMETERS, "INVALID PARAMETERS"},
631 	{E_INVALID_MESSAGE_TYPE, "INVALID MESSAGE TYPE"},
632 	{E_INVALID_FLAGS, "INVALID FLAGS"},
633 	{E_INVALID_TLV, "INVALID TLV"},
634 	{E_EVENT_ERROR, "EVENT ERROR"},
635 	{E_NOT_SUPPORTED, "NOT SUPPORTED"},
636 	{E_MEMORY_ERROR, "MEMORY ERROR"},
637 	{E_INTERNAL_ERROR, "INTERNAL ERROR"},
638 	{E_UNSPECIFIED_ERROR, "UNSPECIFIED ERROR"},
639 	{0, NULL}
640 };
641 
642 #define RESLEN	4
643 
644 static int
645 prestlv_print(netdissect_options *ndo,
646               register const u_char * pptr, register u_int len,
647               uint16_t op_msk _U_, int indent)
648 {
649 	const struct forces_tlv *tlv = (const struct forces_tlv *)pptr;
650 	register const u_char *tdp = (const u_char *) TLV_DATA(tlv);
651 	const struct res_val *r = (const struct res_val *)tdp;
652 	u_int dlen;
653 
654 	/*
655 	 * pdatacnt_print() has ensured that len (the TLV length)
656 	 * >= TLV_HDRL.
657 	 */
658 	dlen = len - TLV_HDRL;
659 	if (dlen != RESLEN) {
660 		ND_PRINT((ndo, "illegal RESULT-TLV: %d bytes!\n", dlen));
661 		return -1;
662 	}
663 
664 	ND_TCHECK(*r);
665 	if (r->result >= 0x18 && r->result <= 0xFE) {
666 		ND_PRINT((ndo, "illegal reserved result code: 0x%x!\n", r->result));
667 		return -1;
668 	}
669 
670 	if (ndo->ndo_vflag >= 3) {
671 		char *ib = indent_pr(indent, 0);
672 		ND_PRINT((ndo, "%s  Result: %s (code 0x%x)\n", ib,
673 		       tok2str(ForCES_errs, NULL, r->result), r->result));
674 	}
675 	return 0;
676 
677 trunc:
678 	ND_PRINT((ndo, "%s", tstr));
679 	return -1;
680 }
681 
682 static int
683 fdatatlv_print(netdissect_options *ndo,
684                register const u_char * pptr, register u_int len,
685                uint16_t op_msk _U_, int indent)
686 {
687 	const struct forces_tlv *tlv = (const struct forces_tlv *)pptr;
688 	u_int rlen;
689 	register const u_char *tdp = (const u_char *) TLV_DATA(tlv);
690 	uint16_t type;
691 
692 	/*
693 	 * pdatacnt_print() or pkeyitlv_print() has ensured that len
694 	 * (the TLV length) >= TLV_HDRL.
695 	 */
696 	rlen = len - TLV_HDRL;
697 	ND_TCHECK(*tlv);
698 	type = EXTRACT_16BITS(&tlv->type);
699 	if (type != F_TLV_FULD) {
700 		ND_PRINT((ndo, "Error: expecting FULLDATA!\n"));
701 		return -1;
702 	}
703 
704 	if (ndo->ndo_vflag >= 3) {
705 		char *ib = indent_pr(indent + 2, 1);
706 		ND_PRINT((ndo, "%s[", &ib[1]));
707 		hex_print_with_offset(ndo, ib, tdp, rlen, 0);
708 		ND_PRINT((ndo, "\n%s]\n", &ib[1]));
709 	}
710 	return 0;
711 
712 trunc:
713 	ND_PRINT((ndo, "%s", tstr));
714 	return -1;
715 }
716 
717 static int
718 sdatailv_print(netdissect_options *ndo,
719                register const u_char * pptr, register u_int len,
720                uint16_t op_msk _U_, int indent)
721 {
722 	u_int rlen;
723 	const struct forces_ilv *ilv = (const struct forces_ilv *)pptr;
724 	int invilv;
725 
726 	if (len < ILV_HDRL) {
727 		ND_PRINT((ndo, "Error: BAD SPARSEDATA-TLV!\n"));
728 		return -1;
729 	}
730 	rlen = len;
731 	indent += 1;
732 	while (rlen != 0) {
733 #if 0
734 		ND_PRINT((ndo, "Jamal - outstanding length <%d>\n", rlen));
735 #endif
736 		char *ib = indent_pr(indent, 1);
737 		register const u_char *tdp = (const u_char *) ILV_DATA(ilv);
738 		ND_TCHECK(*ilv);
739 		invilv = ilv_valid(ilv, rlen);
740 		if (invilv) {
741 			ND_PRINT((ndo, "%s[", &ib[1]));
742 			hex_print_with_offset(ndo, ib, tdp, rlen, 0);
743 			ND_PRINT((ndo, "\n%s]\n", &ib[1]));
744 			return -1;
745 		}
746 		if (ndo->ndo_vflag >= 3) {
747 			int ilvl = EXTRACT_32BITS(&ilv->length);
748 			ND_PRINT((ndo, "\n%s ILV: type %x length %d\n", &ib[1],
749 			       EXTRACT_32BITS(&ilv->type), ilvl));
750 			hex_print_with_offset(ndo, "\t\t[", tdp, ilvl-ILV_HDRL, 0);
751 		}
752 
753 		ilv = GO_NXT_ILV(ilv, rlen);
754 	}
755 
756 	return 0;
757 
758 trunc:
759 	ND_PRINT((ndo, "%s", tstr));
760 	return -1;
761 }
762 
763 static int
764 sdatatlv_print(netdissect_options *ndo,
765                register const u_char * pptr, register u_int len,
766                uint16_t op_msk, int indent)
767 {
768 	const struct forces_tlv *tlv = (const struct forces_tlv *)pptr;
769 	u_int rlen;
770 	register const u_char *tdp = (const u_char *) TLV_DATA(tlv);
771 	uint16_t type;
772 
773 	/*
774 	 * pdatacnt_print() has ensured that len (the TLV length)
775 	 * >= TLV_HDRL.
776 	 */
777 	rlen = len - TLV_HDRL;
778 	ND_TCHECK(*tlv);
779 	type = EXTRACT_16BITS(&tlv->type);
780 	if (type != F_TLV_SPAD) {
781 		ND_PRINT((ndo, "Error: expecting SPARSEDATA!\n"));
782 		return -1;
783 	}
784 
785 	return sdatailv_print(ndo, tdp, rlen, op_msk, indent);
786 
787 trunc:
788 	ND_PRINT((ndo, "%s", tstr));
789 	return -1;
790 }
791 
792 static int
793 pkeyitlv_print(netdissect_options *ndo,
794                register const u_char * pptr, register u_int len,
795                uint16_t op_msk, int indent)
796 {
797 	const struct forces_tlv *tlv = (const struct forces_tlv *)pptr;
798 	register const u_char *tdp = (const u_char *) TLV_DATA(tlv);
799 	register const u_char *dp = tdp + 4;
800 	const struct forces_tlv *kdtlv = (const struct forces_tlv *)dp;
801 	uint32_t id;
802 	char *ib = indent_pr(indent, 0);
803 	uint16_t type, tll;
804 	u_int invtlv;
805 
806 	ND_TCHECK(*tdp);
807 	id = EXTRACT_32BITS(tdp);
808 	ND_PRINT((ndo, "%sKeyinfo: Key 0x%x\n", ib, id));
809 	ND_TCHECK(*kdtlv);
810 	type = EXTRACT_16BITS(&kdtlv->type);
811 	invtlv = tlv_valid(kdtlv, len);
812 
813 	if (invtlv) {
814 		ND_PRINT((ndo, "%s TLV type 0x%x len %d\n",
815 		       tok2str(ForCES_TLV_err, NULL, invtlv), type,
816 		       EXTRACT_16BITS(&kdtlv->length)));
817 		return -1;
818 	}
819 	/*
820 	 * At this point, tlv_valid() has ensured that the TLV
821 	 * length is large enough but not too large (it doesn't
822 	 * go past the end of the containing TLV).
823 	 */
824 	tll = EXTRACT_16BITS(&kdtlv->length);
825 	dp = (const u_char *) TLV_DATA(kdtlv);
826 	return fdatatlv_print(ndo, dp, tll, op_msk, indent);
827 
828 trunc:
829 	ND_PRINT((ndo, "%s", tstr));
830 	return -1;
831 }
832 
833 #define PTH_DESC_SIZE 12
834 
835 static int
836 pdatacnt_print(netdissect_options *ndo,
837                register const u_char * pptr, register u_int len,
838                uint16_t IDcnt, uint16_t op_msk, int indent)
839 {
840 	u_int i;
841 	uint32_t id;
842 	char *ib = indent_pr(indent, 0);
843 
844 	if ((op_msk & B_APPND) && ndo->ndo_vflag >= 3) {
845 		ND_PRINT((ndo, "%sTABLE APPEND\n", ib));
846 	}
847 	for (i = 0; i < IDcnt; i++) {
848 		ND_TCHECK2(*pptr, 4);
849 		if (len < 4)
850 			goto trunc;
851 		id = EXTRACT_32BITS(pptr);
852 		if (ndo->ndo_vflag >= 3)
853 			ND_PRINT((ndo, "%sID#%02u: %d\n", ib, i + 1, id));
854 		len -= 4;
855 		pptr += 4;
856 	}
857 
858 	if ((op_msk & B_TRNG) || (op_msk & B_KEYIN)) {
859 		if (op_msk & B_TRNG) {
860 			uint32_t starti, endi;
861 
862 			if (len < PTH_DESC_SIZE) {
863 				ND_PRINT((ndo, "pathlength %d with key/range too short %d\n",
864 				       len, PTH_DESC_SIZE));
865 				return -1;
866 			}
867 
868 			pptr += sizeof(struct forces_tlv);
869 			len -= sizeof(struct forces_tlv);
870 
871 			starti = EXTRACT_32BITS(pptr);
872 			pptr += 4;
873 			len -= 4;
874 
875 			endi = EXTRACT_32BITS(pptr);
876 			pptr += 4;
877 			len -= 4;
878 
879 			if (ndo->ndo_vflag >= 3)
880 				ND_PRINT((ndo, "%sTable range: [%d,%d]\n", ib, starti, endi));
881 		}
882 
883 		if (op_msk & B_KEYIN) {
884 			const struct forces_tlv *keytlv;
885 			uint16_t tll;
886 
887 			if (len < PTH_DESC_SIZE) {
888 				ND_PRINT((ndo, "pathlength %d with key/range too short %d\n",
889 				       len, PTH_DESC_SIZE));
890 				return -1;
891 			}
892 
893 			/* skip keyid */
894 			pptr += 4;
895 			len -= 4;
896 			keytlv = (const struct forces_tlv *)pptr;
897 			/* skip header */
898 			pptr += sizeof(struct forces_tlv);
899 			len -= sizeof(struct forces_tlv);
900 			/* skip key content */
901 			tll = EXTRACT_16BITS(&keytlv->length);
902 			if (tll < TLV_HDRL) {
903 				ND_PRINT((ndo, "key content length %u < %u\n",
904 					tll, TLV_HDRL));
905 				return -1;
906 			}
907 			tll -= TLV_HDRL;
908 			if (len < tll) {
909 				ND_PRINT((ndo, "key content too short\n"));
910 				return -1;
911 			}
912 			pptr += tll;
913 			len -= tll;
914 		}
915 
916 	}
917 
918 	if (len) {
919 		const struct forces_tlv *pdtlv = (const struct forces_tlv *)pptr;
920 		uint16_t type;
921 		uint16_t tll;
922 		int pad = 0;
923 		u_int aln;
924 		u_int invtlv;
925 
926 		ND_TCHECK(*pdtlv);
927 		type = EXTRACT_16BITS(&pdtlv->type);
928 		invtlv = tlv_valid(pdtlv, len);
929 		if (invtlv) {
930 			ND_PRINT((ndo, "%s Outstanding bytes %d for TLV type 0x%x TLV len %d\n",
931 			          tok2str(ForCES_TLV_err, NULL, invtlv), len, type,
932 			          EXTRACT_16BITS(&pdtlv->length)));
933 			goto pd_err;
934 		}
935 		/*
936 		 * At this point, tlv_valid() has ensured that the TLV
937 		 * length is large enough but not too large (it doesn't
938 		 * go past the end of the containing TLV).
939 		 */
940 		tll = EXTRACT_16BITS(&pdtlv->length) - TLV_HDRL;
941 		aln = F_ALN_LEN(EXTRACT_16BITS(&pdtlv->length));
942 		if (aln > EXTRACT_16BITS(&pdtlv->length)) {
943 			if (aln > len) {
944 				ND_PRINT((ndo,
945 				          "Invalid padded pathdata TLV type 0x%x len %d missing %d pad bytes\n",
946 				          type, EXTRACT_16BITS(&pdtlv->length), aln - len));
947 			} else {
948 				pad = aln - EXTRACT_16BITS(&pdtlv->length);
949 			}
950 		}
951 		if (pd_valid(type)) {
952 			const struct pdata_ops *ops = get_forces_pd(type);
953 
954 			if (ndo->ndo_vflag >= 3 && ops->v != F_TLV_PDAT) {
955 				if (pad)
956 					ND_PRINT((ndo, "%s  %s (Length %d DataLen %d pad %d Bytes)\n",
957 					          ib, ops->s, EXTRACT_16BITS(&pdtlv->length), tll, pad));
958 				else
959 					ND_PRINT((ndo, "%s  %s (Length %d DataLen %d Bytes)\n",
960 					          ib, ops->s, EXTRACT_16BITS(&pdtlv->length), tll));
961 			}
962 
963 			chk_op_type(ndo, type, op_msk, ops->op_msk);
964 
965 			if (ops->print(ndo, (const u_char *)pdtlv,
966 					tll + pad + TLV_HDRL, op_msk,
967 					indent + 2) == -1)
968 				return -1;
969 			len -= (TLV_HDRL + pad + tll);
970 		} else {
971 			ND_PRINT((ndo, "Invalid path data content type 0x%x len %d\n",
972 			       type, EXTRACT_16BITS(&pdtlv->length)));
973 pd_err:
974 			if (EXTRACT_16BITS(&pdtlv->length)) {
975                                 hex_print_with_offset(ndo, "Bad Data val\n\t  [",
976 						      pptr, len, 0);
977 				ND_PRINT((ndo, "]\n"));
978 
979 				return -1;
980 			}
981 		}
982 	}
983 	return len;
984 
985 trunc:
986 	ND_PRINT((ndo, "%s", tstr));
987 	return -1;
988 }
989 
990 static int
991 pdata_print(netdissect_options *ndo,
992             register const u_char * pptr, register u_int len,
993             uint16_t op_msk, int indent)
994 {
995 	const struct pathdata_h *pdh = (const struct pathdata_h *)pptr;
996 	char *ib = indent_pr(indent, 0);
997 	u_int minsize = 0;
998 	int more_pd = 0;
999 	uint16_t idcnt = 0;
1000 
1001 	ND_TCHECK(*pdh);
1002 	if (len < sizeof(struct pathdata_h))
1003 		goto trunc;
1004 	if (ndo->ndo_vflag >= 3) {
1005 		ND_PRINT((ndo, "\n%sPathdata: Flags 0x%x ID count %d\n",
1006 		       ib, EXTRACT_16BITS(&pdh->pflags), EXTRACT_16BITS(&pdh->pIDcnt)));
1007 	}
1008 
1009 	if (EXTRACT_16BITS(&pdh->pflags) & F_SELKEY) {
1010 		op_msk |= B_KEYIN;
1011 	}
1012 
1013 	/* Table GET Range operation */
1014 	if (EXTRACT_16BITS(&pdh->pflags) & F_SELTABRANGE) {
1015 		op_msk |= B_TRNG;
1016 	}
1017 	/* Table SET append operation */
1018 	if (EXTRACT_16BITS(&pdh->pflags) & F_TABAPPEND) {
1019 		op_msk |= B_APPND;
1020 	}
1021 
1022 	pptr += sizeof(struct pathdata_h);
1023 	len -= sizeof(struct pathdata_h);
1024 	idcnt = EXTRACT_16BITS(&pdh->pIDcnt);
1025 	minsize = idcnt * 4;
1026 	if (len < minsize) {
1027 		ND_PRINT((ndo, "\t\t\ttruncated IDs expected %uB got %uB\n", minsize,
1028 		       len));
1029 		hex_print_with_offset(ndo, "\t\t\tID Data[", pptr, len, 0);
1030 		ND_PRINT((ndo, "]\n"));
1031 		return -1;
1032 	}
1033 
1034 	if ((op_msk & B_TRNG) && (op_msk & B_KEYIN)) {
1035 		ND_PRINT((ndo, "\t\t\tIllegal to have both Table ranges and keys\n"));
1036 		return -1;
1037 	}
1038 
1039 	more_pd = pdatacnt_print(ndo, pptr, len, idcnt, op_msk, indent);
1040 	if (more_pd > 0) {
1041 		int consumed = len - more_pd;
1042 		pptr += consumed;
1043 		len = more_pd;
1044 		/* XXX: Argh, recurse some more */
1045 		return recpdoptlv_print(ndo, pptr, len, op_msk, indent+1);
1046 	} else
1047 		return 0;
1048 
1049 trunc:
1050 	ND_PRINT((ndo, "%s", tstr));
1051 	return -1;
1052 }
1053 
1054 static int
1055 genoptlv_print(netdissect_options *ndo,
1056                register const u_char * pptr, register u_int len,
1057                uint16_t op_msk, int indent)
1058 {
1059 	const struct forces_tlv *pdtlv = (const struct forces_tlv *)pptr;
1060 	uint16_t type;
1061 	int tll;
1062 	u_int invtlv;
1063 	char *ib = indent_pr(indent, 0);
1064 
1065 	ND_TCHECK(*pdtlv);
1066 	type = EXTRACT_16BITS(&pdtlv->type);
1067 	tll = EXTRACT_16BITS(&pdtlv->length) - TLV_HDRL;
1068 	invtlv = tlv_valid(pdtlv, len);
1069 	ND_PRINT((ndo, "genoptlvprint - %s TLV type 0x%x len %d\n",
1070 	       tok2str(ForCES_TLV, NULL, type), type, EXTRACT_16BITS(&pdtlv->length)));
1071 	if (!invtlv) {
1072 		/*
1073 		 * At this point, tlv_valid() has ensured that the TLV
1074 		 * length is large enough but not too large (it doesn't
1075 		 * go past the end of the containing TLV).
1076 		 */
1077 		register const u_char *dp = (const u_char *) TLV_DATA(pdtlv);
1078 		if (!ttlv_valid(type)) {
1079 			ND_PRINT((ndo, "%s TLV type 0x%x len %d\n",
1080 			       tok2str(ForCES_TLV_err, NULL, invtlv), type,
1081 			       EXTRACT_16BITS(&pdtlv->length)));
1082 			return -1;
1083 		}
1084 		if (ndo->ndo_vflag >= 3)
1085 			ND_PRINT((ndo, "%s%s, length %d (data length %d Bytes)",
1086 			       ib, tok2str(ForCES_TLV, NULL, type),
1087 			       EXTRACT_16BITS(&pdtlv->length), tll));
1088 
1089 		return pdata_print(ndo, dp, tll, op_msk, indent + 1);
1090 	} else {
1091 		ND_PRINT((ndo, "\t\t\tInvalid ForCES TLV type=%x", type));
1092 		return -1;
1093 	}
1094 
1095 trunc:
1096 	ND_PRINT((ndo, "%s", tstr));
1097 	return -1;
1098 }
1099 
1100 static int
1101 recpdoptlv_print(netdissect_options *ndo,
1102                  register const u_char * pptr, register u_int len,
1103                  uint16_t op_msk, int indent)
1104 {
1105 	const struct forces_tlv *pdtlv = (const struct forces_tlv *)pptr;
1106 	int tll;
1107 	u_int invtlv;
1108 	uint16_t type;
1109 	register const u_char *dp;
1110 	char *ib;
1111 
1112 	while (len != 0) {
1113 		ND_TCHECK(*pdtlv);
1114 		invtlv = tlv_valid(pdtlv, len);
1115 		if (invtlv) {
1116 			break;
1117 		}
1118 
1119 		/*
1120 		 * At this point, tlv_valid() has ensured that the TLV
1121 		 * length is large enough but not too large (it doesn't
1122 		 * go past the end of the containing TLV).
1123 		 */
1124 		ib = indent_pr(indent, 0);
1125 		type = EXTRACT_16BITS(&pdtlv->type);
1126 		dp = (const u_char *) TLV_DATA(pdtlv);
1127 		tll = EXTRACT_16BITS(&pdtlv->length) - TLV_HDRL;
1128 
1129 		if (ndo->ndo_vflag >= 3)
1130 			ND_PRINT((ndo, "%s%s, length %d (data encapsulated %d Bytes)",
1131 			          ib, tok2str(ForCES_TLV, NULL, type),
1132 			          EXTRACT_16BITS(&pdtlv->length),
1133 			          EXTRACT_16BITS(&pdtlv->length) - TLV_HDRL));
1134 
1135 		if (pdata_print(ndo, dp, tll, op_msk, indent + 1) == -1)
1136 			return -1;
1137 		pdtlv = GO_NXT_TLV(pdtlv, len);
1138 	}
1139 
1140 	if (len) {
1141 		ND_PRINT((ndo,
1142 		          "\n\t\tMessy PATHDATA TLV header, type (0x%x)\n\t\texcess of %d Bytes ",
1143 		          EXTRACT_16BITS(&pdtlv->type), len - EXTRACT_16BITS(&pdtlv->length)));
1144 		return -1;
1145 	}
1146 
1147 	return 0;
1148 
1149 trunc:
1150 	ND_PRINT((ndo, "%s", tstr));
1151 	return -1;
1152 }
1153 
1154 static int
1155 invoptlv_print(netdissect_options *ndo,
1156                register const u_char * pptr, register u_int len,
1157                uint16_t op_msk _U_, int indent)
1158 {
1159 	char *ib = indent_pr(indent, 1);
1160 
1161 	if (ndo->ndo_vflag >= 3) {
1162 		ND_PRINT((ndo, "%sData[", &ib[1]));
1163 		hex_print_with_offset(ndo, ib, pptr, len, 0);
1164 		ND_PRINT((ndo, "%s]\n", ib));
1165 	}
1166 	return -1;
1167 }
1168 
1169 static int
1170 otlv_print(netdissect_options *ndo,
1171            const struct forces_tlv *otlv, uint16_t op_msk _U_, int indent)
1172 {
1173 	int rc = 0;
1174 	register const u_char *dp = (const u_char *) TLV_DATA(otlv);
1175 	uint16_t type;
1176 	int tll;
1177 	char *ib = indent_pr(indent, 0);
1178 	const struct optlv_h *ops;
1179 
1180 	/*
1181 	 * lfbselect_print() has ensured that EXTRACT_16BITS(&otlv->length)
1182 	 * >= TLV_HDRL.
1183 	 */
1184 	ND_TCHECK(*otlv);
1185 	type = EXTRACT_16BITS(&otlv->type);
1186 	tll = EXTRACT_16BITS(&otlv->length) - TLV_HDRL;
1187 	ops = get_forces_optlv_h(type);
1188 	if (ndo->ndo_vflag >= 3) {
1189 		ND_PRINT((ndo, "%sOper TLV %s(0x%x) length %d\n", ib, ops->s, type,
1190 		       EXTRACT_16BITS(&otlv->length)));
1191 	}
1192 	/* rest of ops must at least have 12B {pathinfo} */
1193 	if (tll < OP_MIN_SIZ) {
1194 		ND_PRINT((ndo, "\t\tOper TLV %s(0x%x) length %d\n", ops->s, type,
1195 		       EXTRACT_16BITS(&otlv->length)));
1196 		ND_PRINT((ndo, "\t\tTruncated data size %d minimum required %d\n", tll,
1197 		       OP_MIN_SIZ));
1198 		return invoptlv_print(ndo, dp, tll, ops->op_msk, indent);
1199 
1200 	}
1201 
1202 	/* XXX - do anything with ops->flags? */
1203         if(ops->print) {
1204                 rc = ops->print(ndo, dp, tll, ops->op_msk, indent + 1);
1205         }
1206 	return rc;
1207 
1208 trunc:
1209 	ND_PRINT((ndo, "%s", tstr));
1210 	return -1;
1211 }
1212 
1213 #define ASTDLN	4
1214 #define ASTMCD	255
1215 static int
1216 asttlv_print(netdissect_options *ndo,
1217              register const u_char * pptr, register u_int len,
1218              uint16_t op_msk _U_, int indent)
1219 {
1220 	uint32_t rescode;
1221 	u_int dlen;
1222 	char *ib = indent_pr(indent, 0);
1223 
1224 	/*
1225 	 * forces_type_print() has ensured that len (the TLV length)
1226 	 * >= TLV_HDRL.
1227 	 */
1228 	dlen = len - TLV_HDRL;
1229 	if (dlen != ASTDLN) {
1230 		ND_PRINT((ndo, "illegal ASTresult-TLV: %d bytes!\n", dlen));
1231 		return -1;
1232 	}
1233 	ND_TCHECK2(*pptr, 4);
1234 	rescode = EXTRACT_32BITS(pptr);
1235 	if (rescode > ASTMCD) {
1236 		ND_PRINT((ndo, "illegal ASTresult result code: %d!\n", rescode));
1237 		return -1;
1238 	}
1239 
1240 	if (ndo->ndo_vflag >= 3) {
1241 		ND_PRINT((ndo, "Teardown reason:\n%s", ib));
1242 		switch (rescode) {
1243 		case 0:
1244 			ND_PRINT((ndo, "Normal Teardown"));
1245 			break;
1246 		case 1:
1247 			ND_PRINT((ndo, "Loss of Heartbeats"));
1248 			break;
1249 		case 2:
1250 			ND_PRINT((ndo, "Out of bandwidth"));
1251 			break;
1252 		case 3:
1253 			ND_PRINT((ndo, "Out of Memory"));
1254 			break;
1255 		case 4:
1256 			ND_PRINT((ndo, "Application Crash"));
1257 			break;
1258 		default:
1259 			ND_PRINT((ndo, "Unknown Teardown reason"));
1260 			break;
1261 		}
1262 		ND_PRINT((ndo, "(%x)\n%s", rescode, ib));
1263 	}
1264 	return 0;
1265 
1266 trunc:
1267 	ND_PRINT((ndo, "%s", tstr));
1268 	return -1;
1269 }
1270 
1271 #define ASRDLN	4
1272 #define ASRMCD	3
1273 static int
1274 asrtlv_print(netdissect_options *ndo,
1275              register const u_char * pptr, register u_int len,
1276              uint16_t op_msk _U_, int indent)
1277 {
1278 	uint32_t rescode;
1279 	u_int dlen;
1280 	char *ib = indent_pr(indent, 0);
1281 
1282 	/*
1283 	 * forces_type_print() has ensured that len (the TLV length)
1284 	 * >= TLV_HDRL.
1285 	 */
1286 	dlen = len - TLV_HDRL;
1287 	if (dlen != ASRDLN) {	/* id, instance, oper tlv */
1288 		ND_PRINT((ndo, "illegal ASRresult-TLV: %d bytes!\n", dlen));
1289 		return -1;
1290 	}
1291 	ND_TCHECK2(*pptr, 4);
1292 	rescode = EXTRACT_32BITS(pptr);
1293 
1294 	if (rescode > ASRMCD) {
1295 		ND_PRINT((ndo, "illegal ASRresult result code: %d!\n", rescode));
1296 		return -1;
1297 	}
1298 
1299 	if (ndo->ndo_vflag >= 3) {
1300 		ND_PRINT((ndo, "\n%s", ib));
1301 		switch (rescode) {
1302 		case 0:
1303 			ND_PRINT((ndo, "Success "));
1304 			break;
1305 		case 1:
1306 			ND_PRINT((ndo, "FE ID invalid "));
1307 			break;
1308 		case 2:
1309 			ND_PRINT((ndo, "permission denied "));
1310 			break;
1311 		default:
1312 			ND_PRINT((ndo, "Unknown "));
1313 			break;
1314 		}
1315 		ND_PRINT((ndo, "(%x)\n%s", rescode, ib));
1316 	}
1317 	return 0;
1318 
1319 trunc:
1320 	ND_PRINT((ndo, "%s", tstr));
1321 	return -1;
1322 }
1323 
1324 #if 0
1325 /*
1326  * XXX - not used.
1327  */
1328 static int
1329 gentltlv_print(netdissect_options *ndo,
1330                register const u_char * pptr _U_, register u_int len,
1331                uint16_t op_msk _U_, int indent _U_)
1332 {
1333 	u_int dlen = len - TLV_HDRL;
1334 
1335 	if (dlen < 4) {		/* at least 32 bits must exist */
1336 		ND_PRINT((ndo, "truncated TLV: %d bytes missing! ", 4 - dlen));
1337 		return -1;
1338 	}
1339 	return 0;
1340 }
1341 #endif
1342 
1343 #define RD_MIN 8
1344 
1345 static int
1346 print_metailv(netdissect_options *ndo,
1347               register const u_char * pptr, uint16_t op_msk _U_, int indent)
1348 {
1349 	u_int rlen;
1350 	char *ib = indent_pr(indent, 0);
1351 	/* XXX: check header length */
1352 	const struct forces_ilv *ilv = (const struct forces_ilv *)pptr;
1353 
1354 	/*
1355 	 * print_metatlv() has ensured that len (what remains in the
1356 	 * ILV) >= ILV_HDRL.
1357 	 */
1358 	rlen = EXTRACT_32BITS(&ilv->length) - ILV_HDRL;
1359 	ND_TCHECK(*ilv);
1360 	ND_PRINT((ndo, "%sMetaID 0x%x length %d\n", ib, EXTRACT_32BITS(&ilv->type),
1361 	       EXTRACT_32BITS(&ilv->length)));
1362 	if (ndo->ndo_vflag >= 3) {
1363 		hex_print_with_offset(ndo, "\t\t[", ILV_DATA(ilv), rlen, 0);
1364 		ND_PRINT((ndo, " ]\n"));
1365 	}
1366 	return 0;
1367 
1368 trunc:
1369 	ND_PRINT((ndo, "%s", tstr));
1370 	return -1;
1371 }
1372 
1373 static int
1374 print_metatlv(netdissect_options *ndo,
1375               register const u_char * pptr, register u_int len,
1376               uint16_t op_msk _U_, int indent)
1377 {
1378 	u_int dlen;
1379 	char *ib = indent_pr(indent, 0);
1380 	u_int rlen;
1381 	const struct forces_ilv *ilv = (const struct forces_ilv *)pptr;
1382 	int invilv;
1383 
1384 	/*
1385 	 * redirect_print() has ensured that len (what remains in the
1386 	 * TLV) >= TLV_HDRL.
1387 	 */
1388 	dlen = len - TLV_HDRL;
1389 	rlen = dlen;
1390 	ND_PRINT((ndo, "\n%s METADATA length %d \n", ib, rlen));
1391 	while (rlen != 0) {
1392 		ND_TCHECK(*ilv);
1393 		invilv = ilv_valid(ilv, rlen);
1394 		if (invilv) {
1395 			break;
1396 		}
1397 
1398 		/*
1399 		 * At this point, ilv_valid() has ensured that the ILV
1400 		 * length is large enough but not too large (it doesn't
1401 		 * go past the end of the containing TLV).
1402 		 */
1403 		print_metailv(ndo, (const u_char *) ilv, 0, indent + 1);
1404 		ilv = GO_NXT_ILV(ilv, rlen);
1405 	}
1406 
1407 	return 0;
1408 
1409 trunc:
1410 	ND_PRINT((ndo, "%s", tstr));
1411 	return -1;
1412 }
1413 
1414 
1415 static int
1416 print_reddata(netdissect_options *ndo,
1417               register const u_char * pptr, register u_int len,
1418               uint16_t op_msk _U_, int indent)
1419 {
1420 	u_int dlen;
1421 	char *ib = indent_pr(indent, 0);
1422 	u_int rlen;
1423 
1424 	dlen = len - TLV_HDRL;
1425 	rlen = dlen;
1426 	ND_PRINT((ndo, "\n%s Redirect Data length %d \n", ib, rlen));
1427 
1428 	if (ndo->ndo_vflag >= 3) {
1429 		ND_PRINT((ndo, "\t\t["));
1430 		hex_print_with_offset(ndo, "\n\t\t", pptr, rlen, 0);
1431 		ND_PRINT((ndo, "\n\t\t]"));
1432 	}
1433 
1434 	return 0;
1435 }
1436 
1437 static int
1438 redirect_print(netdissect_options *ndo,
1439                register const u_char * pptr, register u_int len,
1440                uint16_t op_msk _U_, int indent)
1441 {
1442 	const struct forces_tlv *tlv = (const struct forces_tlv *)pptr;
1443 	u_int dlen;
1444 	u_int rlen;
1445 	u_int invtlv;
1446 
1447 	/*
1448 	 * forces_type_print() has ensured that len (the TLV length)
1449 	 * >= TLV_HDRL.
1450 	 */
1451 	dlen = len - TLV_HDRL;
1452 	if (dlen <= RD_MIN) {
1453 		ND_PRINT((ndo, "\n\t\ttruncated Redirect TLV: %d bytes missing! ",
1454 		       RD_MIN - dlen));
1455 		return -1;
1456 	}
1457 
1458 	rlen = dlen;
1459 	indent += 1;
1460 	while (rlen != 0) {
1461 		ND_TCHECK(*tlv);
1462 		invtlv = tlv_valid(tlv, rlen);
1463 		if (invtlv) {
1464 			ND_PRINT((ndo, "Bad Redirect data\n"));
1465 			break;
1466 		}
1467 
1468 		/*
1469 		 * At this point, tlv_valid() has ensured that the TLV
1470 		 * length is large enough but not too large (it doesn't
1471 		 * go past the end of the containing TLV).
1472 		 */
1473 		if (EXTRACT_16BITS(&tlv->type) == F_TLV_METD) {
1474 			print_metatlv(ndo, (const u_char *) TLV_DATA(tlv),
1475 				      EXTRACT_16BITS(&tlv->length), 0, indent);
1476 		} else if ((EXTRACT_16BITS(&tlv->type) == F_TLV_REDD)) {
1477 			print_reddata(ndo, (const u_char *) TLV_DATA(tlv),
1478 				      EXTRACT_16BITS(&tlv->length), 0, indent);
1479 		} else {
1480 			ND_PRINT((ndo, "Unknown REDIRECT TLV 0x%x len %d\n",
1481 			       EXTRACT_16BITS(&tlv->type),
1482 			       EXTRACT_16BITS(&tlv->length)));
1483 		}
1484 
1485 		tlv = GO_NXT_TLV(tlv, rlen);
1486 	}
1487 
1488 	if (rlen) {
1489 		ND_PRINT((ndo,
1490 		          "\n\t\tMessy Redirect TLV header, type (0x%x)\n\t\texcess of %d Bytes ",
1491 		          EXTRACT_16BITS(&tlv->type),
1492 		          rlen - EXTRACT_16BITS(&tlv->length)));
1493 		return -1;
1494 	}
1495 
1496 	return 0;
1497 
1498 trunc:
1499 	ND_PRINT((ndo, "%s", tstr));
1500 	return -1;
1501 }
1502 
1503 #define OP_OFF 8
1504 #define OP_MIN 12
1505 
1506 static int
1507 lfbselect_print(netdissect_options *ndo,
1508                 register const u_char * pptr, register u_int len,
1509                 uint16_t op_msk, int indent)
1510 {
1511 	const struct forces_lfbsh *lfbs;
1512 	const struct forces_tlv *otlv;
1513 	char *ib = indent_pr(indent, 0);
1514 	u_int dlen;
1515 	u_int rlen;
1516 	u_int invtlv;
1517 
1518 	/*
1519 	 * forces_type_print() has ensured that len (the TLV length)
1520 	 * >= TLV_HDRL.
1521 	 */
1522 	dlen = len - TLV_HDRL;
1523 	if (dlen <= OP_MIN) {	/* id, instance, oper tlv header .. */
1524 		ND_PRINT((ndo, "\n\t\ttruncated lfb selector: %d bytes missing! ",
1525 		       OP_MIN - dlen));
1526 		return -1;
1527 	}
1528 
1529 	/*
1530 	 * At this point, we know that dlen > OP_MIN; OP_OFF < OP_MIN, so
1531 	 * we also know that it's > OP_OFF.
1532 	 */
1533 	rlen = dlen - OP_OFF;
1534 
1535 	lfbs = (const struct forces_lfbsh *)pptr;
1536 	ND_TCHECK(*lfbs);
1537 	if (ndo->ndo_vflag >= 3) {
1538 		ND_PRINT((ndo, "\n%s%s(Classid %x) instance %x\n",
1539 		       ib, tok2str(ForCES_LFBs, NULL, EXTRACT_32BITS(&lfbs->class)),
1540 		       EXTRACT_32BITS(&lfbs->class),
1541 		       EXTRACT_32BITS(&lfbs->instance)));
1542 	}
1543 
1544 	otlv = (const struct forces_tlv *)(lfbs + 1);
1545 
1546 	indent += 1;
1547 	while (rlen != 0) {
1548 		ND_TCHECK(*otlv);
1549 		invtlv = tlv_valid(otlv, rlen);
1550 		if (invtlv)
1551 			break;
1552 
1553 		/*
1554 		 * At this point, tlv_valid() has ensured that the TLV
1555 		 * length is large enough but not too large (it doesn't
1556 		 * go past the end of the containing TLV).
1557 		 */
1558 		if (op_valid(EXTRACT_16BITS(&otlv->type), op_msk)) {
1559 			otlv_print(ndo, otlv, 0, indent);
1560 		} else {
1561 			if (ndo->ndo_vflag < 3)
1562 				ND_PRINT((ndo, "\n"));
1563 			ND_PRINT((ndo,
1564 			          "\t\tINValid oper-TLV type 0x%x length %d for this ForCES message\n",
1565 			          EXTRACT_16BITS(&otlv->type), EXTRACT_16BITS(&otlv->length)));
1566 			invoptlv_print(ndo, (const u_char *)otlv, rlen, 0, indent);
1567 		}
1568 		otlv = GO_NXT_TLV(otlv, rlen);
1569 	}
1570 
1571 	if (rlen) {
1572 		ND_PRINT((ndo,
1573 		          "\n\t\tMessy oper TLV header, type (0x%x)\n\t\texcess of %d Bytes ",
1574 		          EXTRACT_16BITS(&otlv->type), rlen - EXTRACT_16BITS(&otlv->length)));
1575 		return -1;
1576 	}
1577 
1578 	return 0;
1579 
1580 trunc:
1581 	ND_PRINT((ndo, "%s", tstr));
1582 	return -1;
1583 }
1584 
1585 static int
1586 forces_type_print(netdissect_options *ndo,
1587                   register const u_char * pptr, const struct forcesh *fhdr _U_,
1588                   register u_int mlen, const struct tom_h *tops)
1589 {
1590 	const struct forces_tlv *tltlv;
1591 	u_int rlen;
1592 	u_int invtlv;
1593 	int rc = 0;
1594 	int ttlv = 0;
1595 
1596 	/*
1597 	 * forces_print() has already checked that mlen >= ForCES_HDRL
1598 	 * by calling ForCES_HLN_VALID().
1599 	 */
1600 	rlen = mlen - ForCES_HDRL;
1601 
1602 	if (rlen > TLV_HLN) {
1603 		if (tops->flags & ZERO_TTLV) {
1604 			ND_PRINT((ndo, "<0x%x>Illegal Top level TLV!\n", tops->flags));
1605 			return -1;
1606 		}
1607 	} else {
1608 		if (tops->flags & ZERO_MORE_TTLV)
1609 			return 0;
1610 		if (tops->flags & ONE_MORE_TTLV) {
1611 			ND_PRINT((ndo, "\tTop level TLV Data missing!\n"));
1612 			return -1;
1613 		}
1614 	}
1615 
1616 	if (tops->flags & ZERO_TTLV) {
1617 		return 0;
1618 	}
1619 
1620 	ttlv = tops->flags >> 4;
1621 	tltlv = GET_TOP_TLV(pptr);
1622 
1623 	/*XXX: 15 top level tlvs will probably be fine
1624 	   You are nuts if you send more ;-> */
1625 	while (rlen != 0) {
1626 		ND_TCHECK(*tltlv);
1627 		invtlv = tlv_valid(tltlv, rlen);
1628 		if (invtlv)
1629 			break;
1630 
1631 		/*
1632 		 * At this point, tlv_valid() has ensured that the TLV
1633 		 * length is large enough but not too large (it doesn't
1634 		 * go past the end of the packet).
1635 		 */
1636 		if (!ttlv_valid(EXTRACT_16BITS(&tltlv->type))) {
1637 			ND_PRINT((ndo, "\n\tInvalid ForCES Top TLV type=0x%x",
1638 			       EXTRACT_16BITS(&tltlv->type)));
1639 			return -1;
1640 		}
1641 
1642 		if (ndo->ndo_vflag >= 3)
1643 			ND_PRINT((ndo, "\t%s, length %d (data length %d Bytes)",
1644 			       tok2str(ForCES_TLV, NULL, EXTRACT_16BITS(&tltlv->type)),
1645 			       EXTRACT_16BITS(&tltlv->length),
1646 			       EXTRACT_16BITS(&tltlv->length) - TLV_HDRL));
1647 
1648 		rc = tops->print(ndo, (const u_char *) TLV_DATA(tltlv),
1649 				 EXTRACT_16BITS(&tltlv->length), tops->op_msk, 9);
1650 		if (rc < 0) {
1651 			return -1;
1652 		}
1653 		tltlv = GO_NXT_TLV(tltlv, rlen);
1654 		ttlv--;
1655 		if (ttlv <= 0)
1656 			break;
1657 	}
1658 	/*
1659 	 * XXX - if ttlv != 0, does that mean that the packet was too
1660 	 * short, and didn't have *enough* TLVs in it?
1661 	 */
1662 	if (rlen) {
1663 		ND_PRINT((ndo, "\tMess TopTLV header: min %u, total %d advertised %d ",
1664 		       TLV_HDRL, rlen, EXTRACT_16BITS(&tltlv->length)));
1665 		return -1;
1666 	}
1667 
1668 	return 0;
1669 
1670 trunc:
1671 	ND_PRINT((ndo, "%s", tstr));
1672 	return -1;
1673 }
1674 
1675 void
1676 forces_print(netdissect_options *ndo,
1677              register const u_char * pptr, register u_int len)
1678 {
1679 	const struct forcesh *fhdr;
1680 	u_int mlen;
1681 	uint32_t flg_raw;
1682 	const struct tom_h *tops;
1683 	int rc = 0;
1684 
1685 	fhdr = (const struct forcesh *)pptr;
1686 	ND_TCHECK(*fhdr);
1687 	if (!tom_valid(fhdr->fm_tom)) {
1688 		ND_PRINT((ndo, "Invalid ForCES message type %d\n", fhdr->fm_tom));
1689 		goto error;
1690 	}
1691 
1692 	mlen = ForCES_BLN(fhdr);
1693 
1694 	tops = get_forces_tom(fhdr->fm_tom);
1695 	if (tops->v == TOM_RSVD) {
1696 		ND_PRINT((ndo, "\n\tUnknown ForCES message type=0x%x", fhdr->fm_tom));
1697 		goto error;
1698 	}
1699 
1700 	ND_PRINT((ndo, "\n\tForCES %s ", tops->s));
1701 	if (!ForCES_HLN_VALID(mlen, len)) {
1702 		ND_PRINT((ndo,
1703 		          "Illegal ForCES pkt len - min %u, total recvd %d, advertised %d ",
1704 		          ForCES_HDRL, len, ForCES_BLN(fhdr)));
1705 		goto error;
1706 	}
1707 
1708 	ND_TCHECK2(*(pptr + 20), 4);
1709 	flg_raw = EXTRACT_32BITS(pptr + 20);
1710 	if (ndo->ndo_vflag >= 1) {
1711 		ND_PRINT((ndo, "\n\tForCES Version %d len %uB flags 0x%08x ",
1712 		       ForCES_V(fhdr), mlen, flg_raw));
1713 		ND_PRINT((ndo,
1714 		       "\n\tSrcID 0x%x(%s) DstID 0x%x(%s) Correlator 0x%" PRIx64,
1715 		       ForCES_SID(fhdr), ForCES_node(ForCES_SID(fhdr)),
1716 		       ForCES_DID(fhdr), ForCES_node(ForCES_DID(fhdr)),
1717 		       EXTRACT_64BITS(fhdr->fm_cor)));
1718 
1719 	}
1720 	if (ndo->ndo_vflag >= 2) {
1721 		ND_PRINT((ndo,
1722 		     "\n\tForCES flags:\n\t  %s(0x%x), prio=%d, %s(0x%x),\n\t  %s(0x%x), %s(0x%x)\n",
1723 		     tok2str(ForCES_ACKs, "ACKUnknown", ForCES_ACK(fhdr)),
1724 		     ForCES_ACK(fhdr),
1725 		     ForCES_PRI(fhdr),
1726 		     tok2str(ForCES_EMs, "EMUnknown", ForCES_EM(fhdr)),
1727 		     ForCES_EM(fhdr),
1728 		     tok2str(ForCES_ATs, "ATUnknown", ForCES_AT(fhdr)),
1729 		     ForCES_AT(fhdr),
1730 		     tok2str(ForCES_TPs, "TPUnknown", ForCES_TP(fhdr)),
1731 		     ForCES_TP(fhdr)));
1732 		ND_PRINT((ndo,
1733 		     "\t  Extra flags: rsv(b5-7) 0x%x rsv(b13-31) 0x%x\n",
1734 		     ForCES_RS1(fhdr), ForCES_RS2(fhdr)));
1735 	}
1736 	rc = forces_type_print(ndo, pptr, fhdr, mlen, tops);
1737 	if (rc < 0) {
1738 error:
1739 		hex_print_with_offset(ndo, "\n\t[", pptr, len, 0);
1740 		ND_PRINT((ndo, "\n\t]"));
1741 		return;
1742 	}
1743 
1744 	if (ndo->ndo_vflag >= 4) {
1745 		ND_PRINT((ndo, "\n\t  Raw ForCES message\n\t ["));
1746 		hex_print_with_offset(ndo, "\n\t ", pptr, len, 0);
1747 		ND_PRINT((ndo, "\n\t ]"));
1748 	}
1749 	ND_PRINT((ndo, "\n"));
1750 	return;
1751 
1752 trunc:
1753 	ND_PRINT((ndo, "%s", tstr));
1754 }
1755 /*
1756  * Local Variables:
1757  * c-style: whitesmith
1758  * c-basic-offset: 8
1759  * End:
1760  */
1761