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