print-fr.c revision 190207
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: head/contrib/tcpdump/print-fr.c 190207 2009-03-21 18:30:25Z rpaulo $
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
261146778Ssam                if (ether_encap_print(extracted_ethertype,
262146778Ssam                                      p+addr_len+ETHERTYPE_LEN,
263146778Ssam                                      length-addr_len-ETHERTYPE_LEN,
264147904Ssam                                      length-addr_len-ETHERTYPE_LEN,
265146778Ssam                                      &extracted_ethertype) == 0)
266146778Ssam                    /* ether_type not known, probably it wasn't one */
267146778Ssam                    printf("UI %02x! ", p[addr_len]);
268146778Ssam                else
269146778Ssam                    return hdr_len;
270146778Ssam        }
271146778Ssam
272146778Ssam	if (!p[addr_len + 1]) {	/* pad byte should be used with 3-byte Q.922 */
273146778Ssam		if (addr_len != 3)
274146778Ssam			printf("Pad! ");
275146778Ssam	} else if (addr_len == 3)
276146778Ssam		printf("No pad! ");
277146778Ssam
278146778Ssam	nlpid = p[hdr_len - 1];
279146778Ssam
28032145Spst	if (eflag)
281146778Ssam		fr_hdr_print(length, addr_len, dlci, flags, nlpid);
282146778Ssam	p += hdr_len;
283146778Ssam	length -= hdr_len;
28432145Spst
285146778Ssam	switch (nlpid) {
28632145Spst	case NLPID_IP:
287146778Ssam	        ip_print(gndo, p, length);
28832145Spst		break;
289146778Ssam
290146778Ssam#ifdef INET6
291146778Ssam	case NLPID_IP6:
292146778Ssam		ip6_print(p, length);
293146778Ssam		break;
294146778Ssam#endif
29532145Spst	case NLPID_CLNP:
29632145Spst	case NLPID_ESIS:
29732145Spst	case NLPID_ISIS:
298147904Ssam                isoclns_print(p-1, length+1, length+1); /* OSI printers need the NLPID field */
29932145Spst		break;
300146778Ssam
301146778Ssam	case NLPID_SNAP:
302147904Ssam		if (snap_print(p, length, length, &extracted_ethertype, 0) == 0) {
303146778Ssam			/* ether_type not known, print raw packet */
304146778Ssam                        if (!eflag)
305146778Ssam                            fr_hdr_print(length + hdr_len, hdr_len,
306146778Ssam                                         dlci, flags, nlpid);
307162021Ssam			if (!suppress_default_print)
308147904Ssam                            default_print(p - hdr_len, length + hdr_len);
309146778Ssam		}
31032145Spst		break;
311146778Ssam
312146778Ssam        case NLPID_Q933:
313146778Ssam		q933_print(p, length);
314146778Ssam		break;
315146778Ssam
316146778Ssam        case NLPID_MFR:
317146778Ssam                frf15_print(p, length);
318146778Ssam                break;
319146778Ssam
320172686Smlaier        case NLPID_PPP:
321172686Smlaier                ppp_print(p, length);
322172686Smlaier                break;
323172686Smlaier
32432145Spst	default:
325146778Ssam		if (!eflag)
326146778Ssam                    fr_hdr_print(length + hdr_len, addr_len,
327146778Ssam				     dlci, flags, nlpid);
328146778Ssam		if (!xflag)
329147904Ssam			default_print(p, length);
33032145Spst	}
33132145Spst
332146778Ssam	return hdr_len;
333147904Ssam
334147904Ssam trunc:
335147904Ssam        printf("[|fr]");
336147904Ssam        return 0;
337147904Ssam
33832145Spst}
33932145Spst
340172686Smlaieru_int
341172686Smlaiermfr_if_print(const struct pcap_pkthdr *h, register const u_char *p)
342172686Smlaier{
343172686Smlaier	register u_int length = h->len;
344172686Smlaier	register u_int caplen = h->caplen;
345172686Smlaier
346172686Smlaier        TCHECK2(*p, 2); /* minimum frame header length */
347172686Smlaier
348172686Smlaier        if ((length = mfr_print(p, length)) == 0)
349172686Smlaier            return (0);
350172686Smlaier        else
351172686Smlaier            return length;
352172686Smlaier trunc:
353172686Smlaier        printf("[|mfr]");
354172686Smlaier        return caplen;
355172686Smlaier}
356172686Smlaier
357172686Smlaier
358162021Ssam#define MFR_CTRL_MSG_ADD_LINK        1
359162021Ssam#define MFR_CTRL_MSG_ADD_LINK_ACK    2
360162021Ssam#define MFR_CTRL_MSG_ADD_LINK_REJ    3
361162021Ssam#define MFR_CTRL_MSG_HELLO           4
362162021Ssam#define MFR_CTRL_MSG_HELLO_ACK       5
363162021Ssam#define MFR_CTRL_MSG_REMOVE_LINK     6
364162021Ssam#define MFR_CTRL_MSG_REMOVE_LINK_ACK 7
365162021Ssam
366162021Ssamstruct tok mfr_ctrl_msg_values[] = {
367162021Ssam    { MFR_CTRL_MSG_ADD_LINK, "Add Link" },
368162021Ssam    { MFR_CTRL_MSG_ADD_LINK_ACK, "Add Link ACK" },
369162021Ssam    { MFR_CTRL_MSG_ADD_LINK_REJ, "Add Link Reject" },
370162021Ssam    { MFR_CTRL_MSG_HELLO, "Hello" },
371162021Ssam    { MFR_CTRL_MSG_HELLO_ACK, "Hello ACK" },
372162021Ssam    { MFR_CTRL_MSG_REMOVE_LINK, "Remove Link" },
373162021Ssam    { MFR_CTRL_MSG_REMOVE_LINK_ACK, "Remove Link ACK" },
374162021Ssam    { 0, NULL }
375162021Ssam};
376162021Ssam
377162021Ssam#define MFR_CTRL_IE_BUNDLE_ID  1
378162021Ssam#define MFR_CTRL_IE_LINK_ID    2
379162021Ssam#define MFR_CTRL_IE_MAGIC_NUM  3
380162021Ssam#define MFR_CTRL_IE_TIMESTAMP  5
381162021Ssam#define MFR_CTRL_IE_VENDOR_EXT 6
382162021Ssam#define MFR_CTRL_IE_CAUSE      7
383162021Ssam
384162021Ssamstruct tok mfr_ctrl_ie_values[] = {
385162021Ssam    { MFR_CTRL_IE_BUNDLE_ID, "Bundle ID"},
386162021Ssam    { MFR_CTRL_IE_LINK_ID, "Link ID"},
387162021Ssam    { MFR_CTRL_IE_MAGIC_NUM, "Magic Number"},
388162021Ssam    { MFR_CTRL_IE_TIMESTAMP, "Timestamp"},
389162021Ssam    { MFR_CTRL_IE_VENDOR_EXT, "Vendor Extension"},
390162021Ssam    { MFR_CTRL_IE_CAUSE, "Cause"},
391162021Ssam    { 0, NULL }
392162021Ssam};
393162021Ssam
394162021Ssam#define MFR_ID_STRING_MAXLEN 50
395162021Ssam
396162021Ssamstruct ie_tlv_header_t {
397162021Ssam    u_int8_t ie_type;
398162021Ssam    u_int8_t ie_len;
399162021Ssam};
400162021Ssam
401162021Ssamu_int
402162021Ssammfr_print(register const u_char *p, u_int length)
403162021Ssam{
404162021Ssam    u_int tlen,idx,hdr_len = 0;
405162021Ssam    u_int16_t sequence_num;
406162021Ssam    u_int8_t ie_type,ie_len;
407162021Ssam    const u_int8_t *tptr;
408162021Ssam
409162021Ssam
410162021Ssam/*
411162021Ssam * FRF.16 Link Integrity Control Frame
412162021Ssam *
413162021Ssam *      7    6    5    4    3    2    1    0
414162021Ssam *    +----+----+----+----+----+----+----+----+
415162021Ssam *    | B  | E  | C=1| 0    0    0    0  | EA |
416162021Ssam *    +----+----+----+----+----+----+----+----+
417162021Ssam *    | 0    0    0    0    0    0    0    0  |
418162021Ssam *    +----+----+----+----+----+----+----+----+
419162021Ssam *    |              message type             |
420162021Ssam *    +----+----+----+----+----+----+----+----+
421162021Ssam */
422162021Ssam
423162021Ssam    TCHECK2(*p, 4); /* minimum frame header length */
424162021Ssam
425162021Ssam    if ((p[0] & MFR_BEC_MASK) == MFR_CTRL_FRAME && p[1] == 0) {
426162021Ssam        printf("FRF.16 Control, Flags [%s], %s, length %u",
427162021Ssam               bittok2str(frf_flag_values,"none",(p[0] & MFR_BEC_MASK)),
428162021Ssam               tok2str(mfr_ctrl_msg_values,"Unknown Message (0x%02x)",p[2]),
429162021Ssam               length);
430162021Ssam        tptr = p + 3;
431162021Ssam        tlen = length -3;
432162021Ssam        hdr_len = 3;
433162021Ssam
434162021Ssam        if (!vflag)
435162021Ssam            return hdr_len;
436162021Ssam
437162021Ssam        while (tlen>sizeof(struct ie_tlv_header_t)) {
438162021Ssam            TCHECK2(*tptr, sizeof(struct ie_tlv_header_t));
439162021Ssam            ie_type=tptr[0];
440162021Ssam            ie_len=tptr[1];
441162021Ssam
442162021Ssam            printf("\n\tIE %s (%u), length %u: ",
443162021Ssam                   tok2str(mfr_ctrl_ie_values,"Unknown",ie_type),
444162021Ssam                   ie_type,
445162021Ssam                   ie_len);
446162021Ssam
447162021Ssam            /* infinite loop check */
448162021Ssam            if (ie_type == 0 || ie_len <= sizeof(struct ie_tlv_header_t))
449162021Ssam                return hdr_len;
450162021Ssam
451162021Ssam            TCHECK2(*tptr,ie_len);
452162021Ssam            tptr+=sizeof(struct ie_tlv_header_t);
453162021Ssam            /* tlv len includes header */
454162021Ssam            ie_len-=sizeof(struct ie_tlv_header_t);
455162021Ssam            tlen-=sizeof(struct ie_tlv_header_t);
456162021Ssam
457162021Ssam            switch (ie_type) {
458162021Ssam
459162021Ssam            case MFR_CTRL_IE_MAGIC_NUM:
460162021Ssam                printf("0x%08x",EXTRACT_32BITS(tptr));
461162021Ssam                break;
462162021Ssam
463162021Ssam            case MFR_CTRL_IE_BUNDLE_ID: /* same message format */
464162021Ssam            case MFR_CTRL_IE_LINK_ID:
465162021Ssam                for (idx = 0; idx < ie_len && idx < MFR_ID_STRING_MAXLEN; idx++) {
466162021Ssam                    if (*(tptr+idx) != 0) /* don't print null termination */
467162021Ssam                        safeputchar(*(tptr+idx));
468162021Ssam                    else
469162021Ssam                        break;
470162021Ssam                }
471162021Ssam                break;
472162021Ssam
473162021Ssam            case MFR_CTRL_IE_TIMESTAMP:
474162021Ssam                if (ie_len == sizeof(struct timeval)) {
475162021Ssam                    ts_print((const struct timeval *)tptr);
476162021Ssam                    break;
477162021Ssam                }
478162021Ssam                /* fall through and hexdump if no unix timestamp */
479162021Ssam
480162021Ssam                /*
481162021Ssam                 * FIXME those are the defined IEs that lack a decoder
482162021Ssam                 * you are welcome to contribute code ;-)
483162021Ssam                 */
484162021Ssam
485162021Ssam            case MFR_CTRL_IE_VENDOR_EXT:
486162021Ssam            case MFR_CTRL_IE_CAUSE:
487162021Ssam
488162021Ssam            default:
489162021Ssam                if (vflag <= 1)
490162021Ssam                    print_unknown_data(tptr,"\n\t  ",ie_len);
491162021Ssam                break;
492162021Ssam            }
493162021Ssam
494162021Ssam            /* do we want to see a hexdump of the IE ? */
495162021Ssam            if (vflag > 1 )
496162021Ssam                print_unknown_data(tptr,"\n\t  ",ie_len);
497162021Ssam
498162021Ssam            tlen-=ie_len;
499162021Ssam            tptr+=ie_len;
500162021Ssam        }
501162021Ssam        return hdr_len;
502162021Ssam    }
503162021Ssam/*
504162021Ssam * FRF.16 Fragmentation Frame
505162021Ssam *
506162021Ssam *      7    6    5    4    3    2    1    0
507162021Ssam *    +----+----+----+----+----+----+----+----+
508162021Ssam *    | B  | E  | C=0|seq. (high 4 bits) | EA  |
509162021Ssam *    +----+----+----+----+----+----+----+----+
510162021Ssam *    |        sequence  (low 8 bits)         |
511162021Ssam *    +----+----+----+----+----+----+----+----+
512162021Ssam *    |        DLCI (6 bits)        | CR | EA  |
513162021Ssam *    +----+----+----+----+----+----+----+----+
514162021Ssam *    |   DLCI (4 bits)   |FECN|BECN| DE | EA |
515162021Ssam *    +----+----+----+----+----+----+----+----+
516162021Ssam */
517162021Ssam
518162021Ssam    sequence_num = (p[0]&0x1e)<<7 | p[1];
519162021Ssam    /* whole packet or first fragment ? */
520162021Ssam    if ((p[0] & MFR_BEC_MASK) == MFR_FRAG_FRAME ||
521162021Ssam        (p[0] & MFR_BEC_MASK) == MFR_B_BIT) {
522162021Ssam        printf("FRF.16 Frag, seq %u, Flags [%s], ",
523162021Ssam               sequence_num,
524162021Ssam               bittok2str(frf_flag_values,"none",(p[0] & MFR_BEC_MASK)));
525162021Ssam        hdr_len = 2;
526162021Ssam        fr_print(p+hdr_len,length-hdr_len);
527162021Ssam        return hdr_len;
528162021Ssam    }
529162021Ssam
530162021Ssam    /* must be a middle or the last fragment */
531162021Ssam    printf("FRF.16 Frag, seq %u, Flags [%s]",
532162021Ssam           sequence_num,
533162021Ssam           bittok2str(frf_flag_values,"none",(p[0] & MFR_BEC_MASK)));
534162021Ssam    print_unknown_data(p,"\n\t",length);
535162021Ssam
536162021Ssam    return hdr_len;
537162021Ssam
538162021Ssam trunc:
539162021Ssam    printf("[|mfr]");
540162021Ssam    return length;
541162021Ssam}
542162021Ssam
543146778Ssam/* an NLPID of 0xb1 indicates a 2-byte
544146778Ssam * FRF.15 header
545146778Ssam *
546146778Ssam *      7    6    5    4    3    2    1    0
547146778Ssam *    +----+----+----+----+----+----+----+----+
548146778Ssam *    ~              Q.922 header             ~
549146778Ssam *    +----+----+----+----+----+----+----+----+
550146778Ssam *    |             NLPID (8 bits)            | NLPID=0xb1
551146778Ssam *    +----+----+----+----+----+----+----+----+
552146778Ssam *    | B  | E  | C  |seq. (high 4 bits) | R  |
553146778Ssam *    +----+----+----+----+----+----+----+----+
554146778Ssam *    |        sequence  (low 8 bits)         |
555146778Ssam *    +----+----+----+----+----+----+----+----+
556146778Ssam */
55732145Spst
558146778Ssam#define FR_FRF15_FRAGTYPE 0x01
559146778Ssam
560146778Ssamstatic void
561146778Ssamfrf15_print (const u_char *p, u_int length) {
562146778Ssam
563146778Ssam    u_int16_t sequence_num, flags;
564146778Ssam
565162021Ssam    flags = p[0]&MFR_BEC_MASK;
566146778Ssam    sequence_num = (p[0]&0x1e)<<7 | p[1];
567146778Ssam
568146778Ssam    printf("FRF.15, seq 0x%03x, Flags [%s],%s Fragmentation, length %u",
569146778Ssam           sequence_num,
570162021Ssam           bittok2str(frf_flag_values,"none",flags),
571162021Ssam           p[0]&FR_FRF15_FRAGTYPE ? "Interface" : "End-to-End",
572146778Ssam           length);
573146778Ssam
574146778Ssam/* TODO:
575146778Ssam * depending on all permutations of the B, E and C bit
576146778Ssam * dig as deep as we can - e.g. on the first (B) fragment
577146778Ssam * there is enough payload to print the IP header
578146778Ssam * on non (B) fragments it depends if the fragmentation
579146778Ssam * model is end-to-end or interface based wether we want to print
580146778Ssam * another Q.922 header
581146778Ssam */
582146778Ssam
58332145Spst}
58432145Spst
58532145Spst/*
58632145Spst * Q.933 decoding portion for framerelay specific.
58732145Spst */
58832145Spst
58932145Spst/* Q.933 packet format
59032145Spst                      Format of Other Protocols
59132145Spst                          using Q.933 NLPID
59232145Spst                  +-------------------------------+
59332145Spst                  |        Q.922 Address          |
59432145Spst                  +---------------+---------------+
59532145Spst                  |Control  0x03  | NLPID   0x08  |
59632145Spst                  +---------------+---------------+
59732145Spst                  |          L2 Protocol ID       |
59832145Spst                  | octet 1       |  octet 2      |
59932145Spst                  +-------------------------------+
60032145Spst                  |          L3 Protocol ID       |
60132145Spst                  | octet 2       |  octet 2      |
60232145Spst                  +-------------------------------+
60332145Spst                  |         Protocol Data         |
60432145Spst                  +-------------------------------+
60532145Spst                  | FCS                           |
60632145Spst                  +-------------------------------+
60732145Spst */
60832145Spst
60932145Spst/* L2 (Octet 1)- Call Reference Usually is 0x0 */
61032145Spst
61132145Spst/*
61232145Spst * L2 (Octet 2)- Message Types definition 1 byte long.
61332145Spst */
61432145Spst/* Call Establish */
61532145Spst#define MSG_TYPE_ESC_TO_NATIONAL  0x00
61632145Spst#define MSG_TYPE_ALERT            0x01
61732145Spst#define MSG_TYPE_CALL_PROCEEDING  0x02
61832145Spst#define MSG_TYPE_CONNECT          0x07
61932145Spst#define MSG_TYPE_CONNECT_ACK      0x0F
62032145Spst#define MSG_TYPE_PROGRESS         0x03
62132145Spst#define MSG_TYPE_SETUP            0x05
62232145Spst/* Call Clear */
62332145Spst#define MSG_TYPE_DISCONNECT       0x45
62432145Spst#define MSG_TYPE_RELEASE          0x4D
62532145Spst#define MSG_TYPE_RELEASE_COMPLETE 0x5A
62632145Spst#define MSG_TYPE_RESTART          0x46
62732145Spst#define MSG_TYPE_RESTART_ACK      0x4E
62832145Spst/* Status */
62932145Spst#define MSG_TYPE_STATUS           0x7D
63032145Spst#define MSG_TYPE_STATUS_ENQ       0x75
63132145Spst
632146778Ssamstruct tok fr_q933_msg_values[] = {
633146778Ssam    { MSG_TYPE_ESC_TO_NATIONAL, "ESC to National" },
634146778Ssam    { MSG_TYPE_ALERT, "Alert" },
635146778Ssam    { MSG_TYPE_CALL_PROCEEDING, "Call proceeding" },
636146778Ssam    { MSG_TYPE_CONNECT, "Connect" },
637146778Ssam    { MSG_TYPE_CONNECT_ACK, "Connect ACK" },
638146778Ssam    { MSG_TYPE_PROGRESS, "Progress" },
639146778Ssam    { MSG_TYPE_SETUP, "Setup" },
640146778Ssam    { MSG_TYPE_DISCONNECT, "Disconnect" },
641146778Ssam    { MSG_TYPE_RELEASE, "Release" },
642146778Ssam    { MSG_TYPE_RELEASE_COMPLETE, "Release Complete" },
643146778Ssam    { MSG_TYPE_RESTART, "Restart" },
644146778Ssam    { MSG_TYPE_RESTART_ACK, "Restart ACK" },
645146778Ssam    { MSG_TYPE_STATUS, "Status Reply" },
646146778Ssam    { MSG_TYPE_STATUS_ENQ, "Status Enquiry" },
647146778Ssam    { 0, NULL }
648146778Ssam};
64932145Spst
650146778Ssam#define MSG_ANSI_LOCKING_SHIFT	0x95
651146778Ssam
652146778Ssam#define FR_LMI_ANSI_REPORT_TYPE_IE	0x01
653146778Ssam#define FR_LMI_ANSI_LINK_VERIFY_IE_91	0x19 /* details? */
654146778Ssam#define FR_LMI_ANSI_LINK_VERIFY_IE	0x03
655146778Ssam#define FR_LMI_ANSI_PVC_STATUS_IE	0x07
656146778Ssam
657146778Ssam#define FR_LMI_CCITT_REPORT_TYPE_IE	0x51
658146778Ssam#define FR_LMI_CCITT_LINK_VERIFY_IE	0x53
659146778Ssam#define FR_LMI_CCITT_PVC_STATUS_IE	0x57
660146778Ssam
661146778Ssamstruct tok fr_q933_ie_values_codeset5[] = {
662146778Ssam    { FR_LMI_ANSI_REPORT_TYPE_IE, "ANSI Report Type" },
663146778Ssam    { FR_LMI_ANSI_LINK_VERIFY_IE_91, "ANSI Link Verify" },
664146778Ssam    { FR_LMI_ANSI_LINK_VERIFY_IE, "ANSI Link Verify" },
665146778Ssam    { FR_LMI_ANSI_PVC_STATUS_IE, "ANSI PVC Status" },
666146778Ssam    { FR_LMI_CCITT_REPORT_TYPE_IE, "CCITT Report Type" },
667146778Ssam    { FR_LMI_CCITT_LINK_VERIFY_IE, "CCITT Link Verify" },
668146778Ssam    { FR_LMI_CCITT_PVC_STATUS_IE, "CCITT PVC Status" },
669146778Ssam    { 0, NULL }
67032145Spst};
67132145Spst
672146778Ssam#define FR_LMI_REPORT_TYPE_IE_FULL_STATUS 0
673146778Ssam#define FR_LMI_REPORT_TYPE_IE_LINK_VERIFY 1
674146778Ssam#define FR_LMI_REPORT_TYPE_IE_ASYNC_PVC   2
67532145Spst
676146778Ssamstruct tok fr_lmi_report_type_ie_values[] = {
677146778Ssam    { FR_LMI_REPORT_TYPE_IE_FULL_STATUS, "Full Status" },
678146778Ssam    { FR_LMI_REPORT_TYPE_IE_LINK_VERIFY, "Link verify" },
679146778Ssam    { FR_LMI_REPORT_TYPE_IE_ASYNC_PVC, "Async PVC Status" },
680146778Ssam    { 0, NULL }
681146778Ssam};
68232145Spst
683162021Ssam/* array of 16 codepages - currently we only support codepage 1,5 */
684146778Ssamstatic struct tok *fr_q933_ie_codesets[] = {
685146778Ssam    NULL,
686162021Ssam    fr_q933_ie_values_codeset5,
687146778Ssam    NULL,
688146778Ssam    NULL,
689146778Ssam    NULL,
690146778Ssam    fr_q933_ie_values_codeset5,
691146778Ssam    NULL,
692146778Ssam    NULL,
693146778Ssam    NULL,
694146778Ssam    NULL,
695146778Ssam    NULL,
696146778Ssam    NULL,
697146778Ssam    NULL,
698146778Ssam    NULL,
699146778Ssam    NULL,
700146778Ssam    NULL
70132145Spst};
70232145Spst
703162021Ssamstatic int fr_q933_print_ie_codeset5(const struct ie_tlv_header_t  *ie_p,
704147904Ssam    const u_char *p);
705147904Ssam
706162021Ssamtypedef int (*codeset_pr_func_t)(const struct ie_tlv_header_t  *ie_p,
707147904Ssam    const u_char *p);
708147904Ssam
709162021Ssam/* array of 16 codepages - currently we only support codepage 1,5 */
710147904Ssamstatic codeset_pr_func_t fr_q933_print_ie_codeset[] = {
711147904Ssam    NULL,
712162021Ssam    fr_q933_print_ie_codeset5,
713147904Ssam    NULL,
714147904Ssam    NULL,
715147904Ssam    NULL,
716147904Ssam    fr_q933_print_ie_codeset5,
717147904Ssam    NULL,
718147904Ssam    NULL,
719147904Ssam    NULL,
720147904Ssam    NULL,
721147904Ssam    NULL,
722147904Ssam    NULL,
723147904Ssam    NULL,
724147904Ssam    NULL,
725147904Ssam    NULL,
726147904Ssam    NULL
727147904Ssam};
728147904Ssam
72932145Spstvoid
730146778Ssamq933_print(const u_char *p, u_int length)
73132145Spst{
73232145Spst	const u_char *ptemp = p;
733162021Ssam	struct ie_tlv_header_t  *ie_p;
734146778Ssam        int olen;
735146778Ssam	int is_ansi = 0;
736147904Ssam        u_int codeset;
737162021Ssam        u_int ie_is_known = 0;
738146778Ssam
739146778Ssam	if (length < 9) {	/* shortest: Q.933a LINK VERIFY */
740146778Ssam		printf("[|q.933]");
741146778Ssam		return;
742146778Ssam	}
743146778Ssam
744146778Ssam        codeset = p[2]&0x0f;   /* extract the codeset */
745146778Ssam
746190207Srpaulo	if (p[2] == MSG_ANSI_LOCKING_SHIFT) {
747190207Srpaulo	        is_ansi = 1;
748190207Srpaulo	}
74932145Spst
750146778Ssam        printf("%s", eflag ? "" : "Q.933, ");
75132145Spst
75232145Spst	/* printing out header part */
753162021Ssam	printf("%s, codeset %u", is_ansi ? "ANSI" : "CCITT", codeset);
754146778Ssam
755190207Srpaulo	if (p[0]) {
756190207Srpaulo	        printf(", Call Ref: 0x%02x", p[0]);
757190207Srpaulo	}
758190207Srpaulo        if (vflag) {
759190207Srpaulo                printf(", %s (0x%02x), length %u",
760190207Srpaulo		       tok2str(fr_q933_msg_values,
761190207Srpaulo			       "unknown message", p[1]),
762190207Srpaulo		       p[1],
763190207Srpaulo		       length);
764190207Srpaulo        } else {
765190207Srpaulo                printf(", %s",
766190207Srpaulo		       tok2str(fr_q933_msg_values,
767190207Srpaulo			       "unknown message 0x%02x", p[1]));
768190207Srpaulo	}
769146778Ssam
770146778Ssam        olen = length; /* preserve the original length for non verbose mode */
771146778Ssam
772146778Ssam	if (length < (u_int)(2 - is_ansi)) {
773146778Ssam		printf("[|q.933]");
774146778Ssam		return;
77532145Spst	}
776190207Srpaulo	length -= 2 + is_ansi;
777146778Ssam	ptemp += 2 + is_ansi;
77832145Spst
77932145Spst	/* Loop through the rest of IE */
780190207Srpaulo	while (length > sizeof(struct ie_tlv_header_t)) {
781162021Ssam		ie_p = (struct ie_tlv_header_t  *)ptemp;
782190207Srpaulo		if (length < sizeof(struct ie_tlv_header_t) ||
783190207Srpaulo		    length < sizeof(struct ie_tlv_header_t) + ie_p->ie_len) {
784190207Srpaulo                    if (vflag) { /* not bark if there is just a trailer */
785146778Ssam                        printf("\n[|q.933]");
786190207Srpaulo                    } else {
787146778Ssam                        printf(", length %u",olen);
788190207Srpaulo		    }
789146778Ssam                    return;
79032145Spst		}
79132145Spst
792146778Ssam                /* lets do the full IE parsing only in verbose mode
793146778Ssam                 * however some IEs (DLCI Status, Link Verify)
794190207Srpaulo                 * are also interestting in non-verbose mode */
795190207Srpaulo                if (vflag) {
796162021Ssam                    printf("\n\t%s IE (0x%02x), length %u: ",
797190207Srpaulo                           tok2str(fr_q933_ie_codesets[codeset],
798190207Srpaulo				   "unknown", ie_p->ie_type),
799162021Ssam                           ie_p->ie_type,
800146778Ssam                           ie_p->ie_len);
801190207Srpaulo		}
802190207Srpaulo
803162021Ssam                /* sanity check */
804190207Srpaulo                if (ie_p->ie_type == 0 || ie_p->ie_len == 0) {
805162021Ssam                    return;
806190207Srpaulo		}
80732145Spst
808190207Srpaulo                if (fr_q933_print_ie_codeset[codeset] != NULL) {
809162021Ssam                    ie_is_known = fr_q933_print_ie_codeset[codeset](ie_p, ptemp);
810190207Srpaulo		}
811190207Srpaulo
812190207Srpaulo                if (vflag >= 1 && !ie_is_known) {
813162021Ssam                    print_unknown_data(ptemp+2,"\n\t",ie_p->ie_len);
814190207Srpaulo		}
815162021Ssam
816146778Ssam                /* do we want to see a hexdump of the IE ? */
817190207Srpaulo                if (vflag> 1 && ie_is_known) {
818146778Ssam                    print_unknown_data(ptemp+2,"\n\t  ",ie_p->ie_len);
819190207Srpaulo		}
82032145Spst
821146778Ssam		length = length - ie_p->ie_len - 2;
822146778Ssam		ptemp = ptemp + ie_p->ie_len + 2;
823146778Ssam	}
824190207Srpaulo        if (!vflag) {
825146778Ssam            printf(", length %u",olen);
826190207Srpaulo	}
827146778Ssam}
828147904Ssam
829147904Ssamstatic int
830162021Ssamfr_q933_print_ie_codeset5(const struct ie_tlv_header_t  *ie_p, const u_char *p)
831147904Ssam{
832147904Ssam        u_int dlci;
833147904Ssam
834162021Ssam        switch (ie_p->ie_type) {
835147904Ssam
836147904Ssam        case FR_LMI_ANSI_REPORT_TYPE_IE: /* fall through */
837147904Ssam        case FR_LMI_CCITT_REPORT_TYPE_IE:
838190207Srpaulo            if (vflag) {
839147904Ssam                printf("%s (%u)",
840147904Ssam                       tok2str(fr_lmi_report_type_ie_values,"unknown",p[2]),
841147904Ssam                       p[2]);
842190207Srpaulo	    }
843147904Ssam            return 1;
844147904Ssam
845147904Ssam        case FR_LMI_ANSI_LINK_VERIFY_IE: /* fall through */
846147904Ssam        case FR_LMI_CCITT_LINK_VERIFY_IE:
847147904Ssam        case FR_LMI_ANSI_LINK_VERIFY_IE_91:
848190207Srpaulo            if (!vflag) {
849147904Ssam                printf(", ");
850190207Srpaulo	    }
851147904Ssam            printf("TX Seq: %3d, RX Seq: %3d", p[2], p[3]);
852147904Ssam            return 1;
853147904Ssam
854147904Ssam        case FR_LMI_ANSI_PVC_STATUS_IE: /* fall through */
855147904Ssam        case FR_LMI_CCITT_PVC_STATUS_IE:
856190207Srpaulo            if (!vflag) {
857147904Ssam                printf(", ");
858190207Srpaulo	    }
859147904Ssam            /* now parse the DLCI information element. */
860147904Ssam            if ((ie_p->ie_len < 3) ||
861147904Ssam                (p[2] & 0x80) ||
862147904Ssam                ((ie_p->ie_len == 3) && !(p[3] & 0x80)) ||
863147904Ssam                ((ie_p->ie_len == 4) && ((p[3] & 0x80) || !(p[4] & 0x80))) ||
864147904Ssam                ((ie_p->ie_len == 5) && ((p[3] & 0x80) || (p[4] & 0x80) ||
865147904Ssam                                   !(p[5] & 0x80))) ||
866147904Ssam                (ie_p->ie_len > 5) ||
867190207Srpaulo                !(p[ie_p->ie_len + 1] & 0x80)) {
868147904Ssam                printf("Invalid DLCI IE");
869190207Srpaulo	    }
870147904Ssam
871147904Ssam            dlci = ((p[2] & 0x3F) << 4) | ((p[3] & 0x78) >> 3);
872190207Srpaulo            if (ie_p->ie_len == 4) {
873147904Ssam                dlci = (dlci << 6) | ((p[4] & 0x7E) >> 1);
874190207Srpaulo	    }
875190207Srpaulo            else if (ie_p->ie_len == 5) {
876147904Ssam                dlci = (dlci << 13) | (p[4] & 0x7F) | ((p[5] & 0x7E) >> 1);
877190207Srpaulo	    }
878147904Ssam
879147904Ssam            printf("DLCI %u: status %s%s", dlci,
880147904Ssam                    p[ie_p->ie_len + 1] & 0x8 ? "New, " : "",
881147904Ssam                    p[ie_p->ie_len + 1] & 0x2 ? "Active" : "Inactive");
882147904Ssam            return 1;
883147904Ssam	}
884147904Ssam
885147904Ssam        return 0;
886147904Ssam}
887