132145Spst/*
232145Spst * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996
332145Spst *	The Regents of the University of California.  All rights reserved.
432145Spst *
532145Spst * Redistribution and use in source and binary forms, with or without
632145Spst * modification, are permitted provided that: (1) source code distributions
732145Spst * retain the above copyright notice and this paragraph in its entirety, (2)
832145Spst * distributions including binary code include the above copyright notice and
932145Spst * this paragraph in its entirety in the documentation or other materials
1032145Spst * provided with the distribution, and (3) all advertising materials mentioning
1132145Spst * features or use of this software display the following acknowledgement:
1232145Spst * ``This product includes software developed by the University of California,
1332145Spst * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
1432145Spst * the University nor the names of its contributors may be used to endorse
1532145Spst * or promote products derived from this software without specific prior
1632145Spst * written permission.
1732145Spst * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
1832145Spst * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
1932145Spst * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
2032145Spst */
2132145Spst
22313537Sglebius/* \summary: Frame Relay printer */
23313537Sglebius
24146778Ssam#ifdef HAVE_CONFIG_H
25146778Ssam#include "config.h"
2632145Spst#endif
2732145Spst
28313537Sglebius#include <netdissect-stdinc.h>
2932145Spst
30146778Ssam#include <stdio.h>
31146778Ssam#include <string.h>
3232145Spst
33313537Sglebius#include "netdissect.h"
34146778Ssam#include "addrtoname.h"
3532145Spst#include "ethertype.h"
36285275Spkelsey#include "llc.h"
37146778Ssam#include "nlpid.h"
38146778Ssam#include "extract.h"
39146778Ssam#include "oui.h"
4032145Spst
41276788Sdelphijstatic void frf15_print(netdissect_options *ndo, const u_char *, u_int);
4232145Spst
43146778Ssam/*
44146778Ssam * the frame relay header has a variable length
45146778Ssam *
46146778Ssam * the EA bit determines if there is another byte
47146778Ssam * in the header
48146778Ssam *
49146778Ssam * minimum header length is 2 bytes
50146778Ssam * maximum header length is 4 bytes
51146778Ssam *
52146778Ssam *      7    6    5    4    3    2    1    0
53146778Ssam *    +----+----+----+----+----+----+----+----+
54146778Ssam *    |        DLCI (6 bits)        | CR | EA |
55146778Ssam *    +----+----+----+----+----+----+----+----+
56146778Ssam *    |   DLCI (4 bits)   |FECN|BECN| DE | EA |
57146778Ssam *    +----+----+----+----+----+----+----+----+
58146778Ssam *    |           DLCI (7 bits)          | EA |
59146778Ssam *    +----+----+----+----+----+----+----+----+
60146778Ssam *    |        DLCI (6 bits)        |SDLC| EA |
61146778Ssam *    +----+----+----+----+----+----+----+----+
62146778Ssam */
6332145Spst
64146778Ssam#define FR_EA_BIT	0x01
6532145Spst
66146778Ssam#define FR_CR_BIT       0x02000000
67146778Ssam#define FR_DE_BIT	0x00020000
68146778Ssam#define FR_BECN_BIT	0x00040000
69146778Ssam#define FR_FECN_BIT	0x00080000
70146778Ssam#define FR_SDLC_BIT	0x00000002
7132145Spst
72146778Ssam
73276788Sdelphijstatic const struct tok fr_header_flag_values[] = {
74146778Ssam    { FR_CR_BIT, "C!" },
75146778Ssam    { FR_DE_BIT, "DE" },
76146778Ssam    { FR_BECN_BIT, "BECN" },
77146778Ssam    { FR_FECN_BIT, "FECN" },
78146778Ssam    { FR_SDLC_BIT, "sdlcore" },
79146778Ssam    { 0, NULL }
8032145Spst};
8132145Spst
82162021Ssam/* FRF.15 / FRF.16 */
83162021Ssam#define MFR_B_BIT 0x80
84162021Ssam#define MFR_E_BIT 0x40
85162021Ssam#define MFR_C_BIT 0x20
86162021Ssam#define MFR_BEC_MASK    (MFR_B_BIT | MFR_E_BIT | MFR_C_BIT)
87162021Ssam#define MFR_CTRL_FRAME  (MFR_B_BIT | MFR_E_BIT | MFR_C_BIT)
88162021Ssam#define MFR_FRAG_FRAME  (MFR_B_BIT | MFR_E_BIT )
89146778Ssam
90276788Sdelphijstatic const struct tok frf_flag_values[] = {
91162021Ssam    { MFR_B_BIT, "Begin" },
92162021Ssam    { MFR_E_BIT, "End" },
93162021Ssam    { MFR_C_BIT, "Control" },
94162021Ssam    { 0, NULL }
95162021Ssam};
96162021Ssam
97285275Spkelsey/* Finds out Q.922 address length, DLCI and flags. Returns 1 on success,
98285275Spkelsey * 0 on invalid address, -1 on truncated packet
99146778Ssam * save the flags dep. on address length
100146778Ssam */
101285275Spkelseystatic int parse_q922_addr(netdissect_options *ndo,
102285275Spkelsey                           const u_char *p, u_int *dlci,
103285275Spkelsey                           u_int *addr_len, uint8_t *flags, u_int length)
10432145Spst{
105285275Spkelsey	if (!ND_TTEST(p[0]) || length < 1)
106285275Spkelsey		return -1;
107146778Ssam	if ((p[0] & FR_EA_BIT))
108285275Spkelsey		return 0;
109285275Spkelsey
110285275Spkelsey	if (!ND_TTEST(p[1]) || length < 2)
111146778Ssam		return -1;
112146778Ssam	*addr_len = 2;
113146778Ssam	*dlci = ((p[0] & 0xFC) << 2) | ((p[1] & 0xF0) >> 4);
11432145Spst
115146778Ssam        flags[0] = p[0] & 0x02; /* populate the first flag fields */
116146778Ssam        flags[1] = p[1] & 0x0c;
117172686Smlaier        flags[2] = 0;           /* clear the rest of the flags */
118172686Smlaier        flags[3] = 0;
11932145Spst
120146778Ssam	if (p[1] & FR_EA_BIT)
121285275Spkelsey		return 1;	/* 2-byte Q.922 address */
12232145Spst
123146778Ssam	p += 2;
124285275Spkelsey	length -= 2;
125285275Spkelsey	if (!ND_TTEST(p[0]) || length < 1)
126285275Spkelsey		return -1;
127146778Ssam	(*addr_len)++;		/* 3- or 4-byte Q.922 address */
128146778Ssam	if ((p[0] & FR_EA_BIT) == 0) {
129146778Ssam		*dlci = (*dlci << 7) | (p[0] >> 1);
130146778Ssam		(*addr_len)++;	/* 4-byte Q.922 address */
131146778Ssam		p++;
132285275Spkelsey		length--;
133146778Ssam	}
13432145Spst
135285275Spkelsey	if (!ND_TTEST(p[0]) || length < 1)
136285275Spkelsey		return -1;
137146778Ssam	if ((p[0] & FR_EA_BIT) == 0)
138285275Spkelsey		return 0; /* more than 4 bytes of Q.922 address? */
139146778Ssam
140146778Ssam        flags[3] = p[0] & 0x02;
141146778Ssam
142190207Srpaulo        *dlci = (*dlci << 6) | (p[0] >> 2);
143146778Ssam
144285275Spkelsey	return 1;
14532145Spst}
14632145Spst
147285275Spkelseychar *
148285275Spkelseyq922_string(netdissect_options *ndo, const u_char *p, u_int length)
149285275Spkelsey{
150190207Srpaulo
151190207Srpaulo    static u_int dlci, addr_len;
152276788Sdelphij    static uint8_t flags[4];
153190207Srpaulo    static char buffer[sizeof("DLCI xxxxxxxxxx")];
154190207Srpaulo    memset(buffer, 0, sizeof(buffer));
155190207Srpaulo
156285275Spkelsey    if (parse_q922_addr(ndo, p, &dlci, &addr_len, flags, length) == 1){
157190207Srpaulo        snprintf(buffer, sizeof(buffer), "DLCI %u", dlci);
158190207Srpaulo    }
159190207Srpaulo
160190207Srpaulo    return buffer;
161190207Srpaulo}
162190207Srpaulo
163190207Srpaulo
164146778Ssam/* Frame Relay packet structure, with flags and CRC removed
16532145Spst
16632145Spst                  +---------------------------+
16732145Spst                  |       Q.922 Address*      |
16832145Spst                  +--                       --+
16932145Spst                  |                           |
17032145Spst                  +---------------------------+
17132145Spst                  | Control (UI = 0x03)       |
17232145Spst                  +---------------------------+
17332145Spst                  | Optional Pad      (0x00)  |
17432145Spst                  +---------------------------+
17532145Spst                  | NLPID                     |
17632145Spst                  +---------------------------+
17732145Spst                  |             .             |
17832145Spst                  |             .             |
17932145Spst                  |             .             |
18032145Spst                  |           Data            |
18132145Spst                  |             .             |
18232145Spst                  |             .             |
18332145Spst                  +---------------------------+
18432145Spst
18532145Spst           * Q.922 addresses, as presently defined, are two octets and
18632145Spst             contain a 10-bit DLCI.  In some networks Q.922 addresses
18732145Spst             may optionally be increased to three or four octets.
18832145Spst*/
18932145Spst
190146778Ssamstatic void
191276788Sdelphijfr_hdr_print(netdissect_options *ndo,
192276788Sdelphij             int length, u_int addr_len, u_int dlci, uint8_t *flags, uint16_t nlpid)
19332145Spst{
194276788Sdelphij    if (ndo->ndo_qflag) {
195276788Sdelphij        ND_PRINT((ndo, "Q.922, DLCI %u, length %u: ",
196146778Ssam                     dlci,
197276788Sdelphij                     length));
198146778Ssam    } else {
199146778Ssam        if (nlpid <= 0xff) /* if its smaller than 256 then its a NLPID */
200276788Sdelphij            ND_PRINT((ndo, "Q.922, hdr-len %u, DLCI %u, Flags [%s], NLPID %s (0x%02x), length %u: ",
201146778Ssam                         addr_len,
202146778Ssam                         dlci,
203146778Ssam                         bittok2str(fr_header_flag_values, "none", EXTRACT_32BITS(flags)),
204146778Ssam                         tok2str(nlpid_values,"unknown", nlpid),
205146778Ssam                         nlpid,
206276788Sdelphij                         length));
207146778Ssam        else /* must be an ethertype */
208276788Sdelphij            ND_PRINT((ndo, "Q.922, hdr-len %u, DLCI %u, Flags [%s], cisco-ethertype %s (0x%04x), length %u: ",
209146778Ssam                         addr_len,
210146778Ssam                         dlci,
211146778Ssam                         bittok2str(fr_header_flag_values, "none", EXTRACT_32BITS(flags)),
212146778Ssam                         tok2str(ethertype_values, "unknown", nlpid),
213146778Ssam                         nlpid,
214276788Sdelphij                         length));
215146778Ssam    }
21632145Spst}
21732145Spst
218146778Ssamu_int
219276788Sdelphijfr_if_print(netdissect_options *ndo,
220276788Sdelphij            const struct pcap_pkthdr *h, register const u_char *p)
22132145Spst{
22232145Spst	register u_int length = h->len;
22332145Spst	register u_int caplen = h->caplen;
224147904Ssam
225276788Sdelphij        ND_TCHECK2(*p, 4); /* minimum frame header length */
226147904Ssam
227276788Sdelphij        if ((length = fr_print(ndo, p, length)) == 0)
228147904Ssam            return (0);
229147904Ssam        else
230147904Ssam            return length;
231147904Ssam trunc:
232276788Sdelphij        ND_PRINT((ndo, "[|fr]"));
233147904Ssam        return caplen;
234147904Ssam}
235147904Ssam
236147904Ssamu_int
237276788Sdelphijfr_print(netdissect_options *ndo,
238276788Sdelphij         register const u_char *p, u_int length)
239147904Ssam{
240285275Spkelsey	int ret;
241276788Sdelphij	uint16_t extracted_ethertype;
242146778Ssam	u_int dlci;
243146778Ssam	u_int addr_len;
244276788Sdelphij	uint16_t nlpid;
245146778Ssam	u_int hdr_len;
246276788Sdelphij	uint8_t flags[4];
24732145Spst
248285275Spkelsey	ret = parse_q922_addr(ndo, p, &dlci, &addr_len, flags, length);
249285275Spkelsey	if (ret == -1)
250285275Spkelsey		goto trunc;
251285275Spkelsey	if (ret == 0) {
252276788Sdelphij		ND_PRINT((ndo, "Q.922, invalid address"));
253147904Ssam		return 0;
254146778Ssam	}
25532145Spst
256285275Spkelsey	ND_TCHECK(p[addr_len]);
257285275Spkelsey	if (length < addr_len + 1)
258285275Spkelsey		goto trunc;
259146778Ssam
260285275Spkelsey	if (p[addr_len] != LLC_UI && dlci != 0) {
261285275Spkelsey                /*
262285275Spkelsey                 * Let's figure out if we have Cisco-style encapsulation,
263285275Spkelsey                 * with an Ethernet type (Cisco HDLC type?) following the
264285275Spkelsey                 * address.
265285275Spkelsey                 */
266285275Spkelsey		if (!ND_TTEST2(p[addr_len], 2) || length < addr_len + 2) {
267285275Spkelsey                        /* no Ethertype */
268285275Spkelsey                        ND_PRINT((ndo, "UI %02x! ", p[addr_len]));
269285275Spkelsey                } else {
270285275Spkelsey                        extracted_ethertype = EXTRACT_16BITS(p+addr_len);
27132145Spst
272285275Spkelsey                        if (ndo->ndo_eflag)
273285275Spkelsey                                fr_hdr_print(ndo, length, addr_len, dlci,
274285275Spkelsey                                    flags, extracted_ethertype);
275146778Ssam
276285275Spkelsey                        if (ethertype_print(ndo, extracted_ethertype,
277285275Spkelsey                                            p+addr_len+ETHERTYPE_LEN,
278285275Spkelsey                                            length-addr_len-ETHERTYPE_LEN,
279313537Sglebius                                            ndo->ndo_snapend-p-addr_len-ETHERTYPE_LEN,
280313537Sglebius                                            NULL, NULL) == 0)
281285275Spkelsey                                /* ether_type not known, probably it wasn't one */
282285275Spkelsey                                ND_PRINT((ndo, "UI %02x! ", p[addr_len]));
283285275Spkelsey                        else
284285275Spkelsey                                return addr_len + 2;
285285275Spkelsey                }
286146778Ssam        }
287146778Ssam
288285275Spkelsey	ND_TCHECK(p[addr_len+1]);
289285275Spkelsey	if (length < addr_len + 2)
290285275Spkelsey		goto trunc;
291285275Spkelsey
292285275Spkelsey	if (p[addr_len + 1] == 0) {
293285275Spkelsey		/*
294285275Spkelsey		 * Assume a pad byte after the control (UI) byte.
295285275Spkelsey		 * A pad byte should only be used with 3-byte Q.922.
296285275Spkelsey		 */
297146778Ssam		if (addr_len != 3)
298276788Sdelphij			ND_PRINT((ndo, "Pad! "));
299285275Spkelsey		hdr_len = addr_len + 1 /* UI */ + 1 /* pad */ + 1 /* NLPID */;
300285275Spkelsey	} else {
301285275Spkelsey		/*
302285275Spkelsey		 * Not a pad byte.
303285275Spkelsey		 * A pad byte should be used with 3-byte Q.922.
304285275Spkelsey		 */
305285275Spkelsey		if (addr_len == 3)
306285275Spkelsey			ND_PRINT((ndo, "No pad! "));
307285275Spkelsey		hdr_len = addr_len + 1 /* UI */ + 1 /* NLPID */;
308285275Spkelsey	}
309146778Ssam
310285275Spkelsey        ND_TCHECK(p[hdr_len - 1]);
311285275Spkelsey	if (length < hdr_len)
312285275Spkelsey		goto trunc;
313146778Ssam	nlpid = p[hdr_len - 1];
314146778Ssam
315276788Sdelphij	if (ndo->ndo_eflag)
316276788Sdelphij		fr_hdr_print(ndo, length, addr_len, dlci, flags, nlpid);
317146778Ssam	p += hdr_len;
318146778Ssam	length -= hdr_len;
31932145Spst
320146778Ssam	switch (nlpid) {
32132145Spst	case NLPID_IP:
322276788Sdelphij	        ip_print(ndo, p, length);
32332145Spst		break;
324146778Ssam
325146778Ssam	case NLPID_IP6:
326276788Sdelphij		ip6_print(ndo, p, length);
327146778Ssam		break;
328285275Spkelsey
32932145Spst	case NLPID_CLNP:
33032145Spst	case NLPID_ESIS:
33132145Spst	case NLPID_ISIS:
332327234Semaste		isoclns_print(ndo, p - 1, length + 1); /* OSI printers need the NLPID field */
33332145Spst		break;
334146778Ssam
335146778Ssam	case NLPID_SNAP:
336313537Sglebius		if (snap_print(ndo, p, length, ndo->ndo_snapend - p, NULL, NULL, 0) == 0) {
337146778Ssam			/* ether_type not known, print raw packet */
338276788Sdelphij                        if (!ndo->ndo_eflag)
339276788Sdelphij                            fr_hdr_print(ndo, length + hdr_len, hdr_len,
340146778Ssam                                         dlci, flags, nlpid);
341276788Sdelphij			if (!ndo->ndo_suppress_default_print)
342276788Sdelphij				ND_DEFAULTPRINT(p - hdr_len, length + hdr_len);
343146778Ssam		}
34432145Spst		break;
345146778Ssam
346146778Ssam        case NLPID_Q933:
347276788Sdelphij		q933_print(ndo, p, length);
348146778Ssam		break;
349146778Ssam
350146778Ssam        case NLPID_MFR:
351276788Sdelphij                frf15_print(ndo, p, length);
352146778Ssam                break;
353146778Ssam
354172686Smlaier        case NLPID_PPP:
355276788Sdelphij                ppp_print(ndo, p, length);
356172686Smlaier                break;
357172686Smlaier
35832145Spst	default:
359276788Sdelphij		if (!ndo->ndo_eflag)
360276788Sdelphij                    fr_hdr_print(ndo, length + hdr_len, addr_len,
361146778Ssam				     dlci, flags, nlpid);
362276788Sdelphij		if (!ndo->ndo_xflag)
363276788Sdelphij			ND_DEFAULTPRINT(p, length);
36432145Spst	}
36532145Spst
366146778Ssam	return hdr_len;
367147904Ssam
368147904Ssam trunc:
369276788Sdelphij        ND_PRINT((ndo, "[|fr]"));
370147904Ssam        return 0;
371147904Ssam
37232145Spst}
37332145Spst
374172686Smlaieru_int
375276788Sdelphijmfr_if_print(netdissect_options *ndo,
376276788Sdelphij             const struct pcap_pkthdr *h, register const u_char *p)
377172686Smlaier{
378172686Smlaier	register u_int length = h->len;
379172686Smlaier	register u_int caplen = h->caplen;
380172686Smlaier
381276788Sdelphij        ND_TCHECK2(*p, 2); /* minimum frame header length */
382172686Smlaier
383276788Sdelphij        if ((length = mfr_print(ndo, p, length)) == 0)
384172686Smlaier            return (0);
385172686Smlaier        else
386172686Smlaier            return length;
387172686Smlaier trunc:
388276788Sdelphij        ND_PRINT((ndo, "[|mfr]"));
389172686Smlaier        return caplen;
390172686Smlaier}
391172686Smlaier
392172686Smlaier
393162021Ssam#define MFR_CTRL_MSG_ADD_LINK        1
394162021Ssam#define MFR_CTRL_MSG_ADD_LINK_ACK    2
395162021Ssam#define MFR_CTRL_MSG_ADD_LINK_REJ    3
396162021Ssam#define MFR_CTRL_MSG_HELLO           4
397162021Ssam#define MFR_CTRL_MSG_HELLO_ACK       5
398162021Ssam#define MFR_CTRL_MSG_REMOVE_LINK     6
399162021Ssam#define MFR_CTRL_MSG_REMOVE_LINK_ACK 7
400162021Ssam
401276788Sdelphijstatic const struct tok mfr_ctrl_msg_values[] = {
402162021Ssam    { MFR_CTRL_MSG_ADD_LINK, "Add Link" },
403162021Ssam    { MFR_CTRL_MSG_ADD_LINK_ACK, "Add Link ACK" },
404162021Ssam    { MFR_CTRL_MSG_ADD_LINK_REJ, "Add Link Reject" },
405162021Ssam    { MFR_CTRL_MSG_HELLO, "Hello" },
406162021Ssam    { MFR_CTRL_MSG_HELLO_ACK, "Hello ACK" },
407162021Ssam    { MFR_CTRL_MSG_REMOVE_LINK, "Remove Link" },
408162021Ssam    { MFR_CTRL_MSG_REMOVE_LINK_ACK, "Remove Link ACK" },
409162021Ssam    { 0, NULL }
410162021Ssam};
411162021Ssam
412162021Ssam#define MFR_CTRL_IE_BUNDLE_ID  1
413162021Ssam#define MFR_CTRL_IE_LINK_ID    2
414162021Ssam#define MFR_CTRL_IE_MAGIC_NUM  3
415162021Ssam#define MFR_CTRL_IE_TIMESTAMP  5
416162021Ssam#define MFR_CTRL_IE_VENDOR_EXT 6
417162021Ssam#define MFR_CTRL_IE_CAUSE      7
418162021Ssam
419276788Sdelphijstatic const struct tok mfr_ctrl_ie_values[] = {
420162021Ssam    { MFR_CTRL_IE_BUNDLE_ID, "Bundle ID"},
421162021Ssam    { MFR_CTRL_IE_LINK_ID, "Link ID"},
422162021Ssam    { MFR_CTRL_IE_MAGIC_NUM, "Magic Number"},
423162021Ssam    { MFR_CTRL_IE_TIMESTAMP, "Timestamp"},
424162021Ssam    { MFR_CTRL_IE_VENDOR_EXT, "Vendor Extension"},
425162021Ssam    { MFR_CTRL_IE_CAUSE, "Cause"},
426162021Ssam    { 0, NULL }
427162021Ssam};
428162021Ssam
429162021Ssam#define MFR_ID_STRING_MAXLEN 50
430162021Ssam
431162021Ssamstruct ie_tlv_header_t {
432276788Sdelphij    uint8_t ie_type;
433276788Sdelphij    uint8_t ie_len;
434162021Ssam};
435162021Ssam
436162021Ssamu_int
437276788Sdelphijmfr_print(netdissect_options *ndo,
438276788Sdelphij          register const u_char *p, u_int length)
439162021Ssam{
440162021Ssam    u_int tlen,idx,hdr_len = 0;
441276788Sdelphij    uint16_t sequence_num;
442276788Sdelphij    uint8_t ie_type,ie_len;
443276788Sdelphij    const uint8_t *tptr;
444162021Ssam
445162021Ssam
446162021Ssam/*
447162021Ssam * FRF.16 Link Integrity Control Frame
448276788Sdelphij *
449162021Ssam *      7    6    5    4    3    2    1    0
450162021Ssam *    +----+----+----+----+----+----+----+----+
451162021Ssam *    | B  | E  | C=1| 0    0    0    0  | EA |
452162021Ssam *    +----+----+----+----+----+----+----+----+
453162021Ssam *    | 0    0    0    0    0    0    0    0  |
454162021Ssam *    +----+----+----+----+----+----+----+----+
455162021Ssam *    |              message type             |
456162021Ssam *    +----+----+----+----+----+----+----+----+
457162021Ssam */
458162021Ssam
459276788Sdelphij    ND_TCHECK2(*p, 4); /* minimum frame header length */
460356341Scy    if (length < 4) {
461356341Scy        ND_PRINT((ndo, "Message too short (%u bytes)", length));
462356341Scy        return length;
463356341Scy    }
464162021Ssam
465162021Ssam    if ((p[0] & MFR_BEC_MASK) == MFR_CTRL_FRAME && p[1] == 0) {
466276788Sdelphij        ND_PRINT((ndo, "FRF.16 Control, Flags [%s], %s, length %u",
467162021Ssam               bittok2str(frf_flag_values,"none",(p[0] & MFR_BEC_MASK)),
468162021Ssam               tok2str(mfr_ctrl_msg_values,"Unknown Message (0x%02x)",p[2]),
469276788Sdelphij               length));
470162021Ssam        tptr = p + 3;
471162021Ssam        tlen = length -3;
472162021Ssam        hdr_len = 3;
473162021Ssam
474276788Sdelphij        if (!ndo->ndo_vflag)
475162021Ssam            return hdr_len;
476162021Ssam
477162021Ssam        while (tlen>sizeof(struct ie_tlv_header_t)) {
478276788Sdelphij            ND_TCHECK2(*tptr, sizeof(struct ie_tlv_header_t));
479162021Ssam            ie_type=tptr[0];
480162021Ssam            ie_len=tptr[1];
481162021Ssam
482276788Sdelphij            ND_PRINT((ndo, "\n\tIE %s (%u), length %u: ",
483162021Ssam                   tok2str(mfr_ctrl_ie_values,"Unknown",ie_type),
484162021Ssam                   ie_type,
485276788Sdelphij                   ie_len));
486162021Ssam
487162021Ssam            /* infinite loop check */
488162021Ssam            if (ie_type == 0 || ie_len <= sizeof(struct ie_tlv_header_t))
489162021Ssam                return hdr_len;
490162021Ssam
491276788Sdelphij            ND_TCHECK2(*tptr, ie_len);
492162021Ssam            tptr+=sizeof(struct ie_tlv_header_t);
493162021Ssam            /* tlv len includes header */
494162021Ssam            ie_len-=sizeof(struct ie_tlv_header_t);
495162021Ssam            tlen-=sizeof(struct ie_tlv_header_t);
496162021Ssam
497162021Ssam            switch (ie_type) {
498162021Ssam
499162021Ssam            case MFR_CTRL_IE_MAGIC_NUM:
500356341Scy                /* FRF.16.1 Section 3.4.3 Magic Number Information Element */
501356341Scy                if (ie_len != 4) {
502356341Scy                    ND_PRINT((ndo, "(invalid length)"));
503356341Scy                    break;
504356341Scy                }
505276788Sdelphij                ND_PRINT((ndo, "0x%08x", EXTRACT_32BITS(tptr)));
506162021Ssam                break;
507162021Ssam
508162021Ssam            case MFR_CTRL_IE_BUNDLE_ID: /* same message format */
509162021Ssam            case MFR_CTRL_IE_LINK_ID:
510162021Ssam                for (idx = 0; idx < ie_len && idx < MFR_ID_STRING_MAXLEN; idx++) {
511162021Ssam                    if (*(tptr+idx) != 0) /* don't print null termination */
512276788Sdelphij                        safeputchar(ndo, *(tptr + idx));
513162021Ssam                    else
514162021Ssam                        break;
515162021Ssam                }
516162021Ssam                break;
517162021Ssam
518162021Ssam            case MFR_CTRL_IE_TIMESTAMP:
519162021Ssam                if (ie_len == sizeof(struct timeval)) {
520276788Sdelphij                    ts_print(ndo, (const struct timeval *)tptr);
521162021Ssam                    break;
522162021Ssam                }
523162021Ssam                /* fall through and hexdump if no unix timestamp */
524162021Ssam
525162021Ssam                /*
526162021Ssam                 * FIXME those are the defined IEs that lack a decoder
527162021Ssam                 * you are welcome to contribute code ;-)
528162021Ssam                 */
529162021Ssam
530162021Ssam            case MFR_CTRL_IE_VENDOR_EXT:
531162021Ssam            case MFR_CTRL_IE_CAUSE:
532162021Ssam
533162021Ssam            default:
534276788Sdelphij                if (ndo->ndo_vflag <= 1)
535276788Sdelphij                    print_unknown_data(ndo, tptr, "\n\t  ", ie_len);
536162021Ssam                break;
537162021Ssam            }
538162021Ssam
539162021Ssam            /* do we want to see a hexdump of the IE ? */
540276788Sdelphij            if (ndo->ndo_vflag > 1 )
541276788Sdelphij                print_unknown_data(ndo, tptr, "\n\t  ", ie_len);
542276788Sdelphij
543162021Ssam            tlen-=ie_len;
544162021Ssam            tptr+=ie_len;
545162021Ssam        }
546162021Ssam        return hdr_len;
547162021Ssam    }
548162021Ssam/*
549162021Ssam * FRF.16 Fragmentation Frame
550276788Sdelphij *
551162021Ssam *      7    6    5    4    3    2    1    0
552162021Ssam *    +----+----+----+----+----+----+----+----+
553162021Ssam *    | B  | E  | C=0|seq. (high 4 bits) | EA  |
554162021Ssam *    +----+----+----+----+----+----+----+----+
555162021Ssam *    |        sequence  (low 8 bits)         |
556162021Ssam *    +----+----+----+----+----+----+----+----+
557162021Ssam *    |        DLCI (6 bits)        | CR | EA  |
558162021Ssam *    +----+----+----+----+----+----+----+----+
559162021Ssam *    |   DLCI (4 bits)   |FECN|BECN| DE | EA |
560162021Ssam *    +----+----+----+----+----+----+----+----+
561162021Ssam */
562162021Ssam
563162021Ssam    sequence_num = (p[0]&0x1e)<<7 | p[1];
564162021Ssam    /* whole packet or first fragment ? */
565162021Ssam    if ((p[0] & MFR_BEC_MASK) == MFR_FRAG_FRAME ||
566162021Ssam        (p[0] & MFR_BEC_MASK) == MFR_B_BIT) {
567276788Sdelphij        ND_PRINT((ndo, "FRF.16 Frag, seq %u, Flags [%s], ",
568162021Ssam               sequence_num,
569276788Sdelphij               bittok2str(frf_flag_values,"none",(p[0] & MFR_BEC_MASK))));
570162021Ssam        hdr_len = 2;
571276788Sdelphij        fr_print(ndo, p+hdr_len,length-hdr_len);
572162021Ssam        return hdr_len;
573162021Ssam    }
574162021Ssam
575162021Ssam    /* must be a middle or the last fragment */
576276788Sdelphij    ND_PRINT((ndo, "FRF.16 Frag, seq %u, Flags [%s]",
577162021Ssam           sequence_num,
578276788Sdelphij           bittok2str(frf_flag_values,"none",(p[0] & MFR_BEC_MASK))));
579276788Sdelphij    print_unknown_data(ndo, p, "\n\t", length);
580162021Ssam
581162021Ssam    return hdr_len;
582162021Ssam
583162021Ssam trunc:
584276788Sdelphij    ND_PRINT((ndo, "[|mfr]"));
585162021Ssam    return length;
586162021Ssam}
587162021Ssam
588146778Ssam/* an NLPID of 0xb1 indicates a 2-byte
589146778Ssam * FRF.15 header
590276788Sdelphij *
591146778Ssam *      7    6    5    4    3    2    1    0
592146778Ssam *    +----+----+----+----+----+----+----+----+
593146778Ssam *    ~              Q.922 header             ~
594146778Ssam *    +----+----+----+----+----+----+----+----+
595146778Ssam *    |             NLPID (8 bits)            | NLPID=0xb1
596146778Ssam *    +----+----+----+----+----+----+----+----+
597146778Ssam *    | B  | E  | C  |seq. (high 4 bits) | R  |
598146778Ssam *    +----+----+----+----+----+----+----+----+
599146778Ssam *    |        sequence  (low 8 bits)         |
600146778Ssam *    +----+----+----+----+----+----+----+----+
601146778Ssam */
60232145Spst
603146778Ssam#define FR_FRF15_FRAGTYPE 0x01
604146778Ssam
605146778Ssamstatic void
606276788Sdelphijfrf15_print(netdissect_options *ndo,
607285275Spkelsey            const u_char *p, u_int length)
608285275Spkelsey{
609276788Sdelphij    uint16_t sequence_num, flags;
610276788Sdelphij
611313537Sglebius    if (length < 2)
612313537Sglebius        goto trunc;
613313537Sglebius    ND_TCHECK2(*p, 2);
614313537Sglebius
615162021Ssam    flags = p[0]&MFR_BEC_MASK;
616146778Ssam    sequence_num = (p[0]&0x1e)<<7 | p[1];
617146778Ssam
618276788Sdelphij    ND_PRINT((ndo, "FRF.15, seq 0x%03x, Flags [%s],%s Fragmentation, length %u",
619146778Ssam           sequence_num,
620162021Ssam           bittok2str(frf_flag_values,"none",flags),
621162021Ssam           p[0]&FR_FRF15_FRAGTYPE ? "Interface" : "End-to-End",
622276788Sdelphij           length));
623146778Ssam
624146778Ssam/* TODO:
625146778Ssam * depending on all permutations of the B, E and C bit
626146778Ssam * dig as deep as we can - e.g. on the first (B) fragment
627146778Ssam * there is enough payload to print the IP header
628146778Ssam * on non (B) fragments it depends if the fragmentation
629146778Ssam * model is end-to-end or interface based wether we want to print
630146778Ssam * another Q.922 header
631146778Ssam */
632313537Sglebius    return;
633146778Ssam
634313537Sglebiustrunc:
635313537Sglebius    ND_PRINT((ndo, "[|frf.15]"));
63632145Spst}
63732145Spst
63832145Spst/*
63932145Spst * Q.933 decoding portion for framerelay specific.
64032145Spst */
64132145Spst
64232145Spst/* Q.933 packet format
643276788Sdelphij                      Format of Other Protocols
64432145Spst                          using Q.933 NLPID
645276788Sdelphij                  +-------------------------------+
646276788Sdelphij                  |        Q.922 Address          |
64732145Spst                  +---------------+---------------+
648276788Sdelphij                  |Control  0x03  | NLPID   0x08  |
649276788Sdelphij                  +---------------+---------------+
65032145Spst                  |          L2 Protocol ID       |
65132145Spst                  | octet 1       |  octet 2      |
65232145Spst                  +-------------------------------+
65332145Spst                  |          L3 Protocol ID       |
65432145Spst                  | octet 2       |  octet 2      |
65532145Spst                  +-------------------------------+
65632145Spst                  |         Protocol Data         |
65732145Spst                  +-------------------------------+
65832145Spst                  | FCS                           |
65932145Spst                  +-------------------------------+
66032145Spst */
66132145Spst
66232145Spst/* L2 (Octet 1)- Call Reference Usually is 0x0 */
66332145Spst
66432145Spst/*
66532145Spst * L2 (Octet 2)- Message Types definition 1 byte long.
66632145Spst */
66732145Spst/* Call Establish */
66832145Spst#define MSG_TYPE_ESC_TO_NATIONAL  0x00
66932145Spst#define MSG_TYPE_ALERT            0x01
67032145Spst#define MSG_TYPE_CALL_PROCEEDING  0x02
67132145Spst#define MSG_TYPE_CONNECT          0x07
67232145Spst#define MSG_TYPE_CONNECT_ACK      0x0F
67332145Spst#define MSG_TYPE_PROGRESS         0x03
67432145Spst#define MSG_TYPE_SETUP            0x05
67532145Spst/* Call Clear */
67632145Spst#define MSG_TYPE_DISCONNECT       0x45
67732145Spst#define MSG_TYPE_RELEASE          0x4D
67832145Spst#define MSG_TYPE_RELEASE_COMPLETE 0x5A
67932145Spst#define MSG_TYPE_RESTART          0x46
68032145Spst#define MSG_TYPE_RESTART_ACK      0x4E
68132145Spst/* Status */
68232145Spst#define MSG_TYPE_STATUS           0x7D
68332145Spst#define MSG_TYPE_STATUS_ENQ       0x75
68432145Spst
685276788Sdelphijstatic const struct tok fr_q933_msg_values[] = {
686146778Ssam    { MSG_TYPE_ESC_TO_NATIONAL, "ESC to National" },
687146778Ssam    { MSG_TYPE_ALERT, "Alert" },
688146778Ssam    { MSG_TYPE_CALL_PROCEEDING, "Call proceeding" },
689146778Ssam    { MSG_TYPE_CONNECT, "Connect" },
690146778Ssam    { MSG_TYPE_CONNECT_ACK, "Connect ACK" },
691146778Ssam    { MSG_TYPE_PROGRESS, "Progress" },
692146778Ssam    { MSG_TYPE_SETUP, "Setup" },
693146778Ssam    { MSG_TYPE_DISCONNECT, "Disconnect" },
694146778Ssam    { MSG_TYPE_RELEASE, "Release" },
695146778Ssam    { MSG_TYPE_RELEASE_COMPLETE, "Release Complete" },
696146778Ssam    { MSG_TYPE_RESTART, "Restart" },
697146778Ssam    { MSG_TYPE_RESTART_ACK, "Restart ACK" },
698146778Ssam    { MSG_TYPE_STATUS, "Status Reply" },
699146778Ssam    { MSG_TYPE_STATUS_ENQ, "Status Enquiry" },
700146778Ssam    { 0, NULL }
701146778Ssam};
70232145Spst
703313537Sglebius#define IE_IS_SINGLE_OCTET(iecode)	((iecode) & 0x80)
704313537Sglebius#define IE_IS_SHIFT(iecode)		(((iecode) & 0xF0) == 0x90)
705313537Sglebius#define IE_SHIFT_IS_NON_LOCKING(iecode)	((iecode) & 0x08)
706313537Sglebius#define IE_SHIFT_IS_LOCKING(iecode)	(!(IE_SHIFT_IS_NON_LOCKING(iecode)))
707313537Sglebius#define IE_SHIFT_CODESET(iecode)	((iecode) & 0x07)
708146778Ssam
709146778Ssam#define FR_LMI_ANSI_REPORT_TYPE_IE	0x01
710146778Ssam#define FR_LMI_ANSI_LINK_VERIFY_IE_91	0x19 /* details? */
711146778Ssam#define FR_LMI_ANSI_LINK_VERIFY_IE	0x03
712146778Ssam#define FR_LMI_ANSI_PVC_STATUS_IE	0x07
713146778Ssam
714146778Ssam#define FR_LMI_CCITT_REPORT_TYPE_IE	0x51
715146778Ssam#define FR_LMI_CCITT_LINK_VERIFY_IE	0x53
716146778Ssam#define FR_LMI_CCITT_PVC_STATUS_IE	0x57
717146778Ssam
718313537Sglebiusstatic const struct tok fr_q933_ie_values_codeset_0_5[] = {
719146778Ssam    { FR_LMI_ANSI_REPORT_TYPE_IE, "ANSI Report Type" },
720146778Ssam    { FR_LMI_ANSI_LINK_VERIFY_IE_91, "ANSI Link Verify" },
721146778Ssam    { FR_LMI_ANSI_LINK_VERIFY_IE, "ANSI Link Verify" },
722146778Ssam    { FR_LMI_ANSI_PVC_STATUS_IE, "ANSI PVC Status" },
723146778Ssam    { FR_LMI_CCITT_REPORT_TYPE_IE, "CCITT Report Type" },
724146778Ssam    { FR_LMI_CCITT_LINK_VERIFY_IE, "CCITT Link Verify" },
725146778Ssam    { FR_LMI_CCITT_PVC_STATUS_IE, "CCITT PVC Status" },
726146778Ssam    { 0, NULL }
72732145Spst};
72832145Spst
729146778Ssam#define FR_LMI_REPORT_TYPE_IE_FULL_STATUS 0
730146778Ssam#define FR_LMI_REPORT_TYPE_IE_LINK_VERIFY 1
731146778Ssam#define FR_LMI_REPORT_TYPE_IE_ASYNC_PVC   2
73232145Spst
733276788Sdelphijstatic const struct tok fr_lmi_report_type_ie_values[] = {
734146778Ssam    { FR_LMI_REPORT_TYPE_IE_FULL_STATUS, "Full Status" },
735146778Ssam    { FR_LMI_REPORT_TYPE_IE_LINK_VERIFY, "Link verify" },
736146778Ssam    { FR_LMI_REPORT_TYPE_IE_ASYNC_PVC, "Async PVC Status" },
737146778Ssam    { 0, NULL }
738146778Ssam};
73932145Spst
740313537Sglebius/* array of 16 codesets - currently we only support codepage 0 and 5 */
741276788Sdelphijstatic const struct tok *fr_q933_ie_codesets[] = {
742313537Sglebius    fr_q933_ie_values_codeset_0_5,
743146778Ssam    NULL,
744146778Ssam    NULL,
745146778Ssam    NULL,
746146778Ssam    NULL,
747313537Sglebius    fr_q933_ie_values_codeset_0_5,
748146778Ssam    NULL,
749146778Ssam    NULL,
750146778Ssam    NULL,
751146778Ssam    NULL,
752146778Ssam    NULL,
753146778Ssam    NULL,
754146778Ssam    NULL,
755146778Ssam    NULL,
756146778Ssam    NULL,
757146778Ssam    NULL
75832145Spst};
75932145Spst
760313537Sglebiusstatic int fr_q933_print_ie_codeset_0_5(netdissect_options *ndo, u_int iecode,
761313537Sglebius    u_int ielength, const u_char *p);
762147904Ssam
763313537Sglebiustypedef int (*codeset_pr_func_t)(netdissect_options *, u_int iecode,
764313537Sglebius    u_int ielength, const u_char *p);
765147904Ssam
766313537Sglebius/* array of 16 codesets - currently we only support codepage 0 and 5 */
767276788Sdelphijstatic const codeset_pr_func_t fr_q933_print_ie_codeset[] = {
768313537Sglebius    fr_q933_print_ie_codeset_0_5,
769147904Ssam    NULL,
770147904Ssam    NULL,
771147904Ssam    NULL,
772147904Ssam    NULL,
773313537Sglebius    fr_q933_print_ie_codeset_0_5,
774147904Ssam    NULL,
775147904Ssam    NULL,
776147904Ssam    NULL,
777147904Ssam    NULL,
778147904Ssam    NULL,
779147904Ssam    NULL,
780147904Ssam    NULL,
781147904Ssam    NULL,
782147904Ssam    NULL,
783147904Ssam    NULL
784147904Ssam};
785147904Ssam
786313537Sglebius/*
787313537Sglebius * ITU-T Q.933.
788313537Sglebius *
789313537Sglebius * p points to octet 2, the octet containing the length of the
790313537Sglebius * call reference value, so p[n] is octet n+2 ("octet X" is as
791313537Sglebius * used in Q.931/Q.933).
792313537Sglebius *
793313537Sglebius * XXX - actually used both for Q.931 and Q.933.
794313537Sglebius */
79532145Spstvoid
796276788Sdelphijq933_print(netdissect_options *ndo,
797276788Sdelphij           const u_char *p, u_int length)
79832145Spst{
799313537Sglebius	u_int olen;
800313537Sglebius	u_int call_ref_length, i;
801313537Sglebius	uint8_t call_ref[15];	/* maximum length - length field is 4 bits */
802313537Sglebius	u_int msgtype;
803313537Sglebius	u_int iecode;
804313537Sglebius	u_int ielength;
805313537Sglebius	u_int codeset = 0;
806313537Sglebius	u_int is_ansi = 0;
807313537Sglebius	u_int ie_is_known;
808313537Sglebius	u_int non_locking_shift;
809313537Sglebius	u_int unshift_codeset;
810146778Ssam
811313537Sglebius	ND_PRINT((ndo, "%s", ndo->ndo_eflag ? "" : "Q.933"));
812313537Sglebius
813313537Sglebius	if (length == 0 || !ND_TTEST(*p)) {
814313537Sglebius		if (!ndo->ndo_eflag)
815313537Sglebius			ND_PRINT((ndo, ", "));
816313537Sglebius		ND_PRINT((ndo, "length %u", length));
817313537Sglebius		goto trunc;
818146778Ssam	}
819146778Ssam
820313537Sglebius	/*
821313537Sglebius	 * Get the length of the call reference value.
822313537Sglebius	 */
823313537Sglebius	olen = length; /* preserve the original length for display */
824313537Sglebius	call_ref_length = (*p) & 0x0f;
825313537Sglebius	p++;
826313537Sglebius	length--;
827146778Ssam
828313537Sglebius	/*
829313537Sglebius	 * Get the call reference value.
830313537Sglebius	 */
831313537Sglebius	for (i = 0; i < call_ref_length; i++) {
832313537Sglebius		if (length == 0 || !ND_TTEST(*p)) {
833313537Sglebius			if (!ndo->ndo_eflag)
834313537Sglebius				ND_PRINT((ndo, ", "));
835313537Sglebius			ND_PRINT((ndo, "length %u", olen));
836313537Sglebius			goto trunc;
837313537Sglebius		}
838313537Sglebius		call_ref[i] = *p;
839313537Sglebius		p++;
840313537Sglebius		length--;
841190207Srpaulo	}
84232145Spst
843313537Sglebius	/*
844313537Sglebius	 * Get the message type.
845313537Sglebius	 */
846313537Sglebius	if (length == 0 || !ND_TTEST(*p)) {
847313537Sglebius		if (!ndo->ndo_eflag)
848313537Sglebius			ND_PRINT((ndo, ", "));
849313537Sglebius		ND_PRINT((ndo, "length %u", olen));
850313537Sglebius		goto trunc;
851313537Sglebius	}
852313537Sglebius	msgtype = *p;
853313537Sglebius	p++;
854313537Sglebius	length--;
855276788Sdelphij
856313537Sglebius	/*
857313537Sglebius	 * Peek ahead to see if we start with a shift.
858313537Sglebius	 */
859313537Sglebius	non_locking_shift = 0;
860313537Sglebius	unshift_codeset = codeset;
861313537Sglebius	if (length != 0) {
862313537Sglebius		if (!ND_TTEST(*p)) {
863313537Sglebius			if (!ndo->ndo_eflag)
864313537Sglebius				ND_PRINT((ndo, ", "));
865313537Sglebius			ND_PRINT((ndo, "length %u", olen));
866313537Sglebius			goto trunc;
867313537Sglebius		}
868313537Sglebius		iecode = *p;
869313537Sglebius		if (IE_IS_SHIFT(iecode)) {
870313537Sglebius			/*
871313537Sglebius			 * It's a shift.  Skip over it.
872313537Sglebius			 */
873313537Sglebius			p++;
874313537Sglebius			length--;
875313537Sglebius
876313537Sglebius			/*
877313537Sglebius			 * Get the codeset.
878313537Sglebius			 */
879313537Sglebius			codeset = IE_SHIFT_CODESET(iecode);
880313537Sglebius
881313537Sglebius			/*
882313537Sglebius			 * If it's a locking shift to codeset 5,
883313537Sglebius			 * mark this as ANSI.  (XXX - 5 is actually
884313537Sglebius			 * for national variants in general, not
885313537Sglebius			 * the US variant in particular, but maybe
886313537Sglebius			 * this is more American exceptionalism. :-))
887313537Sglebius			 */
888313537Sglebius			if (IE_SHIFT_IS_LOCKING(iecode)) {
889313537Sglebius				/*
890313537Sglebius				 * It's a locking shift.
891313537Sglebius				 */
892313537Sglebius				if (codeset == 5) {
893313537Sglebius					/*
894313537Sglebius					 * It's a locking shift to
895313537Sglebius					 * codeset 5, so this is
896313537Sglebius					 * T1.617 Annex D.
897313537Sglebius					 */
898313537Sglebius					is_ansi = 1;
899313537Sglebius				}
900313537Sglebius			} else {
901313537Sglebius				/*
902313537Sglebius				 * It's a non-locking shift.
903313537Sglebius				 * Remember the current codeset, so we
904313537Sglebius				 * can revert to it after the next IE.
905313537Sglebius				 */
906313537Sglebius				non_locking_shift = 1;
907313537Sglebius				unshift_codeset = 0;
908313537Sglebius			}
909313537Sglebius		}
910313537Sglebius	}
911313537Sglebius
91232145Spst	/* printing out header part */
913313537Sglebius	if (!ndo->ndo_eflag)
914313537Sglebius		ND_PRINT((ndo, ", "));
915276788Sdelphij	ND_PRINT((ndo, "%s, codeset %u", is_ansi ? "ANSI" : "CCITT", codeset));
916146778Ssam
917313537Sglebius	if (call_ref_length != 0) {
918313537Sglebius		ND_TCHECK(p[0]);
919313537Sglebius		if (call_ref_length > 1 || p[0] != 0) {
920313537Sglebius			/*
921313537Sglebius			 * Not a dummy call reference.
922313537Sglebius			 */
923313537Sglebius			ND_PRINT((ndo, ", Call Ref: 0x"));
924313537Sglebius			for (i = 0; i < call_ref_length; i++)
925313537Sglebius				ND_PRINT((ndo, "%02x", call_ref[i]));
926313537Sglebius		}
927190207Srpaulo	}
928313537Sglebius	if (ndo->ndo_vflag) {
929313537Sglebius		ND_PRINT((ndo, ", %s (0x%02x), length %u",
930313537Sglebius		   tok2str(fr_q933_msg_values,
931313537Sglebius			"unknown message", msgtype),
932313537Sglebius		   msgtype,
933313537Sglebius		   olen));
934313537Sglebius	} else {
935313537Sglebius		ND_PRINT((ndo, ", %s",
936190207Srpaulo		       tok2str(fr_q933_msg_values,
937313537Sglebius			       "unknown message 0x%02x", msgtype)));
938190207Srpaulo	}
939146778Ssam
940313537Sglebius	/* Loop through the rest of the IEs */
941313537Sglebius	while (length != 0) {
942313537Sglebius		/*
943313537Sglebius		 * What's the state of any non-locking shifts?
944313537Sglebius		 */
945313537Sglebius		if (non_locking_shift == 1) {
946313537Sglebius			/*
947313537Sglebius			 * There's a non-locking shift in effect for
948313537Sglebius			 * this IE.  Count it, so we reset the codeset
949313537Sglebius			 * before the next IE.
950313537Sglebius			 */
951313537Sglebius			non_locking_shift = 2;
952313537Sglebius		} else if (non_locking_shift == 2) {
953313537Sglebius			/*
954313537Sglebius			 * Unshift.
955313537Sglebius			 */
956313537Sglebius			codeset = unshift_codeset;
957313537Sglebius			non_locking_shift = 0;
95832145Spst		}
95932145Spst
960313537Sglebius		/*
961313537Sglebius		 * Get the first octet of the IE.
962313537Sglebius		 */
963313537Sglebius		if (!ND_TTEST(*p)) {
964313537Sglebius			if (!ndo->ndo_vflag) {
965313537Sglebius				ND_PRINT((ndo, ", length %u", olen));
966313537Sglebius			}
967313537Sglebius			goto trunc;
968190207Srpaulo		}
969313537Sglebius		iecode = *p;
970313537Sglebius		p++;
971313537Sglebius		length--;
972190207Srpaulo
973313537Sglebius		/* Single-octet IE? */
974313537Sglebius		if (IE_IS_SINGLE_OCTET(iecode)) {
975313537Sglebius			/*
976313537Sglebius			 * Yes.  Is it a shift?
977313537Sglebius			 */
978313537Sglebius			if (IE_IS_SHIFT(iecode)) {
979313537Sglebius				/*
980313537Sglebius				 * Yes.  Is it locking?
981313537Sglebius				 */
982313537Sglebius				if (IE_SHIFT_IS_LOCKING(iecode)) {
983313537Sglebius					/*
984313537Sglebius					 * Yes.
985313537Sglebius					 */
986313537Sglebius					non_locking_shift = 0;
987313537Sglebius				} else {
988313537Sglebius					/*
989313537Sglebius					 * No.  Remember the current
990313537Sglebius					 * codeset, so we can revert
991313537Sglebius					 * to it after the next IE.
992313537Sglebius					 */
993313537Sglebius					non_locking_shift = 1;
994313537Sglebius					unshift_codeset = codeset;
995313537Sglebius				}
99632145Spst
997313537Sglebius				/*
998313537Sglebius				 * Get the codeset.
999313537Sglebius				 */
1000313537Sglebius				codeset = IE_SHIFT_CODESET(iecode);
1001313537Sglebius			}
1002313537Sglebius		} else {
1003313537Sglebius			/*
1004313537Sglebius			 * No.  Get the IE length.
1005313537Sglebius			 */
1006313537Sglebius			if (length == 0 || !ND_TTEST(*p)) {
1007313537Sglebius				if (!ndo->ndo_vflag) {
1008313537Sglebius					ND_PRINT((ndo, ", length %u", olen));
1009313537Sglebius				}
1010313537Sglebius				goto trunc;
1011313537Sglebius			}
1012313537Sglebius			ielength = *p;
1013313537Sglebius			p++;
1014313537Sglebius			length--;
1015190207Srpaulo
1016313537Sglebius			/* lets do the full IE parsing only in verbose mode
1017313537Sglebius			 * however some IEs (DLCI Status, Link Verify)
1018313537Sglebius			 * are also interesting in non-verbose mode */
1019313537Sglebius			if (ndo->ndo_vflag) {
1020313537Sglebius				ND_PRINT((ndo, "\n\t%s IE (0x%02x), length %u: ",
1021313537Sglebius				    tok2str(fr_q933_ie_codesets[codeset],
1022313537Sglebius					"unknown", iecode),
1023313537Sglebius				    iecode,
1024313537Sglebius				    ielength));
1025313537Sglebius			}
1026162021Ssam
1027313537Sglebius			/* sanity checks */
1028313537Sglebius			if (iecode == 0 || ielength == 0) {
1029313537Sglebius				return;
1030313537Sglebius			}
1031313537Sglebius			if (length < ielength || !ND_TTEST2(*p, ielength)) {
1032313537Sglebius				if (!ndo->ndo_vflag) {
1033313537Sglebius					ND_PRINT((ndo, ", length %u", olen));
1034313537Sglebius				}
1035313537Sglebius				goto trunc;
1036313537Sglebius			}
1037313537Sglebius
1038313537Sglebius			ie_is_known = 0;
1039313537Sglebius			if (fr_q933_print_ie_codeset[codeset] != NULL) {
1040313537Sglebius				ie_is_known = fr_q933_print_ie_codeset[codeset](ndo, iecode, ielength, p);
1041313537Sglebius			}
1042313537Sglebius
1043313537Sglebius			if (ie_is_known) {
1044313537Sglebius				/*
1045313537Sglebius				 * Known IE; do we want to see a hexdump
1046313537Sglebius				 * of it?
1047313537Sglebius				 */
1048313537Sglebius				if (ndo->ndo_vflag > 1) {
1049313537Sglebius					/* Yes. */
1050313537Sglebius					print_unknown_data(ndo, p, "\n\t  ", ielength);
1051313537Sglebius				}
1052313537Sglebius			} else {
1053313537Sglebius				/*
1054313537Sglebius				 * Unknown IE; if we're printing verbosely,
1055313537Sglebius				 * print its content in hex.
1056313537Sglebius				 */
1057313537Sglebius				if (ndo->ndo_vflag >= 1) {
1058313537Sglebius					print_unknown_data(ndo, p, "\n\t", ielength);
1059313537Sglebius				}
1060313537Sglebius			}
1061313537Sglebius
1062313537Sglebius			length -= ielength;
1063313537Sglebius			p += ielength;
1064190207Srpaulo		}
1065146778Ssam	}
1066313537Sglebius	if (!ndo->ndo_vflag) {
1067313537Sglebius	    ND_PRINT((ndo, ", length %u", olen));
1068190207Srpaulo	}
1069313537Sglebius	return;
1070313537Sglebius
1071313537Sglebiustrunc:
1072313537Sglebius	ND_PRINT((ndo, "[|q.933]"));
1073146778Ssam}
1074147904Ssam
1075147904Ssamstatic int
1076313537Sglebiusfr_q933_print_ie_codeset_0_5(netdissect_options *ndo, u_int iecode,
1077313537Sglebius                          u_int ielength, const u_char *p)
1078147904Ssam{
1079147904Ssam        u_int dlci;
1080147904Ssam
1081313537Sglebius        switch (iecode) {
1082147904Ssam
1083147904Ssam        case FR_LMI_ANSI_REPORT_TYPE_IE: /* fall through */
1084147904Ssam        case FR_LMI_CCITT_REPORT_TYPE_IE:
1085313537Sglebius            if (ielength < 1) {
1086313537Sglebius                if (!ndo->ndo_vflag) {
1087313537Sglebius                    ND_PRINT((ndo, ", "));
1088313537Sglebius	        }
1089313537Sglebius                ND_PRINT((ndo, "Invalid REPORT TYPE IE"));
1090313537Sglebius                return 1;
1091313537Sglebius            }
1092276788Sdelphij            if (ndo->ndo_vflag) {
1093276788Sdelphij                ND_PRINT((ndo, "%s (%u)",
1094313537Sglebius                       tok2str(fr_lmi_report_type_ie_values,"unknown",p[0]),
1095313537Sglebius                       p[0]));
1096190207Srpaulo	    }
1097147904Ssam            return 1;
1098147904Ssam
1099147904Ssam        case FR_LMI_ANSI_LINK_VERIFY_IE: /* fall through */
1100147904Ssam        case FR_LMI_CCITT_LINK_VERIFY_IE:
1101147904Ssam        case FR_LMI_ANSI_LINK_VERIFY_IE_91:
1102276788Sdelphij            if (!ndo->ndo_vflag) {
1103276788Sdelphij                ND_PRINT((ndo, ", "));
1104190207Srpaulo	    }
1105313537Sglebius            if (ielength < 2) {
1106313537Sglebius                ND_PRINT((ndo, "Invalid LINK VERIFY IE"));
1107313537Sglebius                return 1;
1108313537Sglebius            }
1109313537Sglebius            ND_PRINT((ndo, "TX Seq: %3d, RX Seq: %3d", p[0], p[1]));
1110147904Ssam            return 1;
1111147904Ssam
1112147904Ssam        case FR_LMI_ANSI_PVC_STATUS_IE: /* fall through */
1113147904Ssam        case FR_LMI_CCITT_PVC_STATUS_IE:
1114276788Sdelphij            if (!ndo->ndo_vflag) {
1115276788Sdelphij                ND_PRINT((ndo, ", "));
1116190207Srpaulo	    }
1117276788Sdelphij            /* now parse the DLCI information element. */
1118313537Sglebius            if ((ielength < 3) ||
1119313537Sglebius                (p[0] & 0x80) ||
1120313537Sglebius                ((ielength == 3) && !(p[1] & 0x80)) ||
1121313537Sglebius                ((ielength == 4) && ((p[1] & 0x80) || !(p[2] & 0x80))) ||
1122313537Sglebius                ((ielength == 5) && ((p[1] & 0x80) || (p[2] & 0x80) ||
1123313537Sglebius                                   !(p[3] & 0x80))) ||
1124313537Sglebius                (ielength > 5) ||
1125313537Sglebius                !(p[ielength - 1] & 0x80)) {
1126313537Sglebius                ND_PRINT((ndo, "Invalid DLCI in PVC STATUS IE"));
1127313537Sglebius                return 1;
1128190207Srpaulo	    }
1129276788Sdelphij
1130313537Sglebius            dlci = ((p[0] & 0x3F) << 4) | ((p[1] & 0x78) >> 3);
1131313537Sglebius            if (ielength == 4) {
1132313537Sglebius                dlci = (dlci << 6) | ((p[2] & 0x7E) >> 1);
1133190207Srpaulo	    }
1134313537Sglebius            else if (ielength == 5) {
1135313537Sglebius                dlci = (dlci << 13) | (p[2] & 0x7F) | ((p[3] & 0x7E) >> 1);
1136190207Srpaulo	    }
1137147904Ssam
1138276788Sdelphij            ND_PRINT((ndo, "DLCI %u: status %s%s", dlci,
1139313537Sglebius                    p[ielength - 1] & 0x8 ? "New, " : "",
1140313537Sglebius                    p[ielength - 1] & 0x2 ? "Active" : "Inactive"));
1141147904Ssam            return 1;
1142147904Ssam	}
1143147904Ssam
1144147904Ssam        return 0;
1145147904Ssam}
1146276788Sdelphij/*
1147276788Sdelphij * Local Variables:
1148276788Sdelphij * c-style: whitesmith
1149276788Sdelphij * c-basic-offset: 8
1150276788Sdelphij * End:
1151276788Sdelphij */
1152