xref: /netbsd-src/external/bsd/tcpdump/dist/print-forces.c (revision 26ba0b503b498a5194a71ac319838b7f5497f3fe)
10f74e101Schristos /*
20f74e101Schristos  * Redistribution and use in source and binary forms, with or without
30f74e101Schristos  * modification, are permitted provided that: (1) source code
40f74e101Schristos  * distributions retain the above copyright notice and this paragraph
50f74e101Schristos  * in its entirety, and (2) distributions including binary code include
60f74e101Schristos  * the above copyright notice and this paragraph in its entirety in
70f74e101Schristos  * the documentation or other materials provided with the distribution.
80f74e101Schristos  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
90f74e101Schristos  * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
100f74e101Schristos  * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
110f74e101Schristos  * FOR A PARTICULAR PURPOSE.
120f74e101Schristos  *
130f74e101Schristos  * Copyright (c) 2009 Mojatatu Networks, Inc
140f74e101Schristos  *
150f74e101Schristos  */
160f74e101Schristos 
17fdccd7e4Schristos #include <sys/cdefs.h>
18fdccd7e4Schristos #ifndef lint
19*26ba0b50Schristos __RCSID("$NetBSD: print-forces.c,v 1.8 2024/09/02 16:15:31 christos Exp $");
20fdccd7e4Schristos #endif
21fdccd7e4Schristos 
22dc860a36Sspz /* \summary: Forwarding and Control Element Separation (ForCES) Protocol printer */
23dc860a36Sspz 
24dc860a36Sspz /* specification: RFC 5810 */
25dc860a36Sspz 
26c74ad251Schristos #include <config.h>
270f74e101Schristos 
28c74ad251Schristos #include "netdissect-stdinc.h"
290f74e101Schristos 
30fdccd7e4Schristos #include "netdissect.h"
310f74e101Schristos #include "extract.h"
320f74e101Schristos 
33026d7285Schristos 
34026d7285Schristos #define	ForCES_VERS	1
35026d7285Schristos #define	ForCES_HDRL	24
36026d7285Schristos #define	ForCES_ALNL	4U
37026d7285Schristos #define TLV_HDRL	4
38026d7285Schristos #define ILV_HDRL	8
39026d7285Schristos 
40026d7285Schristos #define TOM_RSVD	0x0
41026d7285Schristos #define TOM_ASSNSETUP	0x1
42026d7285Schristos #define TOM_ASSNTEARD	0x2
43026d7285Schristos #define TOM_CONFIG	0x3
44026d7285Schristos #define TOM_QUERY	0x4
45026d7285Schristos #define TOM_EVENTNOT	0x5
46026d7285Schristos #define TOM_PKTREDIR	0x6
47026d7285Schristos #define TOM_HEARTBT	0x0F
48026d7285Schristos #define TOM_ASSNSETREP	0x11
49026d7285Schristos #define TOM_CONFIGREP	0x13
50026d7285Schristos #define TOM_QUERYREP	0x14
51026d7285Schristos 
52026d7285Schristos /*
53026d7285Schristos  * tom_h Flags: resv1(8b):maxtlvs(4b):resv2(2b):mintlv(2b)
54026d7285Schristos */
55026d7285Schristos #define ZERO_TTLV	0x01
56026d7285Schristos #define ZERO_MORE_TTLV	0x02
57026d7285Schristos #define ONE_MORE_TTLV	0x04
58026d7285Schristos #define ZERO_TLV	0x00
59026d7285Schristos #define ONE_TLV		0x10
60026d7285Schristos #define TWO_TLV		0x20
61026d7285Schristos #define MAX_TLV		0xF0
62026d7285Schristos 
63026d7285Schristos #define TTLV_T1		(ONE_MORE_TTLV|ONE_TLV)
64026d7285Schristos #define TTLV_T2		(ONE_MORE_TTLV|MAX_TLV)
65026d7285Schristos 
66026d7285Schristos struct tom_h {
67b3a00663Schristos 	uint32_t v;
68b3a00663Schristos 	uint16_t flags;
69b3a00663Schristos 	uint16_t op_msk;
70026d7285Schristos 	const char *s;
71c74ad251Schristos 	int (*print) (netdissect_options *ndo, const u_char * pptr, u_int len,
72b3a00663Schristos 		      uint16_t op_msk, int indent);
73026d7285Schristos };
74026d7285Schristos 
75026d7285Schristos enum {
76026d7285Schristos 	TOM_RSV_I,
77026d7285Schristos 	TOM_ASS_I,
78026d7285Schristos 	TOM_AST_I,
79026d7285Schristos 	TOM_CFG_I,
80026d7285Schristos 	TOM_QRY_I,
81026d7285Schristos 	TOM_EVN_I,
82026d7285Schristos 	TOM_RED_I,
83026d7285Schristos 	TOM_HBT_I,
84026d7285Schristos 	TOM_ASR_I,
85026d7285Schristos 	TOM_CNR_I,
86026d7285Schristos 	TOM_QRR_I,
87026d7285Schristos 	_TOM_RSV_MAX
88026d7285Schristos };
89026d7285Schristos #define TOM_MAX_IND (_TOM_RSV_MAX - 1)
90026d7285Schristos 
91c74ad251Schristos static int
92c74ad251Schristos tom_valid(uint8_t tom)
93026d7285Schristos {
94026d7285Schristos 	if (tom > 0) {
95026d7285Schristos 		if (tom >= 0x7 && tom <= 0xe)
96026d7285Schristos 			return 0;
97026d7285Schristos 		if (tom == 0x10)
98026d7285Schristos 			return 0;
99026d7285Schristos 		if (tom > 0x14)
100026d7285Schristos 			return 0;
101026d7285Schristos 		return 1;
102026d7285Schristos 	} else
103026d7285Schristos 		return 0;
104026d7285Schristos }
105026d7285Schristos 
106c74ad251Schristos static const char *
107c74ad251Schristos ForCES_node(uint32_t node)
108026d7285Schristos {
109026d7285Schristos 	if (node <= 0x3FFFFFFF)
110026d7285Schristos 		return "FE";
111026d7285Schristos 	if (node >= 0x40000000 && node <= 0x7FFFFFFF)
112026d7285Schristos 		return "CE";
113026d7285Schristos 	if (node >= 0xC0000000 && node <= 0xFFFFFFEF)
114026d7285Schristos 		return "AllMulticast";
115026d7285Schristos 	if (node == 0xFFFFFFFD)
116026d7285Schristos 		return "AllCEsBroadcast";
117026d7285Schristos 	if (node == 0xFFFFFFFE)
118026d7285Schristos 		return "AllFEsBroadcast";
119026d7285Schristos 	if (node == 0xFFFFFFFF)
120026d7285Schristos 		return "AllBroadcast";
121026d7285Schristos 
122026d7285Schristos 	return "ForCESreserved";
123026d7285Schristos 
124026d7285Schristos }
125026d7285Schristos 
126b3a00663Schristos static const struct tok ForCES_ACKs[] = {
127b3a00663Schristos 	{0x0, "NoACK"},
128b3a00663Schristos 	{0x1, "SuccessACK"},
129b3a00663Schristos 	{0x2, "FailureACK"},
130b3a00663Schristos 	{0x3, "AlwaysACK"},
131b3a00663Schristos 	{0, NULL}
132b3a00663Schristos };
133026d7285Schristos 
134b3a00663Schristos static const struct tok ForCES_EMs[] = {
135b3a00663Schristos 	{0x0, "EMReserved"},
136b3a00663Schristos 	{0x1, "execute-all-or-none"},
137b3a00663Schristos 	{0x2, "execute-until-failure"},
138b3a00663Schristos 	{0x3, "continue-execute-on-failure"},
139b3a00663Schristos 	{0, NULL}
140b3a00663Schristos };
141026d7285Schristos 
142b3a00663Schristos static const struct tok ForCES_ATs[] = {
143b3a00663Schristos 	{0x0, "Standalone"},
144b3a00663Schristos 	{0x1, "2PCtransaction"},
145b3a00663Schristos 	{0, NULL}
146b3a00663Schristos };
147026d7285Schristos 
148b3a00663Schristos static const struct tok ForCES_TPs[] = {
149b3a00663Schristos 	{0x0, "StartofTransaction"},
150b3a00663Schristos 	{0x1, "MiddleofTransaction"},
151b3a00663Schristos 	{0x2, "EndofTransaction"},
152b3a00663Schristos 	{0x3, "abort"},
153b3a00663Schristos 	{0, NULL}
154b3a00663Schristos };
155026d7285Schristos 
156026d7285Schristos /*
157026d7285Schristos  * Structure of forces header, naked of TLVs.
158026d7285Schristos  */
159026d7285Schristos struct forcesh {
160dc860a36Sspz 	nd_uint8_t fm_vrsvd;	/* version and reserved */
161c74ad251Schristos #define ForCES_V(forcesh)	(GET_U_1((forcesh)->fm_vrsvd) >> 4)
162dc860a36Sspz 	nd_uint8_t fm_tom;	/* type of message */
163dc860a36Sspz 	nd_uint16_t fm_len;	/* total length * 4 bytes */
164c74ad251Schristos #define ForCES_BLN(forcesh)	((uint32_t)(GET_BE_U_2((forcesh)->fm_len) << 2))
165dc860a36Sspz 	nd_uint32_t fm_sid;	/* Source ID */
166c74ad251Schristos #define ForCES_SID(forcesh)	GET_BE_U_4((forcesh)->fm_sid)
167dc860a36Sspz 	nd_uint32_t fm_did;	/* Destination ID */
168c74ad251Schristos #define ForCES_DID(forcesh)	GET_BE_U_4((forcesh)->fm_did)
169dc860a36Sspz 	nd_uint8_t fm_cor[8];	/* correlator */
170dc860a36Sspz 	nd_uint32_t fm_flags;	/* flags */
171c74ad251Schristos #define ForCES_ACK(forcesh)	((GET_BE_U_4((forcesh)->fm_flags)&0xC0000000) >> 30)
172c74ad251Schristos #define ForCES_PRI(forcesh)	((GET_BE_U_4((forcesh)->fm_flags)&0x38000000) >> 27)
173c74ad251Schristos #define ForCES_RS1(forcesh)	((GET_BE_U_4((forcesh)->fm_flags)&0x07000000) >> 24)
174c74ad251Schristos #define ForCES_EM(forcesh)	((GET_BE_U_4((forcesh)->fm_flags)&0x00C00000) >> 22)
175c74ad251Schristos #define ForCES_AT(forcesh)	((GET_BE_U_4((forcesh)->fm_flags)&0x00200000) >> 21)
176c74ad251Schristos #define ForCES_TP(forcesh)	((GET_BE_U_4((forcesh)->fm_flags)&0x00180000) >> 19)
177c74ad251Schristos #define ForCES_RS2(forcesh)	((GET_BE_U_4((forcesh)->fm_flags)&0x0007FFFF) >> 0)
178026d7285Schristos };
179026d7285Schristos 
180026d7285Schristos #define ForCES_HLN_VALID(fhl,tlen) ((tlen) >= ForCES_HDRL && \
181026d7285Schristos 				   (fhl) >= ForCES_HDRL && \
182026d7285Schristos 				   (fhl) == (tlen))
183026d7285Schristos 
184026d7285Schristos #define F_LFB_RSVD 0x0
185026d7285Schristos #define F_LFB_FEO 0x1
186026d7285Schristos #define F_LFB_FEPO 0x2
187026d7285Schristos static const struct tok ForCES_LFBs[] = {
188026d7285Schristos 	{F_LFB_RSVD, "Invalid TLV"},
189026d7285Schristos 	{F_LFB_FEO, "FEObj LFB"},
190026d7285Schristos 	{F_LFB_FEPO, "FEProtoObj LFB"},
191026d7285Schristos 	{0, NULL}
192026d7285Schristos };
193026d7285Schristos 
194ba2ff121Schristos /* this is defined in RFC5810 section A.2 */
195c74ad251Schristos /*   https://www.iana.org/assignments/forces/forces.xhtml#oper-tlv-types */
196026d7285Schristos enum {
197ba2ff121Schristos 	F_OP_RSV        = 0,
198ba2ff121Schristos 	F_OP_SET        = 1,
199ba2ff121Schristos 	F_OP_SETPROP    = 2,
200ba2ff121Schristos 	F_OP_SETRESP    = 3,
201ba2ff121Schristos 	F_OP_SETPRESP   = 4,
202ba2ff121Schristos 	F_OP_DEL        = 5,
203ba2ff121Schristos 	F_OP_DELRESP    = 6,
204ba2ff121Schristos 	F_OP_GET        = 7,
205ba2ff121Schristos 	F_OP_GETPROP    = 8,
206ba2ff121Schristos 	F_OP_GETRESP    = 9,
207ba2ff121Schristos 	F_OP_GETPRESP   = 10,
208ba2ff121Schristos 	F_OP_REPORT     = 11,
209ba2ff121Schristos 	F_OP_COMMIT     = 12,
210ba2ff121Schristos 	F_OP_RCOMMIT    = 13,
211ba2ff121Schristos 	F_OP_RTRCOMP    = 14,
212026d7285Schristos 	_F_OP_MAX
213026d7285Schristos };
214026d7285Schristos #define F_OP_MAX	(_F_OP_MAX - 1)
215ba2ff121Schristos 
216026d7285Schristos enum {
217026d7285Schristos 	B_OP_SET = 1 << (F_OP_SET - 1),
218026d7285Schristos 	B_OP_SETPROP = 1 << (F_OP_SETPROP - 1),
219026d7285Schristos 	B_OP_SETRESP = 1 << (F_OP_SETRESP - 1),
220026d7285Schristos 	B_OP_SETPRESP = 1 << (F_OP_SETPRESP - 1),
221026d7285Schristos 	B_OP_DEL = 1 << (F_OP_DEL - 1),
222026d7285Schristos 	B_OP_DELRESP = 1 << (F_OP_DELRESP - 1),
223026d7285Schristos 	B_OP_GET = 1 << (F_OP_GET - 1),
224026d7285Schristos 	B_OP_GETPROP = 1 << (F_OP_GETPROP - 1),
225026d7285Schristos 	B_OP_GETRESP = 1 << (F_OP_GETRESP - 1),
226026d7285Schristos 	B_OP_GETPRESP = 1 << (F_OP_GETPRESP - 1),
227026d7285Schristos 	B_OP_REPORT = 1 << (F_OP_REPORT - 1),
228026d7285Schristos 	B_OP_COMMIT = 1 << (F_OP_COMMIT - 1),
229026d7285Schristos 	B_OP_RCOMMIT = 1 << (F_OP_RCOMMIT - 1),
230fdccd7e4Schristos 	B_OP_RTRCOMP = 1 << (F_OP_RTRCOMP - 1)
231026d7285Schristos };
232026d7285Schristos 
233026d7285Schristos struct optlv_h {
234b3a00663Schristos 	uint16_t flags;
235b3a00663Schristos 	uint16_t op_msk;
236026d7285Schristos 	const char *s;
237c74ad251Schristos 	int (*print) (netdissect_options *ndo, const u_char * pptr, u_int len,
238b3a00663Schristos 		      uint16_t op_msk, int indent);
239026d7285Schristos };
240026d7285Schristos 
241c74ad251Schristos static int genoptlv_print(netdissect_options *, const u_char * pptr, u_int len,
242b3a00663Schristos 			 uint16_t op_msk, int indent);
243c74ad251Schristos static int recpdoptlv_print(netdissect_options *, const u_char * pptr, u_int len,
244b3a00663Schristos 			    uint16_t op_msk, int indent);
245c74ad251Schristos static int invoptlv_print(netdissect_options *, const u_char * pptr, u_int len,
246b3a00663Schristos 			  uint16_t op_msk, int indent);
247026d7285Schristos 
248026d7285Schristos #define OP_MIN_SIZ 8
249026d7285Schristos struct pathdata_h {
250dc860a36Sspz 	nd_uint16_t pflags;
251dc860a36Sspz 	nd_uint16_t pIDcnt;
252026d7285Schristos };
253026d7285Schristos 
254026d7285Schristos #define	B_FULLD		0x1
255026d7285Schristos #define	B_SPARD		0x2
256026d7285Schristos #define B_RESTV		0x4
257026d7285Schristos #define B_KEYIN		0x8
258026d7285Schristos #define B_APPND		0x10
259026d7285Schristos #define B_TRNG		0x20
260026d7285Schristos 
261026d7285Schristos static const struct optlv_h OPTLV_msg[F_OP_MAX + 1] = {
262026d7285Schristos 	/* F_OP_RSV */ {ZERO_TTLV, 0, "Invalid OPTLV", invoptlv_print},
263026d7285Schristos 	/* F_OP_SET */ {TTLV_T2, B_FULLD | B_SPARD, " Set", recpdoptlv_print},
264026d7285Schristos 	/* F_OP_SETPROP */
265026d7285Schristos 	    {TTLV_T2, B_FULLD | B_SPARD, " SetProp", recpdoptlv_print},
266026d7285Schristos 	/* F_OP_SETRESP */ {TTLV_T2, B_RESTV, " SetResp", recpdoptlv_print},
267026d7285Schristos 	/* F_OP_SETPRESP */ {TTLV_T2, B_RESTV, " SetPropResp", recpdoptlv_print},
268026d7285Schristos 	/* F_OP_DEL */ {ZERO_TTLV, 0, " Del", recpdoptlv_print},
269026d7285Schristos 	/* F_OP_DELRESP */ {TTLV_T2, B_RESTV, " DelResp", recpdoptlv_print},
270026d7285Schristos 	/* F_OP_GET */ {ZERO_TTLV, 0, " Get", recpdoptlv_print},
271026d7285Schristos 	/* F_OP_GETPROP */ {ZERO_TTLV, 0, " GetProp", recpdoptlv_print},
272026d7285Schristos 	/* F_OP_GETRESP */
273026d7285Schristos 	    {TTLV_T2, B_FULLD | B_SPARD | B_RESTV, " GetResp", recpdoptlv_print},
274026d7285Schristos 	/* F_OP_GETPRESP */
275026d7285Schristos 	    {TTLV_T2, B_FULLD | B_RESTV, " GetPropResp", recpdoptlv_print},
276026d7285Schristos 	/* F_OP_REPORT */
277026d7285Schristos 	    {TTLV_T2, B_FULLD | B_SPARD, " Report", recpdoptlv_print},
278026d7285Schristos 	/* F_OP_COMMIT */ {ZERO_TTLV, 0, " Commit", NULL},
279026d7285Schristos 	/* F_OP_RCOMMIT */ {TTLV_T1, B_RESTV, " RCommit", genoptlv_print},
280026d7285Schristos 	/* F_OP_RTRCOMP */ {ZERO_TTLV, 0, " RTRCOMP", NULL},
281026d7285Schristos };
282026d7285Schristos 
283c74ad251Schristos static const struct optlv_h *
284c74ad251Schristos get_forces_optlv_h(uint16_t opt)
285026d7285Schristos {
286c74ad251Schristos 	if (opt > F_OP_MAX || opt == F_OP_RSV)
287026d7285Schristos 		return &OPTLV_msg[F_OP_RSV];
288026d7285Schristos 
289026d7285Schristos 	return &OPTLV_msg[opt];
290026d7285Schristos }
291026d7285Schristos 
292026d7285Schristos #define IND_SIZE 256
293026d7285Schristos #define IND_CHR ' '
294026d7285Schristos #define IND_PREF '\n'
295026d7285Schristos #define IND_SUF 0x0
296dc860a36Sspz static char ind_buf[IND_SIZE];
297026d7285Schristos 
298c74ad251Schristos static char *
299c74ad251Schristos indent_pr(int indent, int nlpref)
300026d7285Schristos {
301026d7285Schristos 	int i = 0;
302026d7285Schristos 	char *r = ind_buf;
303026d7285Schristos 
304026d7285Schristos 	if (indent > (IND_SIZE - 1))
305026d7285Schristos 		indent = IND_SIZE - 1;
306026d7285Schristos 
307026d7285Schristos 	if (nlpref) {
308026d7285Schristos 		r[i] = IND_PREF;
309026d7285Schristos 		i++;
310026d7285Schristos 		indent--;
311026d7285Schristos 	}
312026d7285Schristos 
313026d7285Schristos 	while (--indent >= 0)
314026d7285Schristos 		r[i++] = IND_CHR;
315026d7285Schristos 
316026d7285Schristos 	r[i] = IND_SUF;
317026d7285Schristos 	return r;
318026d7285Schristos }
319026d7285Schristos 
320c74ad251Schristos static int
321c74ad251Schristos op_valid(uint16_t op, uint16_t mask)
322026d7285Schristos {
323026d7285Schristos 	if (op == 0)
324026d7285Schristos 		return 0;
325c74ad251Schristos 	if (op <= F_OP_MAX)
326c74ad251Schristos 		return (1 << (op - 1)) & mask; /* works only for 0x0001 through 0x0010 */
327026d7285Schristos 	/* I guess we should allow vendor operations? */
328026d7285Schristos 	if (op >= 0x8000)
329026d7285Schristos 		return 1;
330026d7285Schristos 	return 0;
331026d7285Schristos }
332026d7285Schristos 
333026d7285Schristos #define F_TLV_RSVD	0x0000
334026d7285Schristos #define F_TLV_REDR	0x0001
335026d7285Schristos #define F_TLV_ASRS	0x0010
336026d7285Schristos #define F_TLV_ASRT	0x0011
337026d7285Schristos #define F_TLV_LFBS	0x1000
338026d7285Schristos #define F_TLV_PDAT	0x0110
339026d7285Schristos #define F_TLV_KEYI	0x0111
340026d7285Schristos #define F_TLV_FULD	0x0112
341026d7285Schristos #define F_TLV_SPAD	0x0113
342026d7285Schristos #define F_TLV_REST	0x0114
343026d7285Schristos #define F_TLV_METD	0x0115
344026d7285Schristos #define F_TLV_REDD	0x0116
345026d7285Schristos #define F_TLV_TRNG	0x0117
346026d7285Schristos 
347026d7285Schristos 
348026d7285Schristos #define F_TLV_VNST	0x8000
349026d7285Schristos 
350026d7285Schristos static const struct tok ForCES_TLV[] = {
351026d7285Schristos 	{F_TLV_RSVD, "Invalid TLV"},
352026d7285Schristos 	{F_TLV_REDR, "REDIRECT TLV"},
353026d7285Schristos 	{F_TLV_ASRS, "ASResult TLV"},
354026d7285Schristos 	{F_TLV_ASRT, "ASTreason TLV"},
355026d7285Schristos 	{F_TLV_LFBS, "LFBselect TLV"},
356026d7285Schristos 	{F_TLV_PDAT, "PATH-DATA TLV"},
357026d7285Schristos 	{F_TLV_KEYI, "KEYINFO TLV"},
358026d7285Schristos 	{F_TLV_FULD, "FULLDATA TLV"},
359026d7285Schristos 	{F_TLV_SPAD, "SPARSEDATA TLV"},
360026d7285Schristos 	{F_TLV_REST, "RESULT TLV"},
361026d7285Schristos 	{F_TLV_METD, "METADATA TLV"},
362026d7285Schristos 	{F_TLV_REDD, "REDIRECTDATA TLV"},
363026d7285Schristos 	{0, NULL}
364026d7285Schristos };
365026d7285Schristos 
366026d7285Schristos #define TLV_HLN	4
367c74ad251Schristos static int
368c74ad251Schristos ttlv_valid(uint16_t ttlv)
369026d7285Schristos {
370026d7285Schristos 	if (ttlv > 0) {
371026d7285Schristos 		if (ttlv == 1 || ttlv == 0x1000)
372026d7285Schristos 			return 1;
373026d7285Schristos 		if (ttlv >= 0x10 && ttlv <= 0x11)
374026d7285Schristos 			return 1;
375026d7285Schristos 		if (ttlv >= 0x110 && ttlv <= 0x116)
376026d7285Schristos 			return 1;
377026d7285Schristos 		if (ttlv >= 0x8000)
378026d7285Schristos 			return 0;	/* XXX: */
379026d7285Schristos 	}
380026d7285Schristos 
381026d7285Schristos 	return 0;
382026d7285Schristos }
383026d7285Schristos 
384026d7285Schristos struct forces_ilv {
385dc860a36Sspz 	nd_uint32_t type;
386dc860a36Sspz 	nd_uint32_t length;
387026d7285Schristos };
388026d7285Schristos 
389026d7285Schristos struct forces_tlv {
390dc860a36Sspz 	nd_uint16_t type;
391dc860a36Sspz 	nd_uint16_t length;
392026d7285Schristos };
393026d7285Schristos 
394c74ad251Schristos #define F_ALN_LEN(len) roundup2(len, ForCES_ALNL)
395fdccd7e4Schristos #define	GET_TOP_TLV(fhdr) ((const struct forces_tlv *)((fhdr) + sizeof (struct forcesh)))
396026d7285Schristos #define TLV_SET_LEN(len)  (F_ALN_LEN(TLV_HDRL) + (len))
397fdccd7e4Schristos #define TLV_DATA(tlvp)   ((const void*)(((const char*)(tlvp)) + TLV_SET_LEN(0)))
398c74ad251Schristos #define GO_NXT_TLV(tlv,rlen) ((rlen) -= F_ALN_LEN(GET_BE_U_2((tlv)->length)), \
399fdccd7e4Schristos 		              (const struct forces_tlv*)(((const char*)(tlv)) \
400c74ad251Schristos 				      + F_ALN_LEN(GET_BE_U_2((tlv)->length))))
401026d7285Schristos #define ILV_SET_LEN(len)  (F_ALN_LEN(ILV_HDRL) + (len))
402fdccd7e4Schristos #define ILV_DATA(ilvp)   ((const void*)(((const char*)(ilvp)) + ILV_SET_LEN(0)))
403c74ad251Schristos #define GO_NXT_ILV(ilv,rlen) ((rlen) -= F_ALN_LEN(GET_BE_U_4((ilv)->length)), \
404fdccd7e4Schristos 		              (const struct forces_ilv *)(((const char*)(ilv)) \
405c74ad251Schristos 				      + F_ALN_LEN(GET_BE_U_4((ilv)->length))))
406b3a00663Schristos #define INVALID_RLEN 1
407b3a00663Schristos #define INVALID_STLN 2
408b3a00663Schristos #define INVALID_LTLN 3
409b3a00663Schristos #define INVALID_ALEN 4
410026d7285Schristos 
411026d7285Schristos static const struct tok ForCES_TLV_err[] = {
412026d7285Schristos 	{INVALID_RLEN, "Invalid total length"},
413026d7285Schristos 	{INVALID_STLN, "xLV too short"},
414026d7285Schristos 	{INVALID_LTLN, "xLV too long"},
415026d7285Schristos 	{INVALID_ALEN, "data padding missing"},
416026d7285Schristos 	{0, NULL}
417026d7285Schristos };
418026d7285Schristos 
419c74ad251Schristos static u_int
420c74ad251Schristos tlv_valid(u_int tlvl, u_int rlen)
421026d7285Schristos {
422026d7285Schristos 	if (rlen < TLV_HDRL)
423026d7285Schristos 		return INVALID_RLEN;
424c74ad251Schristos 	if (tlvl < TLV_HDRL)
425026d7285Schristos 		return INVALID_STLN;
426c74ad251Schristos 	if (tlvl > rlen)
427026d7285Schristos 		return INVALID_LTLN;
428c74ad251Schristos 	if (rlen < F_ALN_LEN(tlvl))
429026d7285Schristos 		return INVALID_ALEN;
430026d7285Schristos 
431026d7285Schristos 	return 0;
432026d7285Schristos }
433026d7285Schristos 
434c74ad251Schristos static int
435c74ad251Schristos ilv_valid(netdissect_options *ndo, const struct forces_ilv *ilv, u_int rlen)
436026d7285Schristos {
437026d7285Schristos 	if (rlen < ILV_HDRL)
438026d7285Schristos 		return INVALID_RLEN;
439c74ad251Schristos 	if (GET_BE_U_4(ilv->length) < ILV_HDRL)
440026d7285Schristos 		return INVALID_STLN;
441c74ad251Schristos 	if (GET_BE_U_4(ilv->length) > rlen)
442026d7285Schristos 		return INVALID_LTLN;
443c74ad251Schristos 	if (rlen < F_ALN_LEN(GET_BE_U_4(ilv->length)))
444026d7285Schristos 		return INVALID_ALEN;
445026d7285Schristos 
446026d7285Schristos 	return 0;
447026d7285Schristos }
448026d7285Schristos 
449c74ad251Schristos static int lfbselect_print(netdissect_options *, const u_char * pptr, u_int len,
450b3a00663Schristos 			   uint16_t op_msk, int indent);
451c74ad251Schristos static int redirect_print(netdissect_options *, const u_char * pptr, u_int len,
452b3a00663Schristos 			  uint16_t op_msk, int indent);
453c74ad251Schristos static int asrtlv_print(netdissect_options *, const u_char * pptr, u_int len,
454b3a00663Schristos 			uint16_t op_msk, int indent);
455c74ad251Schristos static int asttlv_print(netdissect_options *, const u_char * pptr, u_int len,
456b3a00663Schristos 			uint16_t op_msk, int indent);
457026d7285Schristos 
458026d7285Schristos struct forces_lfbsh {
459dc860a36Sspz 	nd_uint32_t class;
460dc860a36Sspz 	nd_uint32_t instance;
461026d7285Schristos };
462026d7285Schristos 
463026d7285Schristos #define ASSNS_OPS (B_OP_REPORT)
464026d7285Schristos #define CFG_OPS	(B_OP_SET|B_OP_SETPROP|B_OP_DEL|B_OP_COMMIT|B_OP_RTRCOMP)
465026d7285Schristos #define CFG_ROPS (B_OP_SETRESP|B_OP_SETPRESP|B_OP_DELRESP|B_OP_RCOMMIT)
466026d7285Schristos #define CFG_QY (B_OP_GET|B_OP_GETPROP)
467026d7285Schristos #define CFG_QYR (B_OP_GETRESP|B_OP_GETPRESP)
468026d7285Schristos #define CFG_EVN (B_OP_REPORT)
469026d7285Schristos 
470026d7285Schristos static const struct tom_h ForCES_msg[TOM_MAX_IND + 1] = {
471026d7285Schristos 	/* TOM_RSV_I */ {TOM_RSVD, ZERO_TTLV, 0, "Invalid message", NULL},
472026d7285Schristos 	/* TOM_ASS_I */ {TOM_ASSNSETUP, ZERO_MORE_TTLV | TWO_TLV, ASSNS_OPS,
473026d7285Schristos 		       "Association Setup", lfbselect_print},
474026d7285Schristos 	/* TOM_AST_I */
475026d7285Schristos 	    {TOM_ASSNTEARD, TTLV_T1, 0, "Association TearDown", asttlv_print},
476026d7285Schristos 	/* TOM_CFG_I */ {TOM_CONFIG, TTLV_T2, CFG_OPS, "Config", lfbselect_print},
477026d7285Schristos 	/* TOM_QRY_I */ {TOM_QUERY, TTLV_T2, CFG_QY, "Query", lfbselect_print},
478026d7285Schristos 	/* TOM_EVN_I */ {TOM_EVENTNOT, TTLV_T1, CFG_EVN, "Event Notification",
479026d7285Schristos 		       lfbselect_print},
480026d7285Schristos 	/* TOM_RED_I */
481026d7285Schristos 	    {TOM_PKTREDIR, TTLV_T2, 0, "Packet Redirect", redirect_print},
482026d7285Schristos 	/* TOM_HBT_I */ {TOM_HEARTBT, ZERO_TTLV, 0, "HeartBeat", NULL},
483026d7285Schristos 	/* TOM_ASR_I */
484026d7285Schristos 	    {TOM_ASSNSETREP, TTLV_T1, 0, "Association Response", asrtlv_print},
485026d7285Schristos 	/* TOM_CNR_I */ {TOM_CONFIGREP, TTLV_T2, CFG_ROPS, "Config Response",
486026d7285Schristos 		       lfbselect_print},
487026d7285Schristos 	/* TOM_QRR_I */
488026d7285Schristos 	    {TOM_QUERYREP, TTLV_T2, CFG_QYR, "Query Response", lfbselect_print},
489026d7285Schristos };
490026d7285Schristos 
491c74ad251Schristos static const struct tom_h *
492c74ad251Schristos get_forces_tom(uint8_t tom)
493026d7285Schristos {
494026d7285Schristos 	int i;
495026d7285Schristos 	for (i = TOM_RSV_I; i <= TOM_MAX_IND; i++) {
496026d7285Schristos 		const struct tom_h *th = &ForCES_msg[i];
497026d7285Schristos 		if (th->v == tom)
498026d7285Schristos 			return th;
499026d7285Schristos 	}
500026d7285Schristos 	return &ForCES_msg[TOM_RSV_I];
501026d7285Schristos }
502026d7285Schristos 
503026d7285Schristos struct pdata_ops {
504b3a00663Schristos 	uint32_t v;
505b3a00663Schristos 	uint16_t flags;
506b3a00663Schristos 	uint16_t op_msk;
507026d7285Schristos 	const char *s;
508c74ad251Schristos 	int (*print) (netdissect_options *, const u_char * pptr, u_int len,
509b3a00663Schristos 		      uint16_t op_msk, int indent);
510026d7285Schristos };
511026d7285Schristos 
512026d7285Schristos enum {
513026d7285Schristos 	PD_RSV_I,
514026d7285Schristos 	PD_SEL_I,
515026d7285Schristos 	PD_FDT_I,
516026d7285Schristos 	PD_SDT_I,
517026d7285Schristos 	PD_RES_I,
518026d7285Schristos 	PD_PDT_I,
519026d7285Schristos 	_PD_RSV_MAX
520026d7285Schristos };
521026d7285Schristos #define PD_MAX_IND (_TOM_RSV_MAX - 1)
522026d7285Schristos 
523c74ad251Schristos static int
524c74ad251Schristos pd_valid(uint16_t pd)
525026d7285Schristos {
526026d7285Schristos 	if (pd >= F_TLV_PDAT && pd <= F_TLV_REST)
527026d7285Schristos 		return 1;
528026d7285Schristos 	return 0;
529026d7285Schristos }
530026d7285Schristos 
531c74ad251Schristos static void
532b3a00663Schristos chk_op_type(netdissect_options *ndo,
533b3a00663Schristos             uint16_t type, uint16_t msk, uint16_t omsk)
534026d7285Schristos {
535026d7285Schristos 	if (type != F_TLV_PDAT) {
536026d7285Schristos 		if (msk & B_KEYIN) {
537026d7285Schristos 			if (type != F_TLV_KEYI) {
538c74ad251Schristos 				ND_PRINT("Based on flags expected KEYINFO TLV!\n");
539026d7285Schristos 			}
540026d7285Schristos 		} else {
541026d7285Schristos 			if (!(msk & omsk)) {
542c74ad251Schristos 				ND_PRINT("Illegal DATA encoding for type 0x%x programmed %x got %x\n",
543c74ad251Schristos 				          type, omsk, msk);
544026d7285Schristos 			}
545026d7285Schristos 		}
546026d7285Schristos 	}
547026d7285Schristos 
548026d7285Schristos }
549026d7285Schristos 
550026d7285Schristos #define F_SELKEY 1
551026d7285Schristos #define F_SELTABRANGE 2
552026d7285Schristos #define F_TABAPPEND 4
553026d7285Schristos 
554026d7285Schristos struct res_val {
555dc860a36Sspz 	nd_uint8_t result;
556dc860a36Sspz 	nd_uint8_t resv1;
557dc860a36Sspz 	nd_uint16_t resv2;
558026d7285Schristos };
559026d7285Schristos 
560c74ad251Schristos static int prestlv_print(netdissect_options *, const u_char * pptr, u_int len,
561b3a00663Schristos 			 uint16_t op_msk, int indent);
562c74ad251Schristos static int pkeyitlv_print(netdissect_options *, const u_char * pptr, u_int len,
563b3a00663Schristos 			  uint16_t op_msk, int indent);
564c74ad251Schristos static int fdatatlv_print(netdissect_options *, const u_char * pptr, u_int len,
565b3a00663Schristos 			  uint16_t op_msk, int indent);
566c74ad251Schristos static int sdatatlv_print(netdissect_options *, const u_char * pptr, u_int len,
567b3a00663Schristos 			  uint16_t op_msk, int indent);
568026d7285Schristos 
569026d7285Schristos static const struct pdata_ops ForCES_pdata[PD_MAX_IND + 1] = {
570026d7285Schristos 	/* PD_RSV_I */ {0, 0, 0, "Invalid message", NULL},
571026d7285Schristos 	/* PD_SEL_I */ {F_TLV_KEYI, 0, 0, "KEYINFO TLV", pkeyitlv_print},
572026d7285Schristos 	/* PD_FDT_I */ {F_TLV_FULD, 0, B_FULLD, "FULLDATA TLV", fdatatlv_print},
573026d7285Schristos 	/* PD_SDT_I */ {F_TLV_SPAD, 0, B_SPARD, "SPARSEDATA TLV", sdatatlv_print},
574026d7285Schristos 	/* PD_RES_I */ {F_TLV_REST, 0, B_RESTV, "RESULT TLV", prestlv_print},
575026d7285Schristos 	/* PD_PDT_I */
576026d7285Schristos 	    {F_TLV_PDAT, 0, 0, "Inner PATH-DATA TLV", recpdoptlv_print},
577026d7285Schristos };
578026d7285Schristos 
579c74ad251Schristos static const struct pdata_ops *
580c74ad251Schristos get_forces_pd(uint16_t pd)
581026d7285Schristos {
582026d7285Schristos 	int i;
583026d7285Schristos 	for (i = PD_RSV_I + 1; i <= PD_MAX_IND; i++) {
584026d7285Schristos 		const struct pdata_ops *pdo = &ForCES_pdata[i];
585026d7285Schristos 		if (pdo->v == pd)
586026d7285Schristos 			return pdo;
587026d7285Schristos 	}
588026d7285Schristos 	return &ForCES_pdata[TOM_RSV_I];
589026d7285Schristos }
590026d7285Schristos 
591026d7285Schristos enum {
592026d7285Schristos 	E_SUCCESS,
593026d7285Schristos 	E_INVALID_HEADER,
594026d7285Schristos 	E_LENGTH_MISMATCH,
595026d7285Schristos 	E_VERSION_MISMATCH,
596026d7285Schristos 	E_INVALID_DESTINATION_PID,
597026d7285Schristos 	E_LFB_UNKNOWN,
598026d7285Schristos 	E_LFB_NOT_FOUND,
599026d7285Schristos 	E_LFB_INSTANCE_ID_NOT_FOUND,
600026d7285Schristos 	E_INVALID_PATH,
601026d7285Schristos 	E_COMPONENT_DOES_NOT_EXIST,
602026d7285Schristos 	E_EXISTS,
603026d7285Schristos 	E_NOT_FOUND,
604026d7285Schristos 	E_READ_ONLY,
605026d7285Schristos 	E_INVALID_ARRAY_CREATION,
606026d7285Schristos 	E_VALUE_OUT_OF_RANGE,
607026d7285Schristos 	E_CONTENTS_TOO_LONG,
608026d7285Schristos 	E_INVALID_PARAMETERS,
609026d7285Schristos 	E_INVALID_MESSAGE_TYPE,
610026d7285Schristos 	E_INVALID_FLAGS,
611026d7285Schristos 	E_INVALID_TLV,
612026d7285Schristos 	E_EVENT_ERROR,
613026d7285Schristos 	E_NOT_SUPPORTED,
614026d7285Schristos 	E_MEMORY_ERROR,
615026d7285Schristos 	E_INTERNAL_ERROR,
616026d7285Schristos 	/* 0x18-0xFE are reserved .. */
617026d7285Schristos 	E_UNSPECIFIED_ERROR = 0XFF
618026d7285Schristos };
619026d7285Schristos 
620026d7285Schristos static const struct tok ForCES_errs[] = {
621026d7285Schristos 	{E_SUCCESS, "SUCCESS"},
622026d7285Schristos 	{E_INVALID_HEADER, "INVALID HEADER"},
623026d7285Schristos 	{E_LENGTH_MISMATCH, "LENGTH MISMATCH"},
624026d7285Schristos 	{E_VERSION_MISMATCH, "VERSION MISMATCH"},
625026d7285Schristos 	{E_INVALID_DESTINATION_PID, "INVALID DESTINATION PID"},
626026d7285Schristos 	{E_LFB_UNKNOWN, "LFB UNKNOWN"},
627026d7285Schristos 	{E_LFB_NOT_FOUND, "LFB NOT FOUND"},
628026d7285Schristos 	{E_LFB_INSTANCE_ID_NOT_FOUND, "LFB INSTANCE ID NOT FOUND"},
629026d7285Schristos 	{E_INVALID_PATH, "INVALID PATH"},
630026d7285Schristos 	{E_COMPONENT_DOES_NOT_EXIST, "COMPONENT DOES NOT EXIST"},
631026d7285Schristos 	{E_EXISTS, "EXISTS ALREADY"},
632026d7285Schristos 	{E_NOT_FOUND, "NOT FOUND"},
633026d7285Schristos 	{E_READ_ONLY, "READ ONLY"},
634026d7285Schristos 	{E_INVALID_ARRAY_CREATION, "INVALID ARRAY CREATION"},
635026d7285Schristos 	{E_VALUE_OUT_OF_RANGE, "VALUE OUT OF RANGE"},
636026d7285Schristos 	{E_CONTENTS_TOO_LONG, "CONTENTS TOO LONG"},
637026d7285Schristos 	{E_INVALID_PARAMETERS, "INVALID PARAMETERS"},
638026d7285Schristos 	{E_INVALID_MESSAGE_TYPE, "INVALID MESSAGE TYPE"},
639026d7285Schristos 	{E_INVALID_FLAGS, "INVALID FLAGS"},
640026d7285Schristos 	{E_INVALID_TLV, "INVALID TLV"},
641026d7285Schristos 	{E_EVENT_ERROR, "EVENT ERROR"},
642026d7285Schristos 	{E_NOT_SUPPORTED, "NOT SUPPORTED"},
643026d7285Schristos 	{E_MEMORY_ERROR, "MEMORY ERROR"},
644026d7285Schristos 	{E_INTERNAL_ERROR, "INTERNAL ERROR"},
645026d7285Schristos 	{E_UNSPECIFIED_ERROR, "UNSPECIFIED ERROR"},
646026d7285Schristos 	{0, NULL}
647026d7285Schristos };
6480f74e101Schristos 
6490f74e101Schristos #define RESLEN	4
6500f74e101Schristos 
651026d7285Schristos static int
652b3a00663Schristos prestlv_print(netdissect_options *ndo,
653c74ad251Schristos               const u_char * pptr, u_int len,
654b3a00663Schristos               uint16_t op_msk _U_, int indent)
6550f74e101Schristos {
656fdccd7e4Schristos 	const struct forces_tlv *tlv = (const struct forces_tlv *)pptr;
657c74ad251Schristos 	const u_char *tdp = (const u_char *) TLV_DATA(tlv);
658fdccd7e4Schristos 	const struct res_val *r = (const struct res_val *)tdp;
6590f74e101Schristos 	u_int dlen;
660c74ad251Schristos 	uint8_t result;
6610f74e101Schristos 
6620f74e101Schristos 	/*
6630f74e101Schristos 	 * pdatacnt_print() has ensured that len (the TLV length)
6640f74e101Schristos 	 * >= TLV_HDRL.
6650f74e101Schristos 	 */
6660f74e101Schristos 	dlen = len - TLV_HDRL;
6670f74e101Schristos 	if (dlen != RESLEN) {
668c74ad251Schristos 		ND_PRINT("illegal RESULT-TLV: %u bytes!\n", dlen);
6690f74e101Schristos 		return -1;
6700f74e101Schristos 	}
6710f74e101Schristos 
672c74ad251Schristos 	ND_TCHECK_SIZE(r);
673c74ad251Schristos 	result = GET_U_1(r->result);
674c74ad251Schristos 	if (result >= 0x18 && result <= 0xFE) {
675c74ad251Schristos 		ND_PRINT("illegal reserved result code: 0x%x!\n", result);
6760f74e101Schristos 		return -1;
6770f74e101Schristos 	}
6780f74e101Schristos 
679b3a00663Schristos 	if (ndo->ndo_vflag >= 3) {
6800f74e101Schristos 		char *ib = indent_pr(indent, 0);
681c74ad251Schristos 		ND_PRINT("%s  Result: %s (code 0x%x)\n", ib,
682c74ad251Schristos 		       tok2str(ForCES_errs, NULL, result), result);
6830f74e101Schristos 	}
6840f74e101Schristos 	return 0;
6850f74e101Schristos 
6860f74e101Schristos trunc:
687c74ad251Schristos 	nd_print_trunc(ndo);
6880f74e101Schristos 	return -1;
6890f74e101Schristos }
6900f74e101Schristos 
691026d7285Schristos static int
692b3a00663Schristos fdatatlv_print(netdissect_options *ndo,
693c74ad251Schristos                const u_char * pptr, u_int len,
694b3a00663Schristos                uint16_t op_msk _U_, int indent)
6950f74e101Schristos {
696fdccd7e4Schristos 	const struct forces_tlv *tlv = (const struct forces_tlv *)pptr;
6970f74e101Schristos 	u_int rlen;
698c74ad251Schristos 	const u_char *tdp = (const u_char *) TLV_DATA(tlv);
699b3a00663Schristos 	uint16_t type;
7000f74e101Schristos 
7010f74e101Schristos 	/*
7020f74e101Schristos 	 * pdatacnt_print() or pkeyitlv_print() has ensured that len
7030f74e101Schristos 	 * (the TLV length) >= TLV_HDRL.
7040f74e101Schristos 	 */
7050f74e101Schristos 	rlen = len - TLV_HDRL;
706c74ad251Schristos 	ND_TCHECK_SIZE(tlv);
707c74ad251Schristos 	type = GET_BE_U_2(tlv->type);
7080f74e101Schristos 	if (type != F_TLV_FULD) {
709c74ad251Schristos 		ND_PRINT("Error: expecting FULLDATA!\n");
7100f74e101Schristos 		return -1;
7110f74e101Schristos 	}
7120f74e101Schristos 
713b3a00663Schristos 	if (ndo->ndo_vflag >= 3) {
7140f74e101Schristos 		char *ib = indent_pr(indent + 2, 1);
715c74ad251Schristos 		ND_PRINT("%s[", ib + 1);
716c74ad251Schristos 		hex_print(ndo, ib, tdp, rlen);
717c74ad251Schristos 		ND_PRINT("\n%s]", ib + 1);
7180f74e101Schristos 	}
7190f74e101Schristos 	return 0;
7200f74e101Schristos 
7210f74e101Schristos trunc:
722c74ad251Schristos 	nd_print_trunc(ndo);
7230f74e101Schristos 	return -1;
7240f74e101Schristos }
7250f74e101Schristos 
726026d7285Schristos static int
727b3a00663Schristos sdatailv_print(netdissect_options *ndo,
728c74ad251Schristos                const u_char * pptr, u_int len,
729b3a00663Schristos                uint16_t op_msk _U_, int indent)
7300f74e101Schristos {
7310f74e101Schristos 	u_int rlen;
732fdccd7e4Schristos 	const struct forces_ilv *ilv = (const struct forces_ilv *)pptr;
7330f74e101Schristos 	int invilv;
7340f74e101Schristos 
7350f74e101Schristos 	if (len < ILV_HDRL) {
736c74ad251Schristos 		ND_PRINT("Error: BAD SPARSEDATA-TLV!\n");
7370f74e101Schristos 		return -1;
7380f74e101Schristos 	}
7399546e36dSchristos 	rlen = len;
7400f74e101Schristos 	indent += 1;
7410f74e101Schristos 	while (rlen != 0) {
742026d7285Schristos #if 0
743c74ad251Schristos 		ND_PRINT("Jamal - outstanding length <%u>\n", rlen);
744026d7285Schristos #endif
7459546e36dSchristos 		char *ib = indent_pr(indent, 1);
746c74ad251Schristos 		const u_char *tdp = (const u_char *) ILV_DATA(ilv);
747c74ad251Schristos 		invilv = ilv_valid(ndo, ilv, rlen);
7480f74e101Schristos 		if (invilv) {
749c74ad251Schristos 			ND_PRINT("Error: %s, rlen %u\n",
750c74ad251Schristos 			         tok2str(ForCES_TLV_err, NULL, invilv), rlen);
7519546e36dSchristos 			return -1;
7529546e36dSchristos 		}
753b3a00663Schristos 		if (ndo->ndo_vflag >= 3) {
754c74ad251Schristos 			u_int ilvl = GET_BE_U_4(ilv->length);
755c74ad251Schristos 			ND_PRINT("\n%s ILV: type %x length %u\n", ib + 1,
756c74ad251Schristos 				  GET_BE_U_4(ilv->type), ilvl);
757c74ad251Schristos 			hex_print(ndo, "\t\t[", tdp, ilvl-ILV_HDRL);
7580f74e101Schristos 		}
7590f74e101Schristos 
7600f74e101Schristos 		ilv = GO_NXT_ILV(ilv, rlen);
7610f74e101Schristos 	}
7620f74e101Schristos 
7630f74e101Schristos 	return 0;
7640f74e101Schristos }
7650f74e101Schristos 
766026d7285Schristos static int
767b3a00663Schristos sdatatlv_print(netdissect_options *ndo,
768c74ad251Schristos                const u_char * pptr, u_int len,
769b3a00663Schristos                uint16_t op_msk, int indent)
7700f74e101Schristos {
771fdccd7e4Schristos 	const struct forces_tlv *tlv = (const struct forces_tlv *)pptr;
7720f74e101Schristos 	u_int rlen;
773c74ad251Schristos 	const u_char *tdp = (const u_char *) TLV_DATA(tlv);
774b3a00663Schristos 	uint16_t type;
7750f74e101Schristos 
7760f74e101Schristos 	/*
7770f74e101Schristos 	 * pdatacnt_print() has ensured that len (the TLV length)
7780f74e101Schristos 	 * >= TLV_HDRL.
7790f74e101Schristos 	 */
7800f74e101Schristos 	rlen = len - TLV_HDRL;
781c74ad251Schristos 	ND_TCHECK_SIZE(tlv);
782c74ad251Schristos 	type = GET_BE_U_2(tlv->type);
7830f74e101Schristos 	if (type != F_TLV_SPAD) {
784c74ad251Schristos 		ND_PRINT("Error: expecting SPARSEDATA!\n");
7850f74e101Schristos 		return -1;
7860f74e101Schristos 	}
7870f74e101Schristos 
788b3a00663Schristos 	return sdatailv_print(ndo, tdp, rlen, op_msk, indent);
7890f74e101Schristos 
7900f74e101Schristos trunc:
791c74ad251Schristos 	nd_print_trunc(ndo);
7920f74e101Schristos 	return -1;
7930f74e101Schristos }
7940f74e101Schristos 
795026d7285Schristos static int
796b3a00663Schristos pkeyitlv_print(netdissect_options *ndo,
797c74ad251Schristos                const u_char * pptr, u_int len,
798b3a00663Schristos                uint16_t op_msk, int indent)
7990f74e101Schristos {
800fdccd7e4Schristos 	const struct forces_tlv *tlv = (const struct forces_tlv *)pptr;
801c74ad251Schristos 	const u_char *tdp = (const u_char *) TLV_DATA(tlv);
802c74ad251Schristos 	const u_char *dp = tdp + 4;
803fdccd7e4Schristos 	const struct forces_tlv *kdtlv = (const struct forces_tlv *)dp;
804b3a00663Schristos 	uint32_t id;
8050f74e101Schristos 	char *ib = indent_pr(indent, 0);
806b3a00663Schristos 	uint16_t type, tll;
807b3a00663Schristos 	u_int invtlv;
8080f74e101Schristos 
809c74ad251Schristos 	id = GET_BE_U_4(tdp);
810c74ad251Schristos 	ND_PRINT("%sKeyinfo: Key 0x%x\n", ib, id);
811c74ad251Schristos 	type = GET_BE_U_2(kdtlv->type);
812c74ad251Schristos 	tll = GET_BE_U_2(kdtlv->length);
813c74ad251Schristos 	invtlv = tlv_valid(tll, len);
8140f74e101Schristos 
8150f74e101Schristos 	if (invtlv) {
816c74ad251Schristos 		ND_PRINT("%s TLV type 0x%x len %u\n",
8170f74e101Schristos 		       tok2str(ForCES_TLV_err, NULL, invtlv), type,
818c74ad251Schristos 		       tll);
8190f74e101Schristos 		return -1;
8200f74e101Schristos 	}
8210f74e101Schristos 	/*
8220f74e101Schristos 	 * At this point, tlv_valid() has ensured that the TLV
8230f74e101Schristos 	 * length is large enough but not too large (it doesn't
8240f74e101Schristos 	 * go past the end of the containing TLV).
8250f74e101Schristos 	 */
826c74ad251Schristos 	tll = GET_BE_U_2(kdtlv->length);
827fdccd7e4Schristos 	dp = (const u_char *) TLV_DATA(kdtlv);
828b3a00663Schristos 	return fdatatlv_print(ndo, dp, tll, op_msk, indent);
8290f74e101Schristos }
8300f74e101Schristos 
831026d7285Schristos #define PTH_DESC_SIZE 12
832026d7285Schristos 
833026d7285Schristos static int
834b3a00663Schristos pdatacnt_print(netdissect_options *ndo,
835c74ad251Schristos                const u_char * pptr, u_int len,
836b3a00663Schristos                uint16_t IDcnt, uint16_t op_msk, int indent)
8370f74e101Schristos {
8380f74e101Schristos 	u_int i;
839b3a00663Schristos 	uint32_t id;
8400f74e101Schristos 	char *ib = indent_pr(indent, 0);
8410f74e101Schristos 
842b3a00663Schristos 	if ((op_msk & B_APPND) && ndo->ndo_vflag >= 3) {
843c74ad251Schristos 		ND_PRINT("%sTABLE APPEND\n", ib);
844026d7285Schristos 	}
8450f74e101Schristos 	for (i = 0; i < IDcnt; i++) {
846c74ad251Schristos 		ND_TCHECK_4(pptr);
8470f74e101Schristos 		if (len < 4)
8480f74e101Schristos 			goto trunc;
849c74ad251Schristos 		id = GET_BE_U_4(pptr);
850b3a00663Schristos 		if (ndo->ndo_vflag >= 3)
851c74ad251Schristos 			ND_PRINT("%sID#%02u: %u\n", ib, i + 1, id);
8520f74e101Schristos 		len -= 4;
8530f74e101Schristos 		pptr += 4;
8540f74e101Schristos 	}
855026d7285Schristos 
856026d7285Schristos 	if ((op_msk & B_TRNG) || (op_msk & B_KEYIN)) {
857026d7285Schristos 		if (op_msk & B_TRNG) {
858b3a00663Schristos 			uint32_t starti, endi;
859026d7285Schristos 
860026d7285Schristos 			if (len < PTH_DESC_SIZE) {
861c74ad251Schristos 				ND_PRINT("pathlength %u with key/range too short %u\n",
862c74ad251Schristos 				       len, PTH_DESC_SIZE);
863026d7285Schristos 				return -1;
864026d7285Schristos 			}
865026d7285Schristos 
866026d7285Schristos 			pptr += sizeof(struct forces_tlv);
867026d7285Schristos 			len -= sizeof(struct forces_tlv);
868026d7285Schristos 
869c74ad251Schristos 			starti = GET_BE_U_4(pptr);
870026d7285Schristos 			pptr += 4;
871026d7285Schristos 			len -= 4;
872026d7285Schristos 
873c74ad251Schristos 			endi = GET_BE_U_4(pptr);
874026d7285Schristos 			pptr += 4;
875026d7285Schristos 			len -= 4;
876026d7285Schristos 
877b3a00663Schristos 			if (ndo->ndo_vflag >= 3)
878c74ad251Schristos 				ND_PRINT("%sTable range: [%u,%u]\n", ib, starti, endi);
879026d7285Schristos 		}
880026d7285Schristos 
881026d7285Schristos 		if (op_msk & B_KEYIN) {
882fdccd7e4Schristos 			const struct forces_tlv *keytlv;
883b3a00663Schristos 			uint16_t tll;
884026d7285Schristos 
885026d7285Schristos 			if (len < PTH_DESC_SIZE) {
886c74ad251Schristos 				ND_PRINT("pathlength %u with key/range too short %u\n",
887c74ad251Schristos 				       len, PTH_DESC_SIZE);
888026d7285Schristos 				return -1;
889026d7285Schristos 			}
890026d7285Schristos 
891026d7285Schristos 			/* skip keyid */
892026d7285Schristos 			pptr += 4;
893026d7285Schristos 			len -= 4;
894fdccd7e4Schristos 			keytlv = (const struct forces_tlv *)pptr;
895026d7285Schristos 			/* skip header */
896026d7285Schristos 			pptr += sizeof(struct forces_tlv);
897026d7285Schristos 			len -= sizeof(struct forces_tlv);
898026d7285Schristos 			/* skip key content */
899c74ad251Schristos 			tll = GET_BE_U_2(keytlv->length);
900026d7285Schristos 			if (tll < TLV_HDRL) {
901c74ad251Schristos 				ND_PRINT("key content length %u < %u\n",
902c74ad251Schristos 					tll, TLV_HDRL);
903026d7285Schristos 				return -1;
904026d7285Schristos 			}
905026d7285Schristos 			tll -= TLV_HDRL;
906026d7285Schristos 			if (len < tll) {
907c74ad251Schristos 				ND_PRINT("key content too short\n");
908026d7285Schristos 				return -1;
909026d7285Schristos 			}
910026d7285Schristos 			pptr += tll;
911026d7285Schristos 			len -= tll;
912026d7285Schristos 		}
913026d7285Schristos 
914026d7285Schristos 	}
915026d7285Schristos 
9160f74e101Schristos 	if (len) {
917fdccd7e4Schristos 		const struct forces_tlv *pdtlv = (const struct forces_tlv *)pptr;
918b3a00663Schristos 		uint16_t type;
919c74ad251Schristos 		uint16_t tlvl, tll;
920c74ad251Schristos 		u_int pad = 0;
9210f74e101Schristos 		u_int aln;
922b3a00663Schristos 		u_int invtlv;
9230f74e101Schristos 
924c74ad251Schristos 		type = GET_BE_U_2(pdtlv->type);
925c74ad251Schristos 		tlvl = GET_BE_U_2(pdtlv->length);
926c74ad251Schristos 		invtlv = tlv_valid(tlvl, len);
9270f74e101Schristos 		if (invtlv) {
928c74ad251Schristos 			ND_PRINT("%s Outstanding bytes %u for TLV type 0x%x TLV len %u\n",
9290f74e101Schristos 			          tok2str(ForCES_TLV_err, NULL, invtlv), len, type,
930c74ad251Schristos 			          tlvl);
9310f74e101Schristos 			goto pd_err;
9320f74e101Schristos 		}
9330f74e101Schristos 		/*
9340f74e101Schristos 		 * At this point, tlv_valid() has ensured that the TLV
9350f74e101Schristos 		 * length is large enough but not too large (it doesn't
9360f74e101Schristos 		 * go past the end of the containing TLV).
9370f74e101Schristos 		 */
938c74ad251Schristos 		tll = tlvl - TLV_HDRL;
939c74ad251Schristos 		aln = F_ALN_LEN(tlvl);
940c74ad251Schristos 		if (aln > tlvl) {
9410f74e101Schristos 			if (aln > len) {
942c74ad251Schristos 				ND_PRINT("Invalid padded pathdata TLV type 0x%x len %u missing %u pad bytes\n",
943c74ad251Schristos 				          type, tlvl, aln - len);
9440f74e101Schristos 			} else {
945c74ad251Schristos 				pad = aln - tlvl;
9460f74e101Schristos 			}
9470f74e101Schristos 		}
9480f74e101Schristos 		if (pd_valid(type)) {
9490f74e101Schristos 			const struct pdata_ops *ops = get_forces_pd(type);
9500f74e101Schristos 
951b3a00663Schristos 			if (ndo->ndo_vflag >= 3 && ops->v != F_TLV_PDAT) {
9520f74e101Schristos 				if (pad)
953c74ad251Schristos 					ND_PRINT("%s  %s (Length %u DataLen %u pad %u Bytes)\n",
954c74ad251Schristos 					          ib, ops->s, tlvl, tll, pad);
9550f74e101Schristos 				else
956c74ad251Schristos 					ND_PRINT("%s  %s (Length %u DataLen %u Bytes)\n",
957c74ad251Schristos 					          ib, ops->s, tlvl, tll);
9580f74e101Schristos 			}
9590f74e101Schristos 
960b3a00663Schristos 			chk_op_type(ndo, type, op_msk, ops->op_msk);
9610f74e101Schristos 
962b3a00663Schristos 			if (ops->print(ndo, (const u_char *)pdtlv,
9630f74e101Schristos 					tll + pad + TLV_HDRL, op_msk,
9649546e36dSchristos 					indent + 2) == -1)
9659546e36dSchristos 				return -1;
9669546e36dSchristos 			len -= (TLV_HDRL + pad + tll);
9670f74e101Schristos 		} else {
968c74ad251Schristos 			ND_PRINT("Invalid path data content type 0x%x len %u\n",
969c74ad251Schristos 			       type, tlvl);
9700f74e101Schristos pd_err:
971c74ad251Schristos 			if (tlvl) {
972c74ad251Schristos                                 hex_print(ndo, "Bad Data val\n\t  [",
973c74ad251Schristos 					  pptr, len);
974c74ad251Schristos 				ND_PRINT("]\n");
9750f74e101Schristos 
9760f74e101Schristos 				return -1;
9770f74e101Schristos 			}
9780f74e101Schristos 		}
9790f74e101Schristos 	}
9809546e36dSchristos 	return len;
9810f74e101Schristos 
9820f74e101Schristos trunc:
983c74ad251Schristos 	nd_print_trunc(ndo);
9840f74e101Schristos 	return -1;
9850f74e101Schristos }
9860f74e101Schristos 
987026d7285Schristos static int
988b3a00663Schristos pdata_print(netdissect_options *ndo,
989c74ad251Schristos             const u_char * pptr, u_int len,
990b3a00663Schristos             uint16_t op_msk, int indent)
9910f74e101Schristos {
992fdccd7e4Schristos 	const struct pathdata_h *pdh = (const struct pathdata_h *)pptr;
9930f74e101Schristos 	char *ib = indent_pr(indent, 0);
9940f74e101Schristos 	u_int minsize = 0;
9959546e36dSchristos 	int more_pd = 0;
996b3a00663Schristos 	uint16_t idcnt = 0;
9970f74e101Schristos 
998c74ad251Schristos 	ND_TCHECK_SIZE(pdh);
9990f74e101Schristos 	if (len < sizeof(struct pathdata_h))
10000f74e101Schristos 		goto trunc;
1001b3a00663Schristos 	if (ndo->ndo_vflag >= 3) {
1002c74ad251Schristos 		ND_PRINT("\n%sPathdata: Flags 0x%x ID count %u\n",
1003c74ad251Schristos 		       ib, GET_BE_U_2(pdh->pflags),
1004c74ad251Schristos 		       GET_BE_U_2(pdh->pIDcnt));
10050f74e101Schristos 	}
10060f74e101Schristos 
1007c74ad251Schristos 	if (GET_BE_U_2(pdh->pflags) & F_SELKEY) {
10080f74e101Schristos 		op_msk |= B_KEYIN;
10090f74e101Schristos 	}
1010026d7285Schristos 
1011026d7285Schristos 	/* Table GET Range operation */
1012c74ad251Schristos 	if (GET_BE_U_2(pdh->pflags) & F_SELTABRANGE) {
1013026d7285Schristos 		op_msk |= B_TRNG;
1014026d7285Schristos 	}
1015026d7285Schristos 	/* Table SET append operation */
1016c74ad251Schristos 	if (GET_BE_U_2(pdh->pflags) & F_TABAPPEND) {
1017026d7285Schristos 		op_msk |= B_APPND;
1018026d7285Schristos 	}
1019026d7285Schristos 
10200f74e101Schristos 	pptr += sizeof(struct pathdata_h);
10210f74e101Schristos 	len -= sizeof(struct pathdata_h);
1022c74ad251Schristos 	idcnt = GET_BE_U_2(pdh->pIDcnt);
10239546e36dSchristos 	minsize = idcnt * 4;
10240f74e101Schristos 	if (len < minsize) {
1025c74ad251Schristos 		ND_PRINT("\t\t\ttruncated IDs expected %uB got %uB\n", minsize,
1026c74ad251Schristos 		       len);
1027c74ad251Schristos 		hex_print(ndo, "\t\t\tID Data[", pptr, len);
1028c74ad251Schristos 		ND_PRINT("]\n");
10290f74e101Schristos 		return -1;
10300f74e101Schristos 	}
1031026d7285Schristos 
1032026d7285Schristos 	if ((op_msk & B_TRNG) && (op_msk & B_KEYIN)) {
1033c74ad251Schristos 		ND_PRINT("\t\t\tIllegal to have both Table ranges and keys\n");
1034026d7285Schristos 		return -1;
1035026d7285Schristos 	}
1036026d7285Schristos 
1037b3a00663Schristos 	more_pd = pdatacnt_print(ndo, pptr, len, idcnt, op_msk, indent);
10389546e36dSchristos 	if (more_pd > 0) {
10399546e36dSchristos 		int consumed = len - more_pd;
10409546e36dSchristos 		pptr += consumed;
10419546e36dSchristos 		len = more_pd;
10429546e36dSchristos 		/* XXX: Argh, recurse some more */
1043b3a00663Schristos 		return recpdoptlv_print(ndo, pptr, len, op_msk, indent+1);
10449546e36dSchristos 	} else
10459546e36dSchristos 		return 0;
10460f74e101Schristos 
10470f74e101Schristos trunc:
1048c74ad251Schristos 	nd_print_trunc(ndo);
10490f74e101Schristos 	return -1;
10500f74e101Schristos }
10510f74e101Schristos 
1052026d7285Schristos static int
1053b3a00663Schristos genoptlv_print(netdissect_options *ndo,
1054c74ad251Schristos                const u_char * pptr, u_int len,
1055b3a00663Schristos                uint16_t op_msk, int indent)
10560f74e101Schristos {
1057fdccd7e4Schristos 	const struct forces_tlv *pdtlv = (const struct forces_tlv *)pptr;
1058b3a00663Schristos 	uint16_t type;
1059c74ad251Schristos 	u_int tlvl;
1060b3a00663Schristos 	u_int invtlv;
10610f74e101Schristos 	char *ib = indent_pr(indent, 0);
10620f74e101Schristos 
1063c74ad251Schristos 	type = GET_BE_U_2(pdtlv->type);
1064c74ad251Schristos 	tlvl = GET_BE_U_2(pdtlv->length);
1065c74ad251Schristos 	invtlv = tlv_valid(tlvl, len);
1066c74ad251Schristos 	ND_PRINT("genoptlvprint - %s TLV type 0x%x len %u\n",
1067c74ad251Schristos 	       tok2str(ForCES_TLV, NULL, type), type, tlvl);
10680f74e101Schristos 	if (!invtlv) {
10690f74e101Schristos 		/*
10700f74e101Schristos 		 * At this point, tlv_valid() has ensured that the TLV
10710f74e101Schristos 		 * length is large enough but not too large (it doesn't
10720f74e101Schristos 		 * go past the end of the containing TLV).
10730f74e101Schristos 		 */
1074c74ad251Schristos 		const u_char *dp = (const u_char *) TLV_DATA(pdtlv);
1075c74ad251Schristos 
10760f74e101Schristos 		if (!ttlv_valid(type)) {
1077c74ad251Schristos 			ND_PRINT("%s TLV type 0x%x len %u\n",
10780f74e101Schristos 			       tok2str(ForCES_TLV_err, NULL, invtlv), type,
1079c74ad251Schristos 			       tlvl);
10800f74e101Schristos 			return -1;
10810f74e101Schristos 		}
1082b3a00663Schristos 		if (ndo->ndo_vflag >= 3)
1083c74ad251Schristos 			ND_PRINT("%s%s, length %u (data length %u Bytes)",
10840f74e101Schristos 			       ib, tok2str(ForCES_TLV, NULL, type),
1085c74ad251Schristos 			       tlvl, tlvl - TLV_HDRL);
10860f74e101Schristos 
1087c74ad251Schristos 		return pdata_print(ndo, dp, tlvl - TLV_HDRL, op_msk, indent + 1);
10880f74e101Schristos 	} else {
1089c74ad251Schristos 		ND_PRINT("\t\t\tInvalid ForCES TLV type=%x", type);
10900f74e101Schristos 		return -1;
10910f74e101Schristos 	}
10920f74e101Schristos }
10930f74e101Schristos 
1094026d7285Schristos static int
1095b3a00663Schristos recpdoptlv_print(netdissect_options *ndo,
1096c74ad251Schristos                  const u_char * pptr, u_int len,
1097b3a00663Schristos                  uint16_t op_msk, int indent)
10980f74e101Schristos {
1099fdccd7e4Schristos 	const struct forces_tlv *pdtlv = (const struct forces_tlv *)pptr;
11000f74e101Schristos 
11010f74e101Schristos 	while (len != 0) {
1102c74ad251Schristos 		uint16_t type, tlvl;
1103c74ad251Schristos 		u_int invtlv;
1104c74ad251Schristos 		char *ib;
1105c74ad251Schristos 		const u_char *dp;
1106c74ad251Schristos 
1107c74ad251Schristos 		tlvl = GET_BE_U_2(pdtlv->length);
1108c74ad251Schristos 		invtlv = tlv_valid(tlvl, len);
11090f74e101Schristos 		if (invtlv) {
11100f74e101Schristos 			break;
11110f74e101Schristos 		}
11120f74e101Schristos 
11130f74e101Schristos 		/*
11140f74e101Schristos 		 * At this point, tlv_valid() has ensured that the TLV
11150f74e101Schristos 		 * length is large enough but not too large (it doesn't
11160f74e101Schristos 		 * go past the end of the containing TLV).
11170f74e101Schristos 		 */
11180f74e101Schristos 		ib = indent_pr(indent, 0);
1119c74ad251Schristos 		type = GET_BE_U_2(pdtlv->type);
1120fdccd7e4Schristos 		dp = (const u_char *) TLV_DATA(pdtlv);
11210f74e101Schristos 
1122b3a00663Schristos 		if (ndo->ndo_vflag >= 3)
1123c74ad251Schristos 			ND_PRINT("%s%s, length %u (data encapsulated %u Bytes)",
11240f74e101Schristos 			          ib, tok2str(ForCES_TLV, NULL, type),
1125c74ad251Schristos 			          tlvl,
1126c74ad251Schristos 			          tlvl - TLV_HDRL);
11270f74e101Schristos 
1128c74ad251Schristos 		if (pdata_print(ndo, dp, tlvl - TLV_HDRL, op_msk, indent + 1) == -1)
11299546e36dSchristos 			return -1;
11300f74e101Schristos 		pdtlv = GO_NXT_TLV(pdtlv, len);
11310f74e101Schristos 	}
11320f74e101Schristos 
11330f74e101Schristos 	if (len) {
1134c74ad251Schristos 		ND_PRINT("\n\t\tMessy PATHDATA TLV header, type (0x%x)\n\t\texcess of %u Bytes ",
1135c74ad251Schristos 		          GET_BE_U_2(pdtlv->type),
1136c74ad251Schristos 		          len - GET_BE_U_2(pdtlv->length));
11370f74e101Schristos 		return -1;
11380f74e101Schristos 	}
11390f74e101Schristos 
11400f74e101Schristos 	return 0;
11410f74e101Schristos }
11420f74e101Schristos 
1143026d7285Schristos static int
1144b3a00663Schristos invoptlv_print(netdissect_options *ndo,
1145c74ad251Schristos                const u_char * pptr, u_int len,
1146b3a00663Schristos                uint16_t op_msk _U_, int indent)
11470f74e101Schristos {
11480f74e101Schristos 	char *ib = indent_pr(indent, 1);
11490f74e101Schristos 
1150b3a00663Schristos 	if (ndo->ndo_vflag >= 3) {
1151c74ad251Schristos 		ND_PRINT("%sData[", ib + 1);
1152c74ad251Schristos 		hex_print(ndo, ib, pptr, len);
1153c74ad251Schristos 		ND_PRINT("%s]\n", ib);
11540f74e101Schristos 	}
11550f74e101Schristos 	return -1;
11560f74e101Schristos }
11570f74e101Schristos 
1158026d7285Schristos static int
1159b3a00663Schristos otlv_print(netdissect_options *ndo,
1160b3a00663Schristos            const struct forces_tlv *otlv, uint16_t op_msk _U_, int indent)
11610f74e101Schristos {
11620f74e101Schristos 	int rc = 0;
1163c74ad251Schristos 	const u_char *dp = (const u_char *) TLV_DATA(otlv);
1164b3a00663Schristos 	uint16_t type;
1165c74ad251Schristos 	u_int tll;
11660f74e101Schristos 	char *ib = indent_pr(indent, 0);
11670f74e101Schristos 	const struct optlv_h *ops;
11680f74e101Schristos 
11690f74e101Schristos 	/*
1170c74ad251Schristos 	 * lfbselect_print() has ensured that GET_BE_U_2(otlv->length)
11710f74e101Schristos 	 * >= TLV_HDRL.
11720f74e101Schristos 	 */
1173c74ad251Schristos 	type = GET_BE_U_2(otlv->type);
1174c74ad251Schristos 	tll = GET_BE_U_2(otlv->length) - TLV_HDRL;
11750f74e101Schristos 	ops = get_forces_optlv_h(type);
1176b3a00663Schristos 	if (ndo->ndo_vflag >= 3) {
1177c74ad251Schristos 		ND_PRINT("%sOper TLV %s(0x%x) length %u\n", ib, ops->s, type,
1178c74ad251Schristos 		       GET_BE_U_2(otlv->length));
11790f74e101Schristos 	}
11800f74e101Schristos 	/* rest of ops must at least have 12B {pathinfo} */
11810f74e101Schristos 	if (tll < OP_MIN_SIZ) {
1182c74ad251Schristos 		ND_PRINT("\t\tOper TLV %s(0x%x) length %u\n", ops->s, type,
1183c74ad251Schristos 		       GET_BE_U_2(otlv->length));
1184c74ad251Schristos 		ND_PRINT("\t\tTruncated data size %u minimum required %u\n", tll,
1185c74ad251Schristos 		       OP_MIN_SIZ);
1186b3a00663Schristos 		return invoptlv_print(ndo, dp, tll, ops->op_msk, indent);
11870f74e101Schristos 
11880f74e101Schristos 	}
11890f74e101Schristos 
1190ba2ff121Schristos 	/* XXX - do anything with ops->flags? */
1191ba2ff121Schristos         if(ops->print) {
1192b3a00663Schristos                 rc = ops->print(ndo, dp, tll, ops->op_msk, indent + 1);
1193ba2ff121Schristos         }
11940f74e101Schristos 	return rc;
11950f74e101Schristos }
11960f74e101Schristos 
11970f74e101Schristos #define ASTDLN	4
11980f74e101Schristos #define ASTMCD	255
1199026d7285Schristos static int
1200b3a00663Schristos asttlv_print(netdissect_options *ndo,
1201c74ad251Schristos              const u_char * pptr, u_int len,
1202b3a00663Schristos              uint16_t op_msk _U_, int indent)
12030f74e101Schristos {
1204b3a00663Schristos 	uint32_t rescode;
12050f74e101Schristos 	u_int dlen;
12060f74e101Schristos 	char *ib = indent_pr(indent, 0);
12070f74e101Schristos 
12080f74e101Schristos 	/*
12090f74e101Schristos 	 * forces_type_print() has ensured that len (the TLV length)
12100f74e101Schristos 	 * >= TLV_HDRL.
12110f74e101Schristos 	 */
12120f74e101Schristos 	dlen = len - TLV_HDRL;
12130f74e101Schristos 	if (dlen != ASTDLN) {
1214c74ad251Schristos 		ND_PRINT("illegal ASTresult-TLV: %u bytes!\n", dlen);
12150f74e101Schristos 		return -1;
12160f74e101Schristos 	}
1217c74ad251Schristos 	rescode = GET_BE_U_4(pptr);
12180f74e101Schristos 	if (rescode > ASTMCD) {
1219c74ad251Schristos 		ND_PRINT("illegal ASTresult result code: %u!\n", rescode);
12200f74e101Schristos 		return -1;
12210f74e101Schristos 	}
12220f74e101Schristos 
1223b3a00663Schristos 	if (ndo->ndo_vflag >= 3) {
1224c74ad251Schristos 		ND_PRINT("Teardown reason:\n%s", ib);
12250f74e101Schristos 		switch (rescode) {
12260f74e101Schristos 		case 0:
1227c74ad251Schristos 			ND_PRINT("Normal Teardown");
12280f74e101Schristos 			break;
12290f74e101Schristos 		case 1:
1230c74ad251Schristos 			ND_PRINT("Loss of Heartbeats");
12310f74e101Schristos 			break;
12320f74e101Schristos 		case 2:
1233c74ad251Schristos 			ND_PRINT("Out of bandwidth");
12340f74e101Schristos 			break;
12350f74e101Schristos 		case 3:
1236c74ad251Schristos 			ND_PRINT("Out of Memory");
12370f74e101Schristos 			break;
12380f74e101Schristos 		case 4:
1239c74ad251Schristos 			ND_PRINT("Application Crash");
12400f74e101Schristos 			break;
12410f74e101Schristos 		default:
1242c74ad251Schristos 			ND_PRINT("Unknown Teardown reason");
12430f74e101Schristos 			break;
12440f74e101Schristos 		}
1245c74ad251Schristos 		ND_PRINT("(%x)\n%s", rescode, ib);
12460f74e101Schristos 	}
12470f74e101Schristos 	return 0;
12480f74e101Schristos }
12490f74e101Schristos 
12500f74e101Schristos #define ASRDLN	4
12510f74e101Schristos #define ASRMCD	3
1252026d7285Schristos static int
1253b3a00663Schristos asrtlv_print(netdissect_options *ndo,
1254c74ad251Schristos              const u_char * pptr, u_int len,
1255b3a00663Schristos              uint16_t op_msk _U_, int indent)
12560f74e101Schristos {
1257b3a00663Schristos 	uint32_t rescode;
12580f74e101Schristos 	u_int dlen;
12590f74e101Schristos 	char *ib = indent_pr(indent, 0);
12600f74e101Schristos 
12610f74e101Schristos 	/*
12620f74e101Schristos 	 * forces_type_print() has ensured that len (the TLV length)
12630f74e101Schristos 	 * >= TLV_HDRL.
12640f74e101Schristos 	 */
12650f74e101Schristos 	dlen = len - TLV_HDRL;
12660f74e101Schristos 	if (dlen != ASRDLN) {	/* id, instance, oper tlv */
1267c74ad251Schristos 		ND_PRINT("illegal ASRresult-TLV: %u bytes!\n", dlen);
12680f74e101Schristos 		return -1;
12690f74e101Schristos 	}
1270c74ad251Schristos 	rescode = GET_BE_U_4(pptr);
12710f74e101Schristos 
12720f74e101Schristos 	if (rescode > ASRMCD) {
1273c74ad251Schristos 		ND_PRINT("illegal ASRresult result code: %u!\n", rescode);
12740f74e101Schristos 		return -1;
12750f74e101Schristos 	}
12760f74e101Schristos 
1277b3a00663Schristos 	if (ndo->ndo_vflag >= 3) {
1278c74ad251Schristos 		ND_PRINT("\n%s", ib);
12790f74e101Schristos 		switch (rescode) {
12800f74e101Schristos 		case 0:
1281c74ad251Schristos 			ND_PRINT("Success ");
12820f74e101Schristos 			break;
12830f74e101Schristos 		case 1:
1284c74ad251Schristos 			ND_PRINT("FE ID invalid ");
12850f74e101Schristos 			break;
12860f74e101Schristos 		case 2:
1287c74ad251Schristos 			ND_PRINT("permission denied ");
12880f74e101Schristos 			break;
12890f74e101Schristos 		default:
1290c74ad251Schristos 			ND_PRINT("Unknown ");
12910f74e101Schristos 			break;
12920f74e101Schristos 		}
1293c74ad251Schristos 		ND_PRINT("(%x)\n%s", rescode, ib);
12940f74e101Schristos 	}
12950f74e101Schristos 	return 0;
12960f74e101Schristos }
12970f74e101Schristos 
1298026d7285Schristos #if 0
12990f74e101Schristos /*
13000f74e101Schristos  * XXX - not used.
13010f74e101Schristos  */
1302026d7285Schristos static int
1303b3a00663Schristos gentltlv_print(netdissect_options *ndo,
1304c74ad251Schristos                const u_char * pptr _U_, u_int len,
1305b3a00663Schristos                uint16_t op_msk _U_, int indent _U_)
13060f74e101Schristos {
13070f74e101Schristos 	u_int dlen = len - TLV_HDRL;
13080f74e101Schristos 
13090f74e101Schristos 	if (dlen < 4) {		/* at least 32 bits must exist */
1310c74ad251Schristos 		ND_PRINT("truncated TLV: %u bytes missing! ", 4 - dlen);
13110f74e101Schristos 		return -1;
13120f74e101Schristos 	}
13130f74e101Schristos 	return 0;
13140f74e101Schristos }
1315026d7285Schristos #endif
13160f74e101Schristos 
13170f74e101Schristos #define RD_MIN 8
1318026d7285Schristos 
1319026d7285Schristos static int
1320b3a00663Schristos print_metailv(netdissect_options *ndo,
1321c74ad251Schristos               const u_char * pptr, uint16_t op_msk _U_, int indent)
13220f74e101Schristos {
13230f74e101Schristos 	u_int rlen;
13240f74e101Schristos 	char *ib = indent_pr(indent, 0);
13250f74e101Schristos 	/* XXX: check header length */
1326fdccd7e4Schristos 	const struct forces_ilv *ilv = (const struct forces_ilv *)pptr;
13270f74e101Schristos 
13280f74e101Schristos 	/*
13290f74e101Schristos 	 * print_metatlv() has ensured that len (what remains in the
13300f74e101Schristos 	 * ILV) >= ILV_HDRL.
13310f74e101Schristos 	 */
1332c74ad251Schristos 	rlen = GET_BE_U_4(ilv->length) - ILV_HDRL;
1333c74ad251Schristos 	ND_PRINT("%sMetaID 0x%x length %u\n", ib, GET_BE_U_4(ilv->type),
1334c74ad251Schristos 		  GET_BE_U_4(ilv->length));
1335b3a00663Schristos 	if (ndo->ndo_vflag >= 3) {
1336c74ad251Schristos 		hex_print(ndo, "\t\t[", ILV_DATA(ilv), rlen);
1337c74ad251Schristos 		ND_PRINT(" ]\n");
1338026d7285Schristos 	}
13390f74e101Schristos 	return 0;
13400f74e101Schristos }
13410f74e101Schristos 
1342026d7285Schristos static int
1343b3a00663Schristos print_metatlv(netdissect_options *ndo,
1344c74ad251Schristos               const u_char * pptr, u_int len,
1345b3a00663Schristos               uint16_t op_msk _U_, int indent)
13460f74e101Schristos {
13470f74e101Schristos 	u_int dlen;
13480f74e101Schristos 	char *ib = indent_pr(indent, 0);
13490f74e101Schristos 	u_int rlen;
1350fdccd7e4Schristos 	const struct forces_ilv *ilv = (const struct forces_ilv *)pptr;
13510f74e101Schristos 	int invilv;
13520f74e101Schristos 
13530f74e101Schristos 	/*
13540f74e101Schristos 	 * redirect_print() has ensured that len (what remains in the
13550f74e101Schristos 	 * TLV) >= TLV_HDRL.
13560f74e101Schristos 	 */
13570f74e101Schristos 	dlen = len - TLV_HDRL;
13580f74e101Schristos 	rlen = dlen;
1359c74ad251Schristos 	ND_PRINT("\n%s METADATA length %u\n", ib, rlen);
13600f74e101Schristos 	while (rlen != 0) {
1361c74ad251Schristos 		invilv = ilv_valid(ndo, ilv, rlen);
1362026d7285Schristos 		if (invilv) {
13630f74e101Schristos 			break;
1364026d7285Schristos 		}
13650f74e101Schristos 
13660f74e101Schristos 		/*
13670f74e101Schristos 		 * At this point, ilv_valid() has ensured that the ILV
13680f74e101Schristos 		 * length is large enough but not too large (it doesn't
13690f74e101Schristos 		 * go past the end of the containing TLV).
13700f74e101Schristos 		 */
1371fdccd7e4Schristos 		print_metailv(ndo, (const u_char *) ilv, 0, indent + 1);
13720f74e101Schristos 		ilv = GO_NXT_ILV(ilv, rlen);
13730f74e101Schristos 	}
13740f74e101Schristos 
13750f74e101Schristos 	return 0;
13760f74e101Schristos }
13770f74e101Schristos 
1378026d7285Schristos 
1379026d7285Schristos static int
1380b3a00663Schristos print_reddata(netdissect_options *ndo,
1381c74ad251Schristos               const u_char * pptr, u_int len,
1382dc860a36Sspz               uint16_t op_msk _U_, int indent)
13830f74e101Schristos {
13840f74e101Schristos 	u_int dlen;
1385026d7285Schristos 	char *ib = indent_pr(indent, 0);
13860f74e101Schristos 	u_int rlen;
13870f74e101Schristos 
13880f74e101Schristos 	dlen = len - TLV_HDRL;
13890f74e101Schristos 	rlen = dlen;
1390c74ad251Schristos 	ND_PRINT("\n%s Redirect Data length %u\n", ib, rlen);
13910f74e101Schristos 
1392b3a00663Schristos 	if (ndo->ndo_vflag >= 3) {
1393c74ad251Schristos 		ND_PRINT("\t\t[");
1394c74ad251Schristos 		hex_print(ndo, "\n\t\t", pptr, rlen);
1395c74ad251Schristos 		ND_PRINT("\n\t\t]");
13960f74e101Schristos 	}
13970f74e101Schristos 
13980f74e101Schristos 	return 0;
13990f74e101Schristos }
14000f74e101Schristos 
1401026d7285Schristos static int
1402b3a00663Schristos redirect_print(netdissect_options *ndo,
1403c74ad251Schristos                const u_char * pptr, u_int len,
1404b3a00663Schristos                uint16_t op_msk _U_, int indent)
14050f74e101Schristos {
1406fdccd7e4Schristos 	const struct forces_tlv *tlv = (const struct forces_tlv *)pptr;
14070f74e101Schristos 	u_int dlen;
14080f74e101Schristos 	u_int rlen;
1409b3a00663Schristos 	u_int invtlv;
14100f74e101Schristos 
14110f74e101Schristos 	/*
14120f74e101Schristos 	 * forces_type_print() has ensured that len (the TLV length)
14130f74e101Schristos 	 * >= TLV_HDRL.
14140f74e101Schristos 	 */
14150f74e101Schristos 	dlen = len - TLV_HDRL;
14160f74e101Schristos 	if (dlen <= RD_MIN) {
1417c74ad251Schristos 		ND_PRINT("\n\t\ttruncated Redirect TLV: %u bytes missing! ",
1418c74ad251Schristos 		       RD_MIN - dlen);
14190f74e101Schristos 		return -1;
14200f74e101Schristos 	}
14210f74e101Schristos 
14220f74e101Schristos 	rlen = dlen;
14230f74e101Schristos 	indent += 1;
14240f74e101Schristos 	while (rlen != 0) {
1425c74ad251Schristos 		uint16_t type, tlvl;
1426c74ad251Schristos 
1427c74ad251Schristos 		type = GET_BE_U_2(tlv->type);
1428c74ad251Schristos 		tlvl = GET_BE_U_2(tlv->length);
1429c74ad251Schristos 		invtlv = tlv_valid(tlvl, rlen);
1430026d7285Schristos 		if (invtlv) {
1431c74ad251Schristos 			ND_PRINT("Bad Redirect data\n");
14320f74e101Schristos 			break;
1433026d7285Schristos 		}
14340f74e101Schristos 
14350f74e101Schristos 		/*
14360f74e101Schristos 		 * At this point, tlv_valid() has ensured that the TLV
14370f74e101Schristos 		 * length is large enough but not too large (it doesn't
14380f74e101Schristos 		 * go past the end of the containing TLV).
14390f74e101Schristos 		 */
1440c74ad251Schristos 		if (type == F_TLV_METD) {
1441fdccd7e4Schristos 			print_metatlv(ndo, (const u_char *) TLV_DATA(tlv),
1442c74ad251Schristos 				      tlvl, 0,
1443c74ad251Schristos 				      indent);
1444c74ad251Schristos 		} else if (type == F_TLV_REDD) {
1445fdccd7e4Schristos 			print_reddata(ndo, (const u_char *) TLV_DATA(tlv),
1446c74ad251Schristos 				      tlvl, 0,
1447c74ad251Schristos 				      indent);
14480f74e101Schristos 		} else {
1449c74ad251Schristos 			ND_PRINT("Unknown REDIRECT TLV 0x%x len %u\n",
1450c74ad251Schristos 			       type,
1451c74ad251Schristos 			       tlvl);
14520f74e101Schristos 		}
14530f74e101Schristos 
14540f74e101Schristos 		tlv = GO_NXT_TLV(tlv, rlen);
14550f74e101Schristos 	}
14560f74e101Schristos 
14570f74e101Schristos 	if (rlen) {
1458c74ad251Schristos 		ND_PRINT("\n\t\tMessy Redirect TLV header, type (0x%x)\n\t\texcess of %u Bytes ",
1459c74ad251Schristos 		          GET_BE_U_2(tlv->type),
1460c74ad251Schristos 		          rlen - GET_BE_U_2(tlv->length));
14610f74e101Schristos 		return -1;
14620f74e101Schristos 	}
14630f74e101Schristos 
14640f74e101Schristos 	return 0;
14650f74e101Schristos }
14660f74e101Schristos 
14670f74e101Schristos #define OP_OFF 8
14680f74e101Schristos #define OP_MIN 12
14690f74e101Schristos 
1470026d7285Schristos static int
1471b3a00663Schristos lfbselect_print(netdissect_options *ndo,
1472c74ad251Schristos                 const u_char * pptr, u_int len,
1473b3a00663Schristos                 uint16_t op_msk, int indent)
14740f74e101Schristos {
14750f74e101Schristos 	const struct forces_lfbsh *lfbs;
14760f74e101Schristos 	const struct forces_tlv *otlv;
14770f74e101Schristos 	char *ib = indent_pr(indent, 0);
14780f74e101Schristos 	u_int dlen;
14790f74e101Schristos 	u_int rlen;
1480b3a00663Schristos 	u_int invtlv;
14810f74e101Schristos 
14820f74e101Schristos 	/*
14830f74e101Schristos 	 * forces_type_print() has ensured that len (the TLV length)
14840f74e101Schristos 	 * >= TLV_HDRL.
14850f74e101Schristos 	 */
14860f74e101Schristos 	dlen = len - TLV_HDRL;
14870f74e101Schristos 	if (dlen <= OP_MIN) {	/* id, instance, oper tlv header .. */
1488c74ad251Schristos 		ND_PRINT("\n\t\ttruncated lfb selector: %u bytes missing! ",
1489c74ad251Schristos 		       OP_MIN - dlen);
14900f74e101Schristos 		return -1;
14910f74e101Schristos 	}
14920f74e101Schristos 
14930f74e101Schristos 	/*
14940f74e101Schristos 	 * At this point, we know that dlen > OP_MIN; OP_OFF < OP_MIN, so
14950f74e101Schristos 	 * we also know that it's > OP_OFF.
14960f74e101Schristos 	 */
14970f74e101Schristos 	rlen = dlen - OP_OFF;
14980f74e101Schristos 
14990f74e101Schristos 	lfbs = (const struct forces_lfbsh *)pptr;
1500c74ad251Schristos 	ND_TCHECK_SIZE(lfbs);
1501b3a00663Schristos 	if (ndo->ndo_vflag >= 3) {
1502c74ad251Schristos 		ND_PRINT("\n%s%s(Classid %x) instance %x\n",
1503c74ad251Schristos 		       ib,
1504c74ad251Schristos 		       tok2str(ForCES_LFBs, NULL, GET_BE_U_4(lfbs->class)),
1505c74ad251Schristos 		       GET_BE_U_4(lfbs->class),
1506c74ad251Schristos 		       GET_BE_U_4(lfbs->instance));
15070f74e101Schristos 	}
15080f74e101Schristos 
1509fdccd7e4Schristos 	otlv = (const struct forces_tlv *)(lfbs + 1);
15100f74e101Schristos 
15110f74e101Schristos 	indent += 1;
15120f74e101Schristos 	while (rlen != 0) {
1513c74ad251Schristos 		uint16_t type, tlvl;
1514c74ad251Schristos 
1515c74ad251Schristos 		type = GET_BE_U_2(otlv->type);
1516c74ad251Schristos 		tlvl = GET_BE_U_2(otlv->length);
1517c74ad251Schristos 		invtlv = tlv_valid(tlvl, rlen);
15180f74e101Schristos 		if (invtlv)
15190f74e101Schristos 			break;
15200f74e101Schristos 
15210f74e101Schristos 		/*
15220f74e101Schristos 		 * At this point, tlv_valid() has ensured that the TLV
15230f74e101Schristos 		 * length is large enough but not too large (it doesn't
15240f74e101Schristos 		 * go past the end of the containing TLV).
15250f74e101Schristos 		 */
1526c74ad251Schristos 		if (op_valid(type, op_msk)) {
1527b3a00663Schristos 			otlv_print(ndo, otlv, 0, indent);
15280f74e101Schristos 		} else {
1529b3a00663Schristos 			if (ndo->ndo_vflag < 3)
1530c74ad251Schristos 				ND_PRINT("\n");
1531c74ad251Schristos 			ND_PRINT("\t\tINValid oper-TLV type 0x%x length %u for this ForCES message\n",
1532c74ad251Schristos 			          type, tlvl);
1533fdccd7e4Schristos 			invoptlv_print(ndo, (const u_char *)otlv, rlen, 0, indent);
15340f74e101Schristos 		}
15350f74e101Schristos 		otlv = GO_NXT_TLV(otlv, rlen);
15360f74e101Schristos 	}
15370f74e101Schristos 
15380f74e101Schristos 	if (rlen) {
1539c74ad251Schristos 		ND_PRINT("\n\t\tMessy oper TLV header, type (0x%x)\n\t\texcess of %u Bytes ",
1540c74ad251Schristos 		          GET_BE_U_2(otlv->type),
1541c74ad251Schristos 		          rlen - GET_BE_U_2(otlv->length));
15420f74e101Schristos 		return -1;
15430f74e101Schristos 	}
15440f74e101Schristos 
15450f74e101Schristos 	return 0;
15460f74e101Schristos 
15470f74e101Schristos trunc:
1548c74ad251Schristos 	nd_print_trunc(ndo);
15490f74e101Schristos 	return -1;
15500f74e101Schristos }
15510f74e101Schristos 
1552026d7285Schristos static int
1553b3a00663Schristos forces_type_print(netdissect_options *ndo,
1554c74ad251Schristos                   const u_char * pptr, const struct forcesh *fhdr _U_,
1555c74ad251Schristos                   u_int mlen, const struct tom_h *tops)
15560f74e101Schristos {
15570f74e101Schristos 	const struct forces_tlv *tltlv;
15580f74e101Schristos 	u_int rlen;
1559b3a00663Schristos 	u_int invtlv;
15600f74e101Schristos 	int rc = 0;
1561c74ad251Schristos 	u_int ttlv = 0;
15620f74e101Schristos 
15630f74e101Schristos 	/*
15640f74e101Schristos 	 * forces_print() has already checked that mlen >= ForCES_HDRL
15650f74e101Schristos 	 * by calling ForCES_HLN_VALID().
15660f74e101Schristos 	 */
15670f74e101Schristos 	rlen = mlen - ForCES_HDRL;
15680f74e101Schristos 
15690f74e101Schristos 	if (rlen > TLV_HLN) {
15700f74e101Schristos 		if (tops->flags & ZERO_TTLV) {
1571c74ad251Schristos 			ND_PRINT("<0x%x>Illegal Top level TLV!\n", tops->flags);
15720f74e101Schristos 			return -1;
15730f74e101Schristos 		}
15740f74e101Schristos 	} else {
15750f74e101Schristos 		if (tops->flags & ZERO_MORE_TTLV)
15760f74e101Schristos 			return 0;
15770f74e101Schristos 		if (tops->flags & ONE_MORE_TTLV) {
1578c74ad251Schristos 			ND_PRINT("\tTop level TLV Data missing!\n");
15790f74e101Schristos 			return -1;
15800f74e101Schristos 		}
15810f74e101Schristos 	}
15820f74e101Schristos 
15830f74e101Schristos 	if (tops->flags & ZERO_TTLV) {
15840f74e101Schristos 		return 0;
15850f74e101Schristos 	}
15860f74e101Schristos 
15870f74e101Schristos 	ttlv = tops->flags >> 4;
15880f74e101Schristos 	tltlv = GET_TOP_TLV(pptr);
15890f74e101Schristos 
15900f74e101Schristos 	/*XXX: 15 top level tlvs will probably be fine
15910f74e101Schristos 	   You are nuts if you send more ;-> */
15920f74e101Schristos 	while (rlen != 0) {
1593c74ad251Schristos 		uint16_t type, tlvl;
1594c74ad251Schristos 
1595c74ad251Schristos 		type = GET_BE_U_2(tltlv->type);
1596c74ad251Schristos 		tlvl = GET_BE_U_2(tltlv->length);
1597c74ad251Schristos 		invtlv = tlv_valid(tlvl, rlen);
15980f74e101Schristos 		if (invtlv)
15990f74e101Schristos 			break;
16000f74e101Schristos 
16010f74e101Schristos 		/*
16020f74e101Schristos 		 * At this point, tlv_valid() has ensured that the TLV
16030f74e101Schristos 		 * length is large enough but not too large (it doesn't
16040f74e101Schristos 		 * go past the end of the packet).
16050f74e101Schristos 		 */
1606c74ad251Schristos 		if (!ttlv_valid(type)) {
1607c74ad251Schristos 			ND_PRINT("\n\tInvalid ForCES Top TLV type=0x%x",
1608c74ad251Schristos 			       type);
16090f74e101Schristos 			return -1;
16100f74e101Schristos 		}
16110f74e101Schristos 
1612b3a00663Schristos 		if (ndo->ndo_vflag >= 3)
1613c74ad251Schristos 			ND_PRINT("\t%s, length %u (data length %u Bytes)",
1614c74ad251Schristos 			       tok2str(ForCES_TLV, NULL, type),
1615c74ad251Schristos 			       tlvl,
1616c74ad251Schristos 			       tlvl - TLV_HDRL);
16170f74e101Schristos 
1618fdccd7e4Schristos 		rc = tops->print(ndo, (const u_char *) TLV_DATA(tltlv),
1619c74ad251Schristos 				 tlvl,
1620c74ad251Schristos 				 tops->op_msk, 9);
16210f74e101Schristos 		if (rc < 0) {
16220f74e101Schristos 			return -1;
16230f74e101Schristos 		}
16240f74e101Schristos 		tltlv = GO_NXT_TLV(tltlv, rlen);
16250f74e101Schristos 		ttlv--;
16260f74e101Schristos 		if (ttlv <= 0)
16270f74e101Schristos 			break;
16280f74e101Schristos 	}
16290f74e101Schristos 	/*
16300f74e101Schristos 	 * XXX - if ttlv != 0, does that mean that the packet was too
16310f74e101Schristos 	 * short, and didn't have *enough* TLVs in it?
16320f74e101Schristos 	 */
16330f74e101Schristos 	if (rlen) {
1634c74ad251Schristos 		ND_PRINT("\tMess TopTLV header: min %u, total %u advertised %u ",
1635c74ad251Schristos 		       TLV_HDRL, rlen, GET_BE_U_2(tltlv->length));
16360f74e101Schristos 		return -1;
16370f74e101Schristos 	}
16380f74e101Schristos 
16390f74e101Schristos 	return 0;
16400f74e101Schristos }
16410f74e101Schristos 
1642026d7285Schristos void
1643b3a00663Schristos forces_print(netdissect_options *ndo,
1644c74ad251Schristos              const u_char * pptr, u_int len)
16450f74e101Schristos {
16460f74e101Schristos 	const struct forcesh *fhdr;
16470f74e101Schristos 	u_int mlen;
1648b3a00663Schristos 	uint32_t flg_raw;
1649c74ad251Schristos 	uint8_t tom;
16500f74e101Schristos 	const struct tom_h *tops;
16510f74e101Schristos 	int rc = 0;
16520f74e101Schristos 
1653c74ad251Schristos 	ndo->ndo_protocol = "forces";
16540f74e101Schristos 	fhdr = (const struct forcesh *)pptr;
1655c74ad251Schristos 	ND_TCHECK_SIZE(fhdr);
1656c74ad251Schristos 	tom = GET_U_1(fhdr->fm_tom);
1657c74ad251Schristos 	if (!tom_valid(tom)) {
1658c74ad251Schristos 		ND_PRINT("Invalid ForCES message type %u\n", tom);
16590f74e101Schristos 		goto error;
16600f74e101Schristos 	}
16610f74e101Schristos 
16620f74e101Schristos 	mlen = ForCES_BLN(fhdr);
16630f74e101Schristos 
1664c74ad251Schristos 	tops = get_forces_tom(tom);
16650f74e101Schristos 	if (tops->v == TOM_RSVD) {
1666c74ad251Schristos 		ND_PRINT("\n\tUnknown ForCES message type=0x%x", tom);
16670f74e101Schristos 		goto error;
16680f74e101Schristos 	}
16690f74e101Schristos 
1670c74ad251Schristos 	ND_PRINT("\n\tForCES %s ", tops->s);
16710f74e101Schristos 	if (!ForCES_HLN_VALID(mlen, len)) {
1672c74ad251Schristos 		ND_PRINT("Illegal ForCES pkt len - min %u, total recvd %u, advertised %u ",
1673c74ad251Schristos 		          ForCES_HDRL, len, ForCES_BLN(fhdr));
16740f74e101Schristos 		goto error;
16750f74e101Schristos 	}
16760f74e101Schristos 
1677c74ad251Schristos 	flg_raw = GET_BE_U_4(pptr + 20);
1678b3a00663Schristos 	if (ndo->ndo_vflag >= 1) {
1679c74ad251Schristos 		ND_PRINT("\n\tForCES Version %u len %uB flags 0x%08x ",
1680c74ad251Schristos 		       ForCES_V(fhdr), mlen, flg_raw);
1681c74ad251Schristos 		ND_PRINT("\n\tSrcID 0x%x(%s) DstID 0x%x(%s) Correlator 0x%" PRIx64,
16820f74e101Schristos 		       ForCES_SID(fhdr), ForCES_node(ForCES_SID(fhdr)),
16830f74e101Schristos 		       ForCES_DID(fhdr), ForCES_node(ForCES_DID(fhdr)),
1684c74ad251Schristos 		       GET_BE_U_8(fhdr->fm_cor));
16850f74e101Schristos 
16860f74e101Schristos 	}
1687b3a00663Schristos 	if (ndo->ndo_vflag >= 2) {
1688c74ad251Schristos 		ND_PRINT("\n\tForCES flags:\n\t  %s(0x%x), prio=%u, %s(0x%x),\n\t  %s(0x%x), %s(0x%x)\n",
1689b3a00663Schristos 		     tok2str(ForCES_ACKs, "ACKUnknown", ForCES_ACK(fhdr)),
1690b3a00663Schristos 		     ForCES_ACK(fhdr),
16910f74e101Schristos 		     ForCES_PRI(fhdr),
1692b3a00663Schristos 		     tok2str(ForCES_EMs, "EMUnknown", ForCES_EM(fhdr)),
1693b3a00663Schristos 		     ForCES_EM(fhdr),
1694b3a00663Schristos 		     tok2str(ForCES_ATs, "ATUnknown", ForCES_AT(fhdr)),
1695b3a00663Schristos 		     ForCES_AT(fhdr),
1696b3a00663Schristos 		     tok2str(ForCES_TPs, "TPUnknown", ForCES_TP(fhdr)),
1697c74ad251Schristos 		     ForCES_TP(fhdr));
1698c74ad251Schristos 		ND_PRINT("\t  Extra flags: rsv(b5-7) 0x%x rsv(b13-31) 0x%x\n",
1699c74ad251Schristos 		     ForCES_RS1(fhdr), ForCES_RS2(fhdr));
17000f74e101Schristos 	}
1701b3a00663Schristos 	rc = forces_type_print(ndo, pptr, fhdr, mlen, tops);
17020f74e101Schristos 	if (rc < 0) {
17030f74e101Schristos error:
1704c74ad251Schristos 		hex_print(ndo, "\n\t[", pptr, len);
1705c74ad251Schristos 		ND_PRINT("\n\t]");
17060f74e101Schristos 		return;
17070f74e101Schristos 	}
17080f74e101Schristos 
1709b3a00663Schristos 	if (ndo->ndo_vflag >= 4) {
1710c74ad251Schristos 		ND_PRINT("\n\t  Raw ForCES message\n\t [");
1711c74ad251Schristos 		hex_print(ndo, "\n\t ", pptr, len);
1712c74ad251Schristos 		ND_PRINT("\n\t ]");
17130f74e101Schristos 	}
17140f74e101Schristos 	return;
17150f74e101Schristos 
17160f74e101Schristos trunc:
1717c74ad251Schristos 	nd_print_trunc(ndo);
17180f74e101Schristos }
1719