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.
2066644Skris *
2166644Skris * $FreeBSD$
2232145Spst */
2332145Spst
2432145Spst#ifndef lint
25146778Ssamstatic const char rcsid[] _U_ =
26190207Srpaulo	"@(#)$Header: /tcpdump/master/tcpdump/print-fr.c,v 1.51 2006-06-23 22:20:32 hannes Exp $ (LBL)";
2732145Spst#endif
2832145Spst
29146778Ssam#ifdef HAVE_CONFIG_H
30146778Ssam#include "config.h"
3132145Spst#endif
3232145Spst
33146778Ssam#include <tcpdump-stdinc.h>
3432145Spst
35146778Ssam#include <stdio.h>
36146778Ssam#include <string.h>
3732145Spst#include <pcap.h>
3832145Spst
39146778Ssam#include "addrtoname.h"
40146778Ssam#include "interface.h"
4132145Spst#include "ethertype.h"
42146778Ssam#include "nlpid.h"
43146778Ssam#include "extract.h"
44146778Ssam#include "oui.h"
4532145Spst
46146778Ssamstatic void frf15_print(const u_char *, u_int);
4732145Spst
48146778Ssam/*
49146778Ssam * the frame relay header has a variable length
50146778Ssam *
51146778Ssam * the EA bit determines if there is another byte
52146778Ssam * in the header
53146778Ssam *
54146778Ssam * minimum header length is 2 bytes
55146778Ssam * maximum header length is 4 bytes
56146778Ssam *
57146778Ssam *      7    6    5    4    3    2    1    0
58146778Ssam *    +----+----+----+----+----+----+----+----+
59146778Ssam *    |        DLCI (6 bits)        | CR | EA |
60146778Ssam *    +----+----+----+----+----+----+----+----+
61146778Ssam *    |   DLCI (4 bits)   |FECN|BECN| DE | EA |
62146778Ssam *    +----+----+----+----+----+----+----+----+
63146778Ssam *    |           DLCI (7 bits)          | EA |
64146778Ssam *    +----+----+----+----+----+----+----+----+
65146778Ssam *    |        DLCI (6 bits)        |SDLC| EA |
66146778Ssam *    +----+----+----+----+----+----+----+----+
67146778Ssam */
6832145Spst
69146778Ssam#define FR_EA_BIT	0x01
7032145Spst
71146778Ssam#define FR_CR_BIT       0x02000000
72146778Ssam#define FR_DE_BIT	0x00020000
73146778Ssam#define FR_BECN_BIT	0x00040000
74146778Ssam#define FR_FECN_BIT	0x00080000
75146778Ssam#define FR_SDLC_BIT	0x00000002
7632145Spst
77146778Ssam
78146778Ssamstruct tok fr_header_flag_values[] = {
79146778Ssam    { FR_CR_BIT, "C!" },
80146778Ssam    { FR_DE_BIT, "DE" },
81146778Ssam    { FR_BECN_BIT, "BECN" },
82146778Ssam    { FR_FECN_BIT, "FECN" },
83146778Ssam    { FR_SDLC_BIT, "sdlcore" },
84146778Ssam    { 0, NULL }
8532145Spst};
8632145Spst
87162021Ssam/* FRF.15 / FRF.16 */
88162021Ssam#define MFR_B_BIT 0x80
89162021Ssam#define MFR_E_BIT 0x40
90162021Ssam#define MFR_C_BIT 0x20
91162021Ssam#define MFR_BEC_MASK    (MFR_B_BIT | MFR_E_BIT | MFR_C_BIT)
92162021Ssam#define MFR_CTRL_FRAME  (MFR_B_BIT | MFR_E_BIT | MFR_C_BIT)
93162021Ssam#define MFR_FRAG_FRAME  (MFR_B_BIT | MFR_E_BIT )
94146778Ssam
95162021Ssamstruct tok frf_flag_values[] = {
96162021Ssam    { MFR_B_BIT, "Begin" },
97162021Ssam    { MFR_E_BIT, "End" },
98162021Ssam    { MFR_C_BIT, "Control" },
99162021Ssam    { 0, NULL }
100162021Ssam};
101162021Ssam
102146778Ssam/* Finds out Q.922 address length, DLCI and flags. Returns 0 on success
103146778Ssam * save the flags dep. on address length
104146778Ssam */
105190207Srpaulostatic int parse_q922_addr(const u_char *p, u_int *dlci,
106146778Ssam                           u_int *addr_len, u_int8_t *flags)
10732145Spst{
108146778Ssam	if ((p[0] & FR_EA_BIT))
109146778Ssam		return -1;
11032145Spst
111146778Ssam	*addr_len = 2;
112146778Ssam	*dlci = ((p[0] & 0xFC) << 2) | ((p[1] & 0xF0) >> 4);
11332145Spst
114146778Ssam        flags[0] = p[0] & 0x02; /* populate the first flag fields */
115146778Ssam        flags[1] = p[1] & 0x0c;
116172686Smlaier        flags[2] = 0;           /* clear the rest of the flags */
117172686Smlaier        flags[3] = 0;
11832145Spst
119146778Ssam	if (p[1] & FR_EA_BIT)
120146778Ssam		return 0;	/* 2-byte Q.922 address */
12132145Spst
122146778Ssam	p += 2;
123146778Ssam	(*addr_len)++;		/* 3- or 4-byte Q.922 address */
124146778Ssam	if ((p[0] & FR_EA_BIT) == 0) {
125146778Ssam		*dlci = (*dlci << 7) | (p[0] >> 1);
126146778Ssam		(*addr_len)++;	/* 4-byte Q.922 address */
127146778Ssam		p++;
128146778Ssam	}
12932145Spst
130146778Ssam	if ((p[0] & FR_EA_BIT) == 0)
131146778Ssam		return -1; /* more than 4 bytes of Q.922 address? */
132146778Ssam
133146778Ssam        flags[3] = p[0] & 0x02;
134146778Ssam
135190207Srpaulo        *dlci = (*dlci << 6) | (p[0] >> 2);
136146778Ssam
137146778Ssam	return 0;
13832145Spst}
13932145Spst
140190207Srpaulochar *q922_string(const u_char *p) {
141190207Srpaulo
142190207Srpaulo    static u_int dlci, addr_len;
143190207Srpaulo    static u_int8_t flags[4];
144190207Srpaulo    static char buffer[sizeof("DLCI xxxxxxxxxx")];
145190207Srpaulo    memset(buffer, 0, sizeof(buffer));
146190207Srpaulo
147190207Srpaulo    if (parse_q922_addr(p, &dlci, &addr_len, flags) == 0){
148190207Srpaulo        snprintf(buffer, sizeof(buffer), "DLCI %u", dlci);
149190207Srpaulo    }
150190207Srpaulo
151190207Srpaulo    return buffer;
152190207Srpaulo}
153190207Srpaulo
154190207Srpaulo
155146778Ssam/* Frame Relay packet structure, with flags and CRC removed
15632145Spst
15732145Spst                  +---------------------------+
15832145Spst                  |       Q.922 Address*      |
15932145Spst                  +--                       --+
16032145Spst                  |                           |
16132145Spst                  +---------------------------+
16232145Spst                  | Control (UI = 0x03)       |
16332145Spst                  +---------------------------+
16432145Spst                  | Optional Pad      (0x00)  |
16532145Spst                  +---------------------------+
16632145Spst                  | NLPID                     |
16732145Spst                  +---------------------------+
16832145Spst                  |             .             |
16932145Spst                  |             .             |
17032145Spst                  |             .             |
17132145Spst                  |           Data            |
17232145Spst                  |             .             |
17332145Spst                  |             .             |
17432145Spst                  +---------------------------+
17532145Spst
17632145Spst           * Q.922 addresses, as presently defined, are two octets and
17732145Spst             contain a 10-bit DLCI.  In some networks Q.922 addresses
17832145Spst             may optionally be increased to three or four octets.
17932145Spst*/
18032145Spst
181146778Ssamstatic u_int
182147904Ssamfr_hdrlen(const u_char *p, u_int addr_len)
18332145Spst{
184147904Ssam	if (!p[addr_len + 1] /* pad exist */)
185146778Ssam		return addr_len + 1 /* UI */ + 1 /* pad */ + 1 /* NLPID */;
18632145Spst	else
187146778Ssam		return addr_len + 1 /* UI */ + 1 /* NLPID */;
18832145Spst}
18932145Spst
190146778Ssamstatic void
191146778Ssamfr_hdr_print(int length, u_int addr_len, u_int dlci, u_int8_t *flags, u_int16_t nlpid)
19232145Spst{
193146778Ssam    if (qflag) {
194146778Ssam        (void)printf("Q.922, DLCI %u, length %u: ",
195146778Ssam                     dlci,
196146778Ssam                     length);
197146778Ssam    } else {
198146778Ssam        if (nlpid <= 0xff) /* if its smaller than 256 then its a NLPID */
199146778Ssam            (void)printf("Q.922, hdr-len %u, DLCI %u, Flags [%s], NLPID %s (0x%02x), length %u: ",
200146778Ssam                         addr_len,
201146778Ssam                         dlci,
202146778Ssam                         bittok2str(fr_header_flag_values, "none", EXTRACT_32BITS(flags)),
203146778Ssam                         tok2str(nlpid_values,"unknown", nlpid),
204146778Ssam                         nlpid,
205146778Ssam                         length);
206146778Ssam        else /* must be an ethertype */
207146778Ssam            (void)printf("Q.922, hdr-len %u, DLCI %u, Flags [%s], cisco-ethertype %s (0x%04x), length %u: ",
208146778Ssam                         addr_len,
209146778Ssam                         dlci,
210146778Ssam                         bittok2str(fr_header_flag_values, "none", EXTRACT_32BITS(flags)),
211146778Ssam                         tok2str(ethertype_values, "unknown", nlpid),
212146778Ssam                         nlpid,
213146778Ssam                         length);
214146778Ssam    }
21532145Spst}
21632145Spst
217146778Ssamu_int
218146778Ssamfr_if_print(const struct pcap_pkthdr *h, register const u_char *p)
21932145Spst{
22032145Spst	register u_int length = h->len;
22132145Spst	register u_int caplen = h->caplen;
222147904Ssam
223147904Ssam        TCHECK2(*p, 4); /* minimum frame header length */
224147904Ssam
225147904Ssam        if ((length = fr_print(p, length)) == 0)
226147904Ssam            return (0);
227147904Ssam        else
228147904Ssam            return length;
229147904Ssam trunc:
230147904Ssam        printf("[|fr]");
231147904Ssam        return caplen;
232147904Ssam}
233147904Ssam
234147904Ssamu_int
235147904Ssamfr_print(register const u_char *p, u_int length)
236147904Ssam{
237146778Ssam	u_int16_t extracted_ethertype;
238146778Ssam	u_int dlci;
239146778Ssam	u_int addr_len;
240146778Ssam	u_int16_t nlpid;
241146778Ssam	u_int hdr_len;
242146778Ssam	u_int8_t flags[4];
24332145Spst
244190207Srpaulo	if (parse_q922_addr(p, &dlci, &addr_len, flags)) {
245146778Ssam		printf("Q.922, invalid address");
246147904Ssam		return 0;
247146778Ssam	}
24832145Spst
249147904Ssam        TCHECK2(*p,addr_len+1+1);
250147904Ssam	hdr_len = fr_hdrlen(p, addr_len);
251147904Ssam        TCHECK2(*p,hdr_len);
252146778Ssam
253146778Ssam	if (p[addr_len] != 0x03 && dlci != 0) {
25432145Spst
255146778Ssam                /* lets figure out if we have cisco style encapsulation: */
256146778Ssam                extracted_ethertype = EXTRACT_16BITS(p+addr_len);
257146778Ssam
258146778Ssam                if (eflag)
259146778Ssam                    fr_hdr_print(length, addr_len, dlci, flags, extracted_ethertype);
260146778Ssam
261235530Sdelphij                if (ethertype_print(gndo, extracted_ethertype,
262146778Ssam                                      p+addr_len+ETHERTYPE_LEN,
263146778Ssam                                      length-addr_len-ETHERTYPE_LEN,
264214478Srpaulo                                      length-addr_len-ETHERTYPE_LEN) == 0)
265146778Ssam                    /* ether_type not known, probably it wasn't one */
266146778Ssam                    printf("UI %02x! ", p[addr_len]);
267146778Ssam                else
268146778Ssam                    return hdr_len;
269146778Ssam        }
270146778Ssam
271146778Ssam	if (!p[addr_len + 1]) {	/* pad byte should be used with 3-byte Q.922 */
272146778Ssam		if (addr_len != 3)
273146778Ssam			printf("Pad! ");
274146778Ssam	} else if (addr_len == 3)
275146778Ssam		printf("No pad! ");
276146778Ssam
277146778Ssam	nlpid = p[hdr_len - 1];
278146778Ssam
27932145Spst	if (eflag)
280146778Ssam		fr_hdr_print(length, addr_len, dlci, flags, nlpid);
281146778Ssam	p += hdr_len;
282146778Ssam	length -= hdr_len;
28332145Spst
284146778Ssam	switch (nlpid) {
28532145Spst	case NLPID_IP:
286146778Ssam	        ip_print(gndo, p, length);
28732145Spst		break;
288146778Ssam
289146778Ssam#ifdef INET6
290146778Ssam	case NLPID_IP6:
291235530Sdelphij		ip6_print(gndo, p, length);
292146778Ssam		break;
293146778Ssam#endif
29432145Spst	case NLPID_CLNP:
29532145Spst	case NLPID_ESIS:
29632145Spst	case NLPID_ISIS:
297147904Ssam                isoclns_print(p-1, length+1, length+1); /* OSI printers need the NLPID field */
29832145Spst		break;
299146778Ssam
300146778Ssam	case NLPID_SNAP:
301214478Srpaulo		if (snap_print(p, length, length, 0) == 0) {
302146778Ssam			/* ether_type not known, print raw packet */
303146778Ssam                        if (!eflag)
304146778Ssam                            fr_hdr_print(length + hdr_len, hdr_len,
305146778Ssam                                         dlci, flags, nlpid);
306162021Ssam			if (!suppress_default_print)
307147904Ssam                            default_print(p - hdr_len, length + hdr_len);
308146778Ssam		}
30932145Spst		break;
310146778Ssam
311146778Ssam        case NLPID_Q933:
312146778Ssam		q933_print(p, length);
313146778Ssam		break;
314146778Ssam
315146778Ssam        case NLPID_MFR:
316146778Ssam                frf15_print(p, length);
317146778Ssam                break;
318146778Ssam
319172686Smlaier        case NLPID_PPP:
320172686Smlaier                ppp_print(p, length);
321172686Smlaier                break;
322172686Smlaier
32332145Spst	default:
324146778Ssam		if (!eflag)
325146778Ssam                    fr_hdr_print(length + hdr_len, addr_len,
326146778Ssam				     dlci, flags, nlpid);
327146778Ssam		if (!xflag)
328147904Ssam			default_print(p, length);
32932145Spst	}
33032145Spst
331146778Ssam	return hdr_len;
332147904Ssam
333147904Ssam trunc:
334147904Ssam        printf("[|fr]");
335147904Ssam        return 0;
336147904Ssam
33732145Spst}
33832145Spst
339172686Smlaieru_int
340172686Smlaiermfr_if_print(const struct pcap_pkthdr *h, register const u_char *p)
341172686Smlaier{
342172686Smlaier	register u_int length = h->len;
343172686Smlaier	register u_int caplen = h->caplen;
344172686Smlaier
345172686Smlaier        TCHECK2(*p, 2); /* minimum frame header length */
346172686Smlaier
347172686Smlaier        if ((length = mfr_print(p, length)) == 0)
348172686Smlaier            return (0);
349172686Smlaier        else
350172686Smlaier            return length;
351172686Smlaier trunc:
352172686Smlaier        printf("[|mfr]");
353172686Smlaier        return caplen;
354172686Smlaier}
355172686Smlaier
356172686Smlaier
357162021Ssam#define MFR_CTRL_MSG_ADD_LINK        1
358162021Ssam#define MFR_CTRL_MSG_ADD_LINK_ACK    2
359162021Ssam#define MFR_CTRL_MSG_ADD_LINK_REJ    3
360162021Ssam#define MFR_CTRL_MSG_HELLO           4
361162021Ssam#define MFR_CTRL_MSG_HELLO_ACK       5
362162021Ssam#define MFR_CTRL_MSG_REMOVE_LINK     6
363162021Ssam#define MFR_CTRL_MSG_REMOVE_LINK_ACK 7
364162021Ssam
365162021Ssamstruct tok mfr_ctrl_msg_values[] = {
366162021Ssam    { MFR_CTRL_MSG_ADD_LINK, "Add Link" },
367162021Ssam    { MFR_CTRL_MSG_ADD_LINK_ACK, "Add Link ACK" },
368162021Ssam    { MFR_CTRL_MSG_ADD_LINK_REJ, "Add Link Reject" },
369162021Ssam    { MFR_CTRL_MSG_HELLO, "Hello" },
370162021Ssam    { MFR_CTRL_MSG_HELLO_ACK, "Hello ACK" },
371162021Ssam    { MFR_CTRL_MSG_REMOVE_LINK, "Remove Link" },
372162021Ssam    { MFR_CTRL_MSG_REMOVE_LINK_ACK, "Remove Link ACK" },
373162021Ssam    { 0, NULL }
374162021Ssam};
375162021Ssam
376162021Ssam#define MFR_CTRL_IE_BUNDLE_ID  1
377162021Ssam#define MFR_CTRL_IE_LINK_ID    2
378162021Ssam#define MFR_CTRL_IE_MAGIC_NUM  3
379162021Ssam#define MFR_CTRL_IE_TIMESTAMP  5
380162021Ssam#define MFR_CTRL_IE_VENDOR_EXT 6
381162021Ssam#define MFR_CTRL_IE_CAUSE      7
382162021Ssam
383162021Ssamstruct tok mfr_ctrl_ie_values[] = {
384162021Ssam    { MFR_CTRL_IE_BUNDLE_ID, "Bundle ID"},
385162021Ssam    { MFR_CTRL_IE_LINK_ID, "Link ID"},
386162021Ssam    { MFR_CTRL_IE_MAGIC_NUM, "Magic Number"},
387162021Ssam    { MFR_CTRL_IE_TIMESTAMP, "Timestamp"},
388162021Ssam    { MFR_CTRL_IE_VENDOR_EXT, "Vendor Extension"},
389162021Ssam    { MFR_CTRL_IE_CAUSE, "Cause"},
390162021Ssam    { 0, NULL }
391162021Ssam};
392162021Ssam
393162021Ssam#define MFR_ID_STRING_MAXLEN 50
394162021Ssam
395162021Ssamstruct ie_tlv_header_t {
396162021Ssam    u_int8_t ie_type;
397162021Ssam    u_int8_t ie_len;
398162021Ssam};
399162021Ssam
400162021Ssamu_int
401162021Ssammfr_print(register const u_char *p, u_int length)
402162021Ssam{
403162021Ssam    u_int tlen,idx,hdr_len = 0;
404162021Ssam    u_int16_t sequence_num;
405162021Ssam    u_int8_t ie_type,ie_len;
406162021Ssam    const u_int8_t *tptr;
407162021Ssam
408162021Ssam
409162021Ssam/*
410162021Ssam * FRF.16 Link Integrity Control Frame
411162021Ssam *
412162021Ssam *      7    6    5    4    3    2    1    0
413162021Ssam *    +----+----+----+----+----+----+----+----+
414162021Ssam *    | B  | E  | C=1| 0    0    0    0  | EA |
415162021Ssam *    +----+----+----+----+----+----+----+----+
416162021Ssam *    | 0    0    0    0    0    0    0    0  |
417162021Ssam *    +----+----+----+----+----+----+----+----+
418162021Ssam *    |              message type             |
419162021Ssam *    +----+----+----+----+----+----+----+----+
420162021Ssam */
421162021Ssam
422162021Ssam    TCHECK2(*p, 4); /* minimum frame header length */
423162021Ssam
424162021Ssam    if ((p[0] & MFR_BEC_MASK) == MFR_CTRL_FRAME && p[1] == 0) {
425162021Ssam        printf("FRF.16 Control, Flags [%s], %s, length %u",
426162021Ssam               bittok2str(frf_flag_values,"none",(p[0] & MFR_BEC_MASK)),
427162021Ssam               tok2str(mfr_ctrl_msg_values,"Unknown Message (0x%02x)",p[2]),
428162021Ssam               length);
429162021Ssam        tptr = p + 3;
430162021Ssam        tlen = length -3;
431162021Ssam        hdr_len = 3;
432162021Ssam
433162021Ssam        if (!vflag)
434162021Ssam            return hdr_len;
435162021Ssam
436162021Ssam        while (tlen>sizeof(struct ie_tlv_header_t)) {
437162021Ssam            TCHECK2(*tptr, sizeof(struct ie_tlv_header_t));
438162021Ssam            ie_type=tptr[0];
439162021Ssam            ie_len=tptr[1];
440162021Ssam
441162021Ssam            printf("\n\tIE %s (%u), length %u: ",
442162021Ssam                   tok2str(mfr_ctrl_ie_values,"Unknown",ie_type),
443162021Ssam                   ie_type,
444162021Ssam                   ie_len);
445162021Ssam
446162021Ssam            /* infinite loop check */
447162021Ssam            if (ie_type == 0 || ie_len <= sizeof(struct ie_tlv_header_t))
448162021Ssam                return hdr_len;
449162021Ssam
450162021Ssam            TCHECK2(*tptr,ie_len);
451162021Ssam            tptr+=sizeof(struct ie_tlv_header_t);
452162021Ssam            /* tlv len includes header */
453162021Ssam            ie_len-=sizeof(struct ie_tlv_header_t);
454162021Ssam            tlen-=sizeof(struct ie_tlv_header_t);
455162021Ssam
456162021Ssam            switch (ie_type) {
457162021Ssam
458162021Ssam            case MFR_CTRL_IE_MAGIC_NUM:
459162021Ssam                printf("0x%08x",EXTRACT_32BITS(tptr));
460162021Ssam                break;
461162021Ssam
462162021Ssam            case MFR_CTRL_IE_BUNDLE_ID: /* same message format */
463162021Ssam            case MFR_CTRL_IE_LINK_ID:
464162021Ssam                for (idx = 0; idx < ie_len && idx < MFR_ID_STRING_MAXLEN; idx++) {
465162021Ssam                    if (*(tptr+idx) != 0) /* don't print null termination */
466162021Ssam                        safeputchar(*(tptr+idx));
467162021Ssam                    else
468162021Ssam                        break;
469162021Ssam                }
470162021Ssam                break;
471162021Ssam
472162021Ssam            case MFR_CTRL_IE_TIMESTAMP:
473162021Ssam                if (ie_len == sizeof(struct timeval)) {
474162021Ssam                    ts_print((const struct timeval *)tptr);
475162021Ssam                    break;
476162021Ssam                }
477162021Ssam                /* fall through and hexdump if no unix timestamp */
478162021Ssam
479162021Ssam                /*
480162021Ssam                 * FIXME those are the defined IEs that lack a decoder
481162021Ssam                 * you are welcome to contribute code ;-)
482162021Ssam                 */
483162021Ssam
484162021Ssam            case MFR_CTRL_IE_VENDOR_EXT:
485162021Ssam            case MFR_CTRL_IE_CAUSE:
486162021Ssam
487162021Ssam            default:
488162021Ssam                if (vflag <= 1)
489162021Ssam                    print_unknown_data(tptr,"\n\t  ",ie_len);
490162021Ssam                break;
491162021Ssam            }
492162021Ssam
493162021Ssam            /* do we want to see a hexdump of the IE ? */
494162021Ssam            if (vflag > 1 )
495162021Ssam                print_unknown_data(tptr,"\n\t  ",ie_len);
496162021Ssam
497162021Ssam            tlen-=ie_len;
498162021Ssam            tptr+=ie_len;
499162021Ssam        }
500162021Ssam        return hdr_len;
501162021Ssam    }
502162021Ssam/*
503162021Ssam * FRF.16 Fragmentation Frame
504162021Ssam *
505162021Ssam *      7    6    5    4    3    2    1    0
506162021Ssam *    +----+----+----+----+----+----+----+----+
507162021Ssam *    | B  | E  | C=0|seq. (high 4 bits) | EA  |
508162021Ssam *    +----+----+----+----+----+----+----+----+
509162021Ssam *    |        sequence  (low 8 bits)         |
510162021Ssam *    +----+----+----+----+----+----+----+----+
511162021Ssam *    |        DLCI (6 bits)        | CR | EA  |
512162021Ssam *    +----+----+----+----+----+----+----+----+
513162021Ssam *    |   DLCI (4 bits)   |FECN|BECN| DE | EA |
514162021Ssam *    +----+----+----+----+----+----+----+----+
515162021Ssam */
516162021Ssam
517162021Ssam    sequence_num = (p[0]&0x1e)<<7 | p[1];
518162021Ssam    /* whole packet or first fragment ? */
519162021Ssam    if ((p[0] & MFR_BEC_MASK) == MFR_FRAG_FRAME ||
520162021Ssam        (p[0] & MFR_BEC_MASK) == MFR_B_BIT) {
521162021Ssam        printf("FRF.16 Frag, seq %u, Flags [%s], ",
522162021Ssam               sequence_num,
523162021Ssam               bittok2str(frf_flag_values,"none",(p[0] & MFR_BEC_MASK)));
524162021Ssam        hdr_len = 2;
525162021Ssam        fr_print(p+hdr_len,length-hdr_len);
526162021Ssam        return hdr_len;
527162021Ssam    }
528162021Ssam
529162021Ssam    /* must be a middle or the last fragment */
530162021Ssam    printf("FRF.16 Frag, seq %u, Flags [%s]",
531162021Ssam           sequence_num,
532162021Ssam           bittok2str(frf_flag_values,"none",(p[0] & MFR_BEC_MASK)));
533162021Ssam    print_unknown_data(p,"\n\t",length);
534162021Ssam
535162021Ssam    return hdr_len;
536162021Ssam
537162021Ssam trunc:
538162021Ssam    printf("[|mfr]");
539162021Ssam    return length;
540162021Ssam}
541162021Ssam
542146778Ssam/* an NLPID of 0xb1 indicates a 2-byte
543146778Ssam * FRF.15 header
544146778Ssam *
545146778Ssam *      7    6    5    4    3    2    1    0
546146778Ssam *    +----+----+----+----+----+----+----+----+
547146778Ssam *    ~              Q.922 header             ~
548146778Ssam *    +----+----+----+----+----+----+----+----+
549146778Ssam *    |             NLPID (8 bits)            | NLPID=0xb1
550146778Ssam *    +----+----+----+----+----+----+----+----+
551146778Ssam *    | B  | E  | C  |seq. (high 4 bits) | R  |
552146778Ssam *    +----+----+----+----+----+----+----+----+
553146778Ssam *    |        sequence  (low 8 bits)         |
554146778Ssam *    +----+----+----+----+----+----+----+----+
555146778Ssam */
55632145Spst
557146778Ssam#define FR_FRF15_FRAGTYPE 0x01
558146778Ssam
559146778Ssamstatic void
560146778Ssamfrf15_print (const u_char *p, u_int length) {
561146778Ssam
562146778Ssam    u_int16_t sequence_num, flags;
563146778Ssam
564162021Ssam    flags = p[0]&MFR_BEC_MASK;
565146778Ssam    sequence_num = (p[0]&0x1e)<<7 | p[1];
566146778Ssam
567146778Ssam    printf("FRF.15, seq 0x%03x, Flags [%s],%s Fragmentation, length %u",
568146778Ssam           sequence_num,
569162021Ssam           bittok2str(frf_flag_values,"none",flags),
570162021Ssam           p[0]&FR_FRF15_FRAGTYPE ? "Interface" : "End-to-End",
571146778Ssam           length);
572146778Ssam
573146778Ssam/* TODO:
574146778Ssam * depending on all permutations of the B, E and C bit
575146778Ssam * dig as deep as we can - e.g. on the first (B) fragment
576146778Ssam * there is enough payload to print the IP header
577146778Ssam * on non (B) fragments it depends if the fragmentation
578146778Ssam * model is end-to-end or interface based wether we want to print
579146778Ssam * another Q.922 header
580146778Ssam */
581146778Ssam
58232145Spst}
58332145Spst
58432145Spst/*
58532145Spst * Q.933 decoding portion for framerelay specific.
58632145Spst */
58732145Spst
58832145Spst/* Q.933 packet format
58932145Spst                      Format of Other Protocols
59032145Spst                          using Q.933 NLPID
59132145Spst                  +-------------------------------+
59232145Spst                  |        Q.922 Address          |
59332145Spst                  +---------------+---------------+
59432145Spst                  |Control  0x03  | NLPID   0x08  |
59532145Spst                  +---------------+---------------+
59632145Spst                  |          L2 Protocol ID       |
59732145Spst                  | octet 1       |  octet 2      |
59832145Spst                  +-------------------------------+
59932145Spst                  |          L3 Protocol ID       |
60032145Spst                  | octet 2       |  octet 2      |
60132145Spst                  +-------------------------------+
60232145Spst                  |         Protocol Data         |
60332145Spst                  +-------------------------------+
60432145Spst                  | FCS                           |
60532145Spst                  +-------------------------------+
60632145Spst */
60732145Spst
60832145Spst/* L2 (Octet 1)- Call Reference Usually is 0x0 */
60932145Spst
61032145Spst/*
61132145Spst * L2 (Octet 2)- Message Types definition 1 byte long.
61232145Spst */
61332145Spst/* Call Establish */
61432145Spst#define MSG_TYPE_ESC_TO_NATIONAL  0x00
61532145Spst#define MSG_TYPE_ALERT            0x01
61632145Spst#define MSG_TYPE_CALL_PROCEEDING  0x02
61732145Spst#define MSG_TYPE_CONNECT          0x07
61832145Spst#define MSG_TYPE_CONNECT_ACK      0x0F
61932145Spst#define MSG_TYPE_PROGRESS         0x03
62032145Spst#define MSG_TYPE_SETUP            0x05
62132145Spst/* Call Clear */
62232145Spst#define MSG_TYPE_DISCONNECT       0x45
62332145Spst#define MSG_TYPE_RELEASE          0x4D
62432145Spst#define MSG_TYPE_RELEASE_COMPLETE 0x5A
62532145Spst#define MSG_TYPE_RESTART          0x46
62632145Spst#define MSG_TYPE_RESTART_ACK      0x4E
62732145Spst/* Status */
62832145Spst#define MSG_TYPE_STATUS           0x7D
62932145Spst#define MSG_TYPE_STATUS_ENQ       0x75
63032145Spst
631146778Ssamstruct tok fr_q933_msg_values[] = {
632146778Ssam    { MSG_TYPE_ESC_TO_NATIONAL, "ESC to National" },
633146778Ssam    { MSG_TYPE_ALERT, "Alert" },
634146778Ssam    { MSG_TYPE_CALL_PROCEEDING, "Call proceeding" },
635146778Ssam    { MSG_TYPE_CONNECT, "Connect" },
636146778Ssam    { MSG_TYPE_CONNECT_ACK, "Connect ACK" },
637146778Ssam    { MSG_TYPE_PROGRESS, "Progress" },
638146778Ssam    { MSG_TYPE_SETUP, "Setup" },
639146778Ssam    { MSG_TYPE_DISCONNECT, "Disconnect" },
640146778Ssam    { MSG_TYPE_RELEASE, "Release" },
641146778Ssam    { MSG_TYPE_RELEASE_COMPLETE, "Release Complete" },
642146778Ssam    { MSG_TYPE_RESTART, "Restart" },
643146778Ssam    { MSG_TYPE_RESTART_ACK, "Restart ACK" },
644146778Ssam    { MSG_TYPE_STATUS, "Status Reply" },
645146778Ssam    { MSG_TYPE_STATUS_ENQ, "Status Enquiry" },
646146778Ssam    { 0, NULL }
647146778Ssam};
64832145Spst
649146778Ssam#define MSG_ANSI_LOCKING_SHIFT	0x95
650146778Ssam
651146778Ssam#define FR_LMI_ANSI_REPORT_TYPE_IE	0x01
652146778Ssam#define FR_LMI_ANSI_LINK_VERIFY_IE_91	0x19 /* details? */
653146778Ssam#define FR_LMI_ANSI_LINK_VERIFY_IE	0x03
654146778Ssam#define FR_LMI_ANSI_PVC_STATUS_IE	0x07
655146778Ssam
656146778Ssam#define FR_LMI_CCITT_REPORT_TYPE_IE	0x51
657146778Ssam#define FR_LMI_CCITT_LINK_VERIFY_IE	0x53
658146778Ssam#define FR_LMI_CCITT_PVC_STATUS_IE	0x57
659146778Ssam
660146778Ssamstruct tok fr_q933_ie_values_codeset5[] = {
661146778Ssam    { FR_LMI_ANSI_REPORT_TYPE_IE, "ANSI Report Type" },
662146778Ssam    { FR_LMI_ANSI_LINK_VERIFY_IE_91, "ANSI Link Verify" },
663146778Ssam    { FR_LMI_ANSI_LINK_VERIFY_IE, "ANSI Link Verify" },
664146778Ssam    { FR_LMI_ANSI_PVC_STATUS_IE, "ANSI PVC Status" },
665146778Ssam    { FR_LMI_CCITT_REPORT_TYPE_IE, "CCITT Report Type" },
666146778Ssam    { FR_LMI_CCITT_LINK_VERIFY_IE, "CCITT Link Verify" },
667146778Ssam    { FR_LMI_CCITT_PVC_STATUS_IE, "CCITT PVC Status" },
668146778Ssam    { 0, NULL }
66932145Spst};
67032145Spst
671146778Ssam#define FR_LMI_REPORT_TYPE_IE_FULL_STATUS 0
672146778Ssam#define FR_LMI_REPORT_TYPE_IE_LINK_VERIFY 1
673146778Ssam#define FR_LMI_REPORT_TYPE_IE_ASYNC_PVC   2
67432145Spst
675146778Ssamstruct tok fr_lmi_report_type_ie_values[] = {
676146778Ssam    { FR_LMI_REPORT_TYPE_IE_FULL_STATUS, "Full Status" },
677146778Ssam    { FR_LMI_REPORT_TYPE_IE_LINK_VERIFY, "Link verify" },
678146778Ssam    { FR_LMI_REPORT_TYPE_IE_ASYNC_PVC, "Async PVC Status" },
679146778Ssam    { 0, NULL }
680146778Ssam};
68132145Spst
682162021Ssam/* array of 16 codepages - currently we only support codepage 1,5 */
683146778Ssamstatic struct tok *fr_q933_ie_codesets[] = {
684146778Ssam    NULL,
685162021Ssam    fr_q933_ie_values_codeset5,
686146778Ssam    NULL,
687146778Ssam    NULL,
688146778Ssam    NULL,
689146778Ssam    fr_q933_ie_values_codeset5,
690146778Ssam    NULL,
691146778Ssam    NULL,
692146778Ssam    NULL,
693146778Ssam    NULL,
694146778Ssam    NULL,
695146778Ssam    NULL,
696146778Ssam    NULL,
697146778Ssam    NULL,
698146778Ssam    NULL,
699146778Ssam    NULL
70032145Spst};
70132145Spst
702162021Ssamstatic int fr_q933_print_ie_codeset5(const struct ie_tlv_header_t  *ie_p,
703147904Ssam    const u_char *p);
704147904Ssam
705162021Ssamtypedef int (*codeset_pr_func_t)(const struct ie_tlv_header_t  *ie_p,
706147904Ssam    const u_char *p);
707147904Ssam
708162021Ssam/* array of 16 codepages - currently we only support codepage 1,5 */
709147904Ssamstatic codeset_pr_func_t fr_q933_print_ie_codeset[] = {
710147904Ssam    NULL,
711162021Ssam    fr_q933_print_ie_codeset5,
712147904Ssam    NULL,
713147904Ssam    NULL,
714147904Ssam    NULL,
715147904Ssam    fr_q933_print_ie_codeset5,
716147904Ssam    NULL,
717147904Ssam    NULL,
718147904Ssam    NULL,
719147904Ssam    NULL,
720147904Ssam    NULL,
721147904Ssam    NULL,
722147904Ssam    NULL,
723147904Ssam    NULL,
724147904Ssam    NULL,
725147904Ssam    NULL
726147904Ssam};
727147904Ssam
72832145Spstvoid
729146778Ssamq933_print(const u_char *p, u_int length)
73032145Spst{
73132145Spst	const u_char *ptemp = p;
732162021Ssam	struct ie_tlv_header_t  *ie_p;
733146778Ssam        int olen;
734146778Ssam	int is_ansi = 0;
735147904Ssam        u_int codeset;
736162021Ssam        u_int ie_is_known = 0;
737146778Ssam
738146778Ssam	if (length < 9) {	/* shortest: Q.933a LINK VERIFY */
739146778Ssam		printf("[|q.933]");
740146778Ssam		return;
741146778Ssam	}
742146778Ssam
743146778Ssam        codeset = p[2]&0x0f;   /* extract the codeset */
744146778Ssam
745190207Srpaulo	if (p[2] == MSG_ANSI_LOCKING_SHIFT) {
746190207Srpaulo	        is_ansi = 1;
747190207Srpaulo	}
74832145Spst
749146778Ssam        printf("%s", eflag ? "" : "Q.933, ");
75032145Spst
75132145Spst	/* printing out header part */
752162021Ssam	printf("%s, codeset %u", is_ansi ? "ANSI" : "CCITT", codeset);
753146778Ssam
754190207Srpaulo	if (p[0]) {
755190207Srpaulo	        printf(", Call Ref: 0x%02x", p[0]);
756190207Srpaulo	}
757190207Srpaulo        if (vflag) {
758190207Srpaulo                printf(", %s (0x%02x), length %u",
759190207Srpaulo		       tok2str(fr_q933_msg_values,
760190207Srpaulo			       "unknown message", p[1]),
761190207Srpaulo		       p[1],
762190207Srpaulo		       length);
763190207Srpaulo        } else {
764190207Srpaulo                printf(", %s",
765190207Srpaulo		       tok2str(fr_q933_msg_values,
766190207Srpaulo			       "unknown message 0x%02x", p[1]));
767190207Srpaulo	}
768146778Ssam
769146778Ssam        olen = length; /* preserve the original length for non verbose mode */
770146778Ssam
771146778Ssam	if (length < (u_int)(2 - is_ansi)) {
772146778Ssam		printf("[|q.933]");
773146778Ssam		return;
77432145Spst	}
775190207Srpaulo	length -= 2 + is_ansi;
776146778Ssam	ptemp += 2 + is_ansi;
77732145Spst
77832145Spst	/* Loop through the rest of IE */
779190207Srpaulo	while (length > sizeof(struct ie_tlv_header_t)) {
780162021Ssam		ie_p = (struct ie_tlv_header_t  *)ptemp;
781190207Srpaulo		if (length < sizeof(struct ie_tlv_header_t) ||
782190207Srpaulo		    length < sizeof(struct ie_tlv_header_t) + ie_p->ie_len) {
783190207Srpaulo                    if (vflag) { /* not bark if there is just a trailer */
784146778Ssam                        printf("\n[|q.933]");
785190207Srpaulo                    } else {
786146778Ssam                        printf(", length %u",olen);
787190207Srpaulo		    }
788146778Ssam                    return;
78932145Spst		}
79032145Spst
791146778Ssam                /* lets do the full IE parsing only in verbose mode
792146778Ssam                 * however some IEs (DLCI Status, Link Verify)
793190207Srpaulo                 * are also interestting in non-verbose mode */
794190207Srpaulo                if (vflag) {
795162021Ssam                    printf("\n\t%s IE (0x%02x), length %u: ",
796190207Srpaulo                           tok2str(fr_q933_ie_codesets[codeset],
797190207Srpaulo				   "unknown", ie_p->ie_type),
798162021Ssam                           ie_p->ie_type,
799146778Ssam                           ie_p->ie_len);
800190207Srpaulo		}
801190207Srpaulo
802162021Ssam                /* sanity check */
803190207Srpaulo                if (ie_p->ie_type == 0 || ie_p->ie_len == 0) {
804162021Ssam                    return;
805190207Srpaulo		}
80632145Spst
807190207Srpaulo                if (fr_q933_print_ie_codeset[codeset] != NULL) {
808162021Ssam                    ie_is_known = fr_q933_print_ie_codeset[codeset](ie_p, ptemp);
809190207Srpaulo		}
810190207Srpaulo
811190207Srpaulo                if (vflag >= 1 && !ie_is_known) {
812162021Ssam                    print_unknown_data(ptemp+2,"\n\t",ie_p->ie_len);
813190207Srpaulo		}
814162021Ssam
815146778Ssam                /* do we want to see a hexdump of the IE ? */
816190207Srpaulo                if (vflag> 1 && ie_is_known) {
817146778Ssam                    print_unknown_data(ptemp+2,"\n\t  ",ie_p->ie_len);
818190207Srpaulo		}
81932145Spst
820146778Ssam		length = length - ie_p->ie_len - 2;
821146778Ssam		ptemp = ptemp + ie_p->ie_len + 2;
822146778Ssam	}
823190207Srpaulo        if (!vflag) {
824146778Ssam            printf(", length %u",olen);
825190207Srpaulo	}
826146778Ssam}
827147904Ssam
828147904Ssamstatic int
829162021Ssamfr_q933_print_ie_codeset5(const struct ie_tlv_header_t  *ie_p, const u_char *p)
830147904Ssam{
831147904Ssam        u_int dlci;
832147904Ssam
833162021Ssam        switch (ie_p->ie_type) {
834147904Ssam
835147904Ssam        case FR_LMI_ANSI_REPORT_TYPE_IE: /* fall through */
836147904Ssam        case FR_LMI_CCITT_REPORT_TYPE_IE:
837190207Srpaulo            if (vflag) {
838147904Ssam                printf("%s (%u)",
839147904Ssam                       tok2str(fr_lmi_report_type_ie_values,"unknown",p[2]),
840147904Ssam                       p[2]);
841190207Srpaulo	    }
842147904Ssam            return 1;
843147904Ssam
844147904Ssam        case FR_LMI_ANSI_LINK_VERIFY_IE: /* fall through */
845147904Ssam        case FR_LMI_CCITT_LINK_VERIFY_IE:
846147904Ssam        case FR_LMI_ANSI_LINK_VERIFY_IE_91:
847190207Srpaulo            if (!vflag) {
848147904Ssam                printf(", ");
849190207Srpaulo	    }
850147904Ssam            printf("TX Seq: %3d, RX Seq: %3d", p[2], p[3]);
851147904Ssam            return 1;
852147904Ssam
853147904Ssam        case FR_LMI_ANSI_PVC_STATUS_IE: /* fall through */
854147904Ssam        case FR_LMI_CCITT_PVC_STATUS_IE:
855190207Srpaulo            if (!vflag) {
856147904Ssam                printf(", ");
857190207Srpaulo	    }
858147904Ssam            /* now parse the DLCI information element. */
859147904Ssam            if ((ie_p->ie_len < 3) ||
860147904Ssam                (p[2] & 0x80) ||
861147904Ssam                ((ie_p->ie_len == 3) && !(p[3] & 0x80)) ||
862147904Ssam                ((ie_p->ie_len == 4) && ((p[3] & 0x80) || !(p[4] & 0x80))) ||
863147904Ssam                ((ie_p->ie_len == 5) && ((p[3] & 0x80) || (p[4] & 0x80) ||
864147904Ssam                                   !(p[5] & 0x80))) ||
865147904Ssam                (ie_p->ie_len > 5) ||
866190207Srpaulo                !(p[ie_p->ie_len + 1] & 0x80)) {
867147904Ssam                printf("Invalid DLCI IE");
868190207Srpaulo	    }
869147904Ssam
870147904Ssam            dlci = ((p[2] & 0x3F) << 4) | ((p[3] & 0x78) >> 3);
871190207Srpaulo            if (ie_p->ie_len == 4) {
872147904Ssam                dlci = (dlci << 6) | ((p[4] & 0x7E) >> 1);
873190207Srpaulo	    }
874190207Srpaulo            else if (ie_p->ie_len == 5) {
875147904Ssam                dlci = (dlci << 13) | (p[4] & 0x7F) | ((p[5] & 0x7E) >> 1);
876190207Srpaulo	    }
877147904Ssam
878147904Ssam            printf("DLCI %u: status %s%s", dlci,
879147904Ssam                    p[ie_p->ie_len + 1] & 0x8 ? "New, " : "",
880147904Ssam                    p[ie_p->ie_len + 1] & 0x2 ? "Active" : "Inactive");
881147904Ssam            return 1;
882147904Ssam	}
883147904Ssam
884147904Ssam        return 0;
885147904Ssam}
886