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