#!/usr/local/bin/perl
#
# pmdecode
#
# Read and interpret ISDN and/or PPP debugging output from a Lucent
# PortMaster.
#
# Usage: pmdecode [-cehiIpsx] debugfile
#
#   -c : show passwords in clear (masked by default).
#   -d xxx : only decode ISDN on line Dxxx.
#   -e : echo hex representation of data from input file.
#   -h : show detailed hex breakdown of ppp packets.
#   -i : interpret ISDN messages, terse mode.
#   -I : interpret ISDN messages, verbose mode.
#   -p : interpret ppp packets.
#   -s xxx : only decode PPP on port Sxxx.
#   -v : suppress some vertical spacing (more compact, less readable).
#   -x : eXclusive: print only the recognized lines.
#
#  debugfile : path name of file containing the debugging information.
#        If left blank, read from standard input.
#
# This program reads, pretty prints and interprets the output
# produced by the 'set debug isdn-d' and 'set debug 0x51' commands.
# Usage of 'set debug clock on' is recommended, but not mandatory.
#
# The ISDN sigalling interpreter is meant to at least pretty
# print any valid Q.921/Q.931 frame, but since there are so
# many extension mechanisms available in those standards, anything
# can happen if used to read debugging information coming from
# untested contexts. 
# 
# Currently, It has only been tested with the ouput of a PortMaster 3
# connected to an European ISDN (E1) line, using DSS1 signalling.
#
# The PPP packet interpreter should be able to decode any code or
# option found in the following RFC's: 1332, 1334, 1552, 1570, 1661,
# 1662, 1663, 1877, 1962, 1974, 1990, 1994, 2125, 2153, 2290, 2484.
#
# (C) 1999 Alain Fontaine <fontaine@sri.ucl.ac.be>
#
# This program may be freely copied, distributed and run.
#
# Disclaimer: this piece of software is provided 'as-is'.
# It is not guaranteed to give any useful result, nor to
# be harmless in all possible situations. If this piece
# of software is not exactly what you were dreaming it
# would be, you are entitled to erase it from your disk,
# hereby recovering the valuable space it occupied.
#
# If you encounter some interesting erroneous results, you
# may always drop me a note, including the original lines
# of debugging output triggering the firework.
# Comments about the programming style are welcome at the
# nearest /dev/null.
#
# 1999-05-02 - 1.0
#     First public release
#

# Initialize the global variables representing the options
($printall,$echohex,$nospace,$isdn,$isdnd,$pppd,$printhex,$clearpw) = (1,0,0,0,0,0,0,0);

# Parse arguments
$i = 0;
$arg = $ARGV[0];
while ($arg =~ s/^-//) {
  while ($arg) {
    ($flag,$arg) = split(//,$arg,2);
    if ($flag eq 'c') { $clearpw = 1;
    } elsif ($flag eq 'd') {
      die "Syntax error on flag 'd'\n" if $arg;
      $dnum = $ARGV[++$i];
    } elsif ($flag eq 'e') { $echohex = 1;
    } elsif ($flag eq 'h') { $printhex = 1;
    } elsif ($flag eq 'i') { $isdn = 1;
    } elsif ($flag eq 'I') { ($isdn,$isdnd) = (1,1);
    } elsif ($flag eq 'p') { $pppd = 1;
    } elsif ($flag eq 's') {
      die "Syntax error on flag 's'\n" if $arg;
      $pnum = $ARGV[++$i];
    } elsif ($flag eq 'v') { $nospace = 1;
    } elsif ($flag eq 'x') { $printall = 0;
    } else { die "Bad flag '$flag'\n";
    }
  }
  $arg = $ARGV[++$i]
}
unless ($arg) {
  $arg = '-';
  $| = 1;
}
 
# Open the file specified on the command line
open(IN, $arg) ||
  die "Could not open file '$arg'\n";

# Initialize the interpretation tables
&initpppdata if $pppd;
&initisdndata if $isdn;

# Read debug file and print data
$line = <IN>;
$wanthex = 0;
while ($line) {
  $line =~ s/[\n\r]//go;
  $clock = $line =~ s/^[^\[]*(\[[^\]]*\] )//o;
  $clock = $1 if $clock;
  if ($wanthex) {
    if ($line=~/(([\dabcdefABCDEF][\dabcdefABCDEF]\s)+)$/o) {
      $bytes = $bytes.$1;
      print $clock,$line,"\n" if $echohex;
    } else {
      $wanthex = 0;
      $bytes =~ s/[\s]//go;
      $bytes = pack("H*",$bytes);
      parseppp($ppptype) if $bytes;
      print "\n" unless $nospace;
    }
  }
  unless ($wanthex) {
    $noreco = 1;
    if ($isdn&&$line=~s/^[^D]*(D[\d]: (recv|send) )//o) {
      $dir = $1;
      if (!length($dnum)||$dir=~/^D$dnum/) {
        ($bytes = $line) =~ s/[\s]//go;
        $bytes = pack("H*",$bytes);
        print $clock,$dir;
        print "$line\n" if $echohex;
        &parselapd(index($dir,'recv')>0);
        print "\n" unless $nospace;
        $noreco = 0;
      }
    } elsif ($pppd&&$line=~/(LCP|PAP|CHAP|CCP|BACP|IPCP|IPXCP|BAP)/o) {
      $ppptype = $1;
      if (!length($pnum)||$line=~/on port S$pnum|S$pnum:/) {
        $wanthex = $line=~/containing:/;
        print $clock,$line,"\n";
        $bytes = '' if $wanthex;
        $noreco = 0;
      }
    }
    print $clock,$line,"\n" if $printall&&$noreco;
  }
  $line = <IN>;
}

sub parseppp {
  my($ppptype) = @_;
  my($code,$id,$length,$head);
  ($code,$id,$length) = unpack('C C n',$bytes);
  $head = sprintf '  Code: %2d, ID: %3d, Length: %4d',$code,$id,$length;
  if ($length!=length($bytes)) {
    if ($length<4) {
      print "***** Bad length, must be >= 4\n";
      $length = 4;
    } elsif ($length>length($bytes)) {
      print '***** Bad length, frame only contains ',length($bytes)," bytes\n";
    }
  }
  hexprint($bytes,4) if $printhex;
  print "$head";
  $bytes = substr($bytes,4,$length-4);
  if ($code==0) { &parsespecific;
  } elsif ($ppptype eq 'LCP') { parselcp($code);
  } elsif ($ppptype eq 'PAP') { parsepap($code);
  } elsif ($ppptype eq 'CHAP') { parsechap($code);
  } elsif ($ppptype eq 'CCP') { parseccp($code);
  } elsif ($ppptype eq 'BACP') {
    parselcp1to7only($ppptype,$code,'parsebacpopt');
  } elsif ($ppptype eq 'IPCP') {
    parselcp1to7only($ppptype,$code,'parseipcpopt');
  } elsif ($ppptype eq 'IPXCP') {
    parselcp1to7only($ppptype,$code,'parseipxcpopt');
  } elsif ($ppptype eq 'BAP') { parsebap($code);
  } else { print "\n";
  }
  printend('','Additionnal data','x') if $bytes;
}

sub parsespecific {
  my($val1,$val2);
  print "   $lcpcode[0]\n";
  hexprint($bytes,4) if $printhex;
  ($val1,$bytes) = unpack('H8 a*',$bytes);
  print "    Magic number: x'$val1'\n";
  hexprint($bytes,4) if $printhex;
  ($val1,$val2,$bytes) = unpack('H6 C a*',$bytes);
  print "    OUI: x'$val1', Kind: $val2\n";
  printend('','Vendor data:','x');
}

sub parselcp {
  my($code) = @_;
  my($val1);
  print "   $lcpcode[$code]\n";
  if ($code>=1&&$code<=7) {
    parselcp1to7('LCP',$code,'parselcpopt');
  } elsif ($code==8) {
    hexprint($bytes,2) if $printhex;
    ($val1,$bytes) = unpack('H4',$bytes);
    print "    Rejected protocol: x'$val1' ($protocol{$val1})";
    print " - Skipping rest of frame\n";
  } elsif ($code>=9&&$code<=11) {
    hexprint($bytes,4) if $printhex;
    ($val1,$bytes) = unpack('H8 a*',$bytes);
    print "    Magic number: x'$val1'\n";
    printend('','Data:','x') if ($bytes);
  } elsif ($code==12) {
    hexprint($bytes,4) if $printhex;
    ($val1,$bytes) = unpack('H8 a*',$bytes);
    print "    Magic number: x'$val1'\n";
    printend('','Message:','') if ($bytes);
  } elsif ($code==13) {
    hexprint($bytes,4) if $printhex;
    ($val1,$bytes) = unpack('H8 a*',$bytes);
    print "    Magic number: x'$val1'\n";
    hexprint($bytes,4) if $printhex;
    ($val1,$bytes) = unpack('N a*',$bytes);
    print "    Seconds remaining: $val1\n";
    printend('','Message:','') if ($bytes);
  } else {
    printend("***** Unknown LCP code\n",'Content:','x');
  }
}

sub parselcp1to7 {
  my($ppptype,$code,$optsub) = @_;
  if ($code>=1&&$code<=4) {
    parseopts($bytes,1,$optsub);
    $bytes = '';
  } elsif ($code==5||$code==6) {
    printend('','Data:','x') if ($bytes);
  } elsif ($bytes) {
    print "***** Content of rejected packet *************************************\n";
    parseppp($ppptype);
  }
}

sub parsepap {
  my($code) = @_;
  my($len,$val1);
  print "   $papcode[$code]\n";
  if ($code==1) {
    $len = unpack('C',$bytes);
    hexprint($bytes,$len+1) if $printhex;
    ($len,$val1,$bytes) = unpack("C a$len a*",$bytes);
    print "    Length: $len, Peer-id: '",cleanstr($val1),"'\n";
    $len = unpack('C',$bytes);
    if ($printhex) {
      $val1 = $bytes;
      $val1 = substr($val1,0,1)."\x00"x $len unless $clearpw;
      hexprint($val1,$len+1);
    }
    ($len,$val1,$bytes) = unpack("C a$len a*",$bytes);
    $val1 =~ s/./*/go unless $clearpw;
    print "    Length: $len, Password: '",cleanstr($val1),"'\n";
  } elsif ($code==2||$code==3) {
    $len = unpack('C',$bytes);
    hexprint($bytes,$len+1) if $printhex;
    ($len,$val1,$bytes) = unpack("C a$len a*",$bytes);
    print "    Length: $len, Message: '",cleanstr($val1),"'\n";
  } else {
    printend("***** Unknown PAP code\n",'Content:','x');
  }
}

sub parsechap {
  my($code) = @_;
  my($len,$val1);
  print "   $chapcode[$code]\n";
  if ($code==1||$code==2) {
    $len = unpack('C',$bytes);
    hexprint($bytes,$len+1) if $printhex;
    $len *= 2;
    ($len,$val1,$bytes) = unpack("C H$len a*",$bytes);
    print "    Length: $len, Value: x'$val1'\n";
    printend('','Name:','') if ($bytes);
  } elsif ($code==3||$code==4) {
    printend('','Message:','') if ($bytes);
  } else {
    printend("***** Unknown CHAP code\n",'Content:','x');
  }
}

sub parseccp {
  my($code) = @_;
  if ($code>=1&&$code<=7) {
    print "   $lcpcode[$code]\n";
    parselcp1to7('CCP',$code,'parseccpopt');
  } elsif ($code==14||$code==15) {
    print "   $lcpcode[$code]\n";
    printend('','Data:','x');
  } else {
    printend("\n***** Unknown CCP code\n",'Content:','x');
  }
}

sub parselcp1to7only {
  my($ppptype,$code,$optsub) = @_;
  if ($code>=1&&$code<=7) {
    print "   $lcpcode[$code]\n";
    parselcp1to7($ppptype,$code,$optsub);
  } else {
    printend("\n***** Unknown $ppptype code\n",'Content:','x');
  }
}

sub parsebap {
  my($code) = @_;
  my($val1);
  print "   $bapcode[$code]\n";
  if ($code==2||$code==4||$code==6||$code==8) {
    hexprint($bytes,1) if $printhex;
    ($val1,$bytes) = unpack('C a*',$bytes);
    print "    Response code: $val1 ($bapresp[$val1])\n";
  }
  if ($code>8) {
    printend("***** Unknown BAP code\n",'Content:','x');
  } else {
    parseopts($bytes,1,'parsebapopt');
    $bytes = '';
  }
}

sub printend {
  my($msg,$label,$mode) = @_;
  print "$msg";
  hexprint($bytes,0) if $printhex;
  print "    $label $mode'",$mode ? unpack('H*',$bytes) : cleanstr($bytes),"'\n";
  $bytes = '';
}

sub parseopts {
  my($options,$level,$optsub) = @_;
  my($type,$length,$option,$opthead);
  while($options) {
    ($type,$length) = unpack('C C',$options);
    $opthead = $level==1 ? sprintf '    Op: %3d, Length: %3d',$type,$length :
               sprintf '     +    Subop: %3d, Length: %3d',$type,$length;
    if ($length<2) {
      print "***** Bad option length, must be >= 2\n";
      $length = 2;
    } elsif ($length>length($options)) {
      print '***** Bad option length, only ',length($options)," bytes available\n";
    }
    ($option,$options) = unpack("a$length a*",$options);
    $option = &$optsub($type,$option,$opthead) if $optsub;
    if ($option) {
      hexprint($option,0) if $printhex;
      print "     +    Additional Data: x'",unpack('H*',$option),"'\n";
    }
  }
}

sub parselcpopt {
  my($type,$option,$opthead) = @_;
  my($val1,$val2);
  hexprint($option,2+$lcpoptlen{$type}) if $printhex;
  print "$opthead   $lcpoption{$type}";
  $option = substr($option,2);
  if ($type==0) {
    print "\n";
    hexprint($option,4) if $printhex;
    ($val1,$val2,$option) = unpack('H6 C a*',$option);
    print "     +    OUI: x'$val1', Kind: $val2\n";
    hexprint($option,0) if $printhex;
    ($val1,$option) = unpack('H*',$option);
    print "     +    Vendor data: x'$val1'\n";
  } elsif ($type==1||$type==17) {
    ($val1,$option) = unpack('n a*',$option);
    print ": $val1\n";
  } elsif ($type==2||$type==5) {
    ($val1,$option) = unpack('H8 a*',$option);
    print ": x'$val1'\n";
  } elsif ($type==3) {
    print "\n";
    hexprint($option,2) if $printhex;
    ($val1,$option) = unpack('H4 a*',$option);
    print "     +    x'$val1' ($protocol{$val1})\n";
    if ($val1 eq 'c223') {
      hexprint($option,1) if $printhex;
      ($val1,$option) = unpack('C a*',$option);
      print "     +    Algorithm: $val1 ($chapalg{$val1})\n";
    }
  } elsif ($type==4) {
    print "\n";
    hexprint($option,2) if $printhex;
    ($val1,$option) = unpack('H4 a*',$option);
    print "     +    x'$val1' ($protocol{$val1})\n";
    if ($val1 eq 'c025') {
      hexprint($option,4) if $printhex;
      ($val1,$option) = unpack('N a*',$option);
      print "     +    Reporting period: $val1 hundredths of seconds\n";
    }
  } elsif ($type==9) {
    print "\n";
    hexprint($option,1) if $printhex;
    ($val1,$option) = unpack('a a*',$option);
    print "     +    b'",unpack('B8',$val1),"' Use: ";
    $val1 = unpack('C',$val1);
    for ($i = 0;$i<=2;$i++) {
      print " $fcsalt[$i]" if $val1%2==1;
      $val1 = $val1>>1;
      print ',' if $val1;
    }
    print "\n";
  } elsif ($type==10) {
    ($val1,$option) = unpack('C a*',$option);
    print " - Max: $val1\n";
  } elsif ($type==11) {
    print "\n";
    hexprint($option,0) if $printhex;
    ($val1,$val2,$option) = unpack('C H*',$option);
    print "     +    Window: $val1, Address: x'$val2'\n";
  } elsif ($type==13) {
    print "\n";
    hexprint($option,1) if $printhex;
    ($val1,$option) = unpack('C a*',$option);
    print "     +    Operation: $val1 ($backoper[$val1])\n";
    hexprint($option,0) if $printhex;
    ($val1,$option) = unpack('a*',$option);
    print "     +    Message: '",cleanstr($val1),"'\n";
  } elsif ($type==19) {
    print "\n";
    hexprint($option,1) if $printhex;
    ($val1,$option) = unpack('C a*',$option);
    print "     +    Class: $val1 ($endclass[$val1])\n";
    if ($val1==1) {
      hexprint($option,0) if $printhex;
      print "     +    Address: x'",unpack('H*',$option),"'\n";
      $option = '';
    } elsif ($val1==2) {
      hexprint($option,4) if $printhex;
      ($val1,$option) = unpack('a4 a*',$option);
      print "     +    IP address: ",join('.',unpack('CCCC',$val1)),"\n";
    } elsif ($val1==3) {
      hexprint($option,6) if $printhex;
      ($val1,$option) = unpack('a6 a*',$option);
      print "     +    MAC address: ",join('-',unpack('H2H2H2H2H2H2',$val1)),"\n";
    } elsif ($val1==4) {
      hexprint($option,0) if $printhex;
      print "     +    Magic numbers: x'",unpack('H*',$option),"'\n";
      $option = '';
    } elsif ($val1==5) {
      hexprint($option,0) if $printhex;
      print "     +    Directory number: '",cleanstr($option),"'\n";
      $option = '';
    }
  } elsif ($type==23) {
    ($val1,$option) = unpack('H4 a*',$option);
    print ": x'$val1'\n";
  } elsif ($type==28) {
    print "\n";
    hexprint($option,4) if $printhex;
    ($val1,$option) = unpack('N a*',$option);
    print "     +    MIBenum: $val1\n";
    hexprint($option,0) if $printhex;
    ($val1,$option) = unpack('a*',$option);
    print "     +    Language-Tag: ",cleanstr($val1),"\n";
  } elsif ($type==7||$type==8||$type==15||$type==18) {
    print "\n";
  } else {
    print 'Unknown LCP option' unless $lcpoption{$type};
    print "\n";
  }
  return $option;
}

sub parseccpopt {
  my($type,$option,$opthead) = @_;
  my($oui,$subtype,$val1,$val2);
  hexprint($option,2) if $printhex;
  print "$opthead   $ccpoption{$type}";
  $option = substr($option,2);
  if ($type==0) {
    print "\n";
    hexprint($option,4) if $printhex;
    ($oui,$subtype,$option) = unpack('H6 C a*',$option);
    print "     +    OUI: x'$oui', Subtype: $subtype\n";
    hexprint($option,0) if $printhex;
    ($val1,$option) = unpack('H*',$option);
    print "     +    Values: x'$val1'\n";
  } elsif ($type==17) {
    print "\n";
    hexprint($option,3) if $printhex;
    ($val1,$val2,$option) = unpack('n C a*',$option);
    print "     +    History count: $val1, Check Mode: $val2 ($lszcm[$val2])\n";
  } else {
    print 'Unknown CCP option' unless $ccpoption{$type}; 
    print "\n";
  }
  return $option;
}

sub parsebacpopt {
  my($type,$option,$opthead) = @_;
  my($val1);
  hexprint($option,2+$bacpoptlen[$type]) if $printhex;
  print "$opthead   $bacpoption[$type]";
  $option = substr($option,2);
  if ($type==1) {
    ($val1,$option) = unpack('H8 a*',$option);
    print " - Magic-number: x'$val1'\n";
  } else {
    print "Unknown BACP option\n";
  }
  return $option;
}

sub parseipcpopt {
  my($type,$option,$opthead) = @_;
  my($val1,$val2,$val3);
  hexprint($option,2+$ipcpoptlen{$type}) if $printhex;
  print "$opthead   $ipcpoption{$type}";
  $option = substr($option,2);
  if ($type==1) {
    print "\n";
    hexprint($option,4) if $printhex;
    ($val1,$option) = unpack('a4 a*',$option);
    print '     +    Source-IP-Address: ',join('.',unpack('CCCC',$val1)),"\n";
    hexprint($option,4) if $printhex;
    ($val1,$option) = unpack('a4 a*',$option);
    print '     +    Destination-IP-Address: ',join('.',unpack('CCCC',$val1)),"\n";
  } elsif ($type==2) {
    print "\n";
    hexprint($option,2) if $printhex;
    ($val1,$option) = unpack('H4 a*',$option);
    print "     +    x'$val1' ($protocol{$val1})\n";
    if ($val1 eq '002d') {
      hexprint($option,2) if $printhex;
      ($val2,$val3,$option) = unpack('C C a*',$option);
      print "     +    Max-Slot-Id: $val2, Comp-Slot-Id: $val3\n";
    }
  } elsif ($type==3||$type==4||($type>=129&&$type<=132)) {
    ($val1,$option) = unpack('a4 a*',$option);
    print ': ',join('.',unpack('CCCC',$val1)),"\n";
  } else {
    print "Unknown IPCP option\n";
  }
  return $option;
}

sub parseipxcpopt {
  my($type,$option,$opthead) = @_;
  my($val1);
  hexprint($option,2+$ipxcpoptlen[$type]) if $printhex;
  print "$opthead   $ipxcpoption[$type]";
  $option = substr($option,2);
  if ($type==1) {
    ($val1,$option) = unpack('H8 a*',$option);
    print ": x'$val1'\n";
  } elsif ($type==2) {
    ($val1,$option) = unpack('H12 a*',$option);
    print ": x'$val1'\n";
  } elsif ($type==3) {
    print "\n";
    hexprint($option,2) if $printhex;
    ($val1,$option) = unpack('H4 a*',$option);
    print "     +    x'$val1' ($protocol{$val1})\n";
  } elsif ($type==4) {
    print "\n";
    hexprint($option,2) if $printhex;
    ($val1,$option) = unpack('n a*',$option);
    print "     +    $val1 ($ipxcprouting[$val1])\n";
  } elsif ($type==5) {
    print "\n";
    hexprint($option,0) if $printhex;
    ($val1,$option) = unpack('a*',$option);
    print "     +    Name: '",cleanstr($val1),"'\n";
  } elsif ($type==6) {
    print "\n";
  } else {
    print "Unknown IPXCP option\n";
  }
  return $option;
}

sub parsebapopt {
  my($type,$option,$opthead) = @_;
  my($val1,$val2);
  hexprint($option,2+$bapoptlen[$type]) if $printhex;
  print "$opthead   $bapoption[$type]";
  $option = substr($option,2);
  if ($type==1) {
    print "\n";
    hexprint($option,2) if $printhex;
    ($val1,$option) = unpack('n a*',$option);
    print "     +    Link speed: $val1 kbps\n";
    hexprint($option,1) if $printhex;
    ($val1,$option) = unpack('a a*',$option);
    print "     +    Link Type : b'",unpack('B8',$val1),"' -";
    $val1 = unpack('C',$val1);
    for ($i = 0;$i<=4;$i++) {
      print " $bapltyp[$i]" if $val1%2==1;
      $val1 = $val1>>1;
      print ',' if $val1;
    }
    print "\n";
  } elsif ($type==2) {
    print "\n";
    parseopts($option,2,'parsebap2subopt');
    $option = '';
  } elsif ($type==4) {
    print "\n";
    hexprint($option,0) if $printhex;
    ($val1,$option) = unpack('a*',$option);
    print "     +    Reason: '",cleanstr($val1),"'\n";
  } elsif ($type==5) {
    ($val1,$option) = unpack('H4 a*',$option);
    print ": x'$val1'\n";
  } elsif ($type==6) {
    print "\n";
    hexprint($option,2) if $printhex;
    ($val1,$val2,$option) = unpack('C C a*',$option);
    print "     +    Status: $val1, Action: $val2 ($bapact[$val2])\n";
  } else {
    print "Unknown BAP option" unless $bapoption[$type];
    print "\n";
  }
  return $option;
}

sub parsebap2subopt {
  my($type,$option,$opthead) = @_;
  my($val1,$val2);
  hexprint($option,2+$bapop2sublen[$type]) if $printhex;
  print "$opthead   $bapop2sub[$type]";
  $option = substr($option,2);
  if ($type==1) {
    ($val1,$option) = unpack('C a*',$option);
    print ": $val1\n";
  } elsif ($type==2||$type==3) {
    ($val1,$option) = unpack('a*',$option);
    print ": '",cleanstr($val1),"'\n";
  } else {
    print "Unknown BAP suboption" unless $bapop2sub[$type];
    print "\n";
  }
  return $option;
}

sub hexprint {
  my($bytes,$num) = @_;
  my($endstr);
  $num = length($bytes) if $num==0;
  ($num,$endstr) = (6,'+') if $num>6;
  $num *= 2;
  printf '%-14s',unpack("H$num",$bytes).$endstr;
}

sub cleanstr {
  my($message) = @_;
  $message =~ s/[^\w !"#$%&'()*+,-.\/:;<=>?@[\\\]^_`{|}~]/ /go;
  return $message;
}

sub parselapd {
  my($dir) = @_;
  my($byte,$resp,$nntyp,$ns,$data);
  printf '%3.0d octets LAPD',length($bytes);
  $data = 0;
  if ((substr($bytes,0,2) & "\x01\x01") eq "\x00\01") {
    ($byte,$bytes) = split(//,$bytes,2);
    $resp = (ord($byte & "\x02")/2+$dir)%2;
    print " $crbit[$resp] SAPI=",ord($byte & "\xFC")>>2;
    ($byte,$bytes) = split(//,$bytes,2);
    print ' TEI=',ord($byte & "\xFE")>>1,' ';
    if ($bytes) {
      ($byte,$bytes) = split(//,$bytes,2);
      if (ord($byte & "\x01")) {
        if (ord($byte & "\x02")) {
          $nntyp = ord($byte & "\xEC");
          print "$utype{$nntyp} $pfval[$resp]=",ord($byte & "\x10")>>4;
          if ($nntyp==0) {
            $data = 1;
          } elsif ($nntyp==132) {
            ($nntyp,$bytes) = unpack("a5 a*",$bytes);
            print " '",unpack('H*',$nntyp),"'";
          } elsif ($nntyp==172) {
            ($ns,$bytes) = unpack('H*',$bytes);
            print "\n  XID data: '$ns'";
          }
        } else {
          print "$stype[ord($byte & \"\x0C\")>>2] $pfval[$resp]=";
          ($byte,$bytes) = split(//,$bytes,2);
          print ord($byte & "\x01"), ' NR=',ord($byte)>>1;
        }
      } else {
        $data = 1;
        $ns = ord($byte)>>1;
        ($byte,$bytes) = split(//,$bytes,2);
        print 'INFO  P=',ord($byte & "\x01"), ' NR=',ord($byte)>>1," NS=$ns";
      }
    }
    print "\n";
  } else {
    print " ***** Unexpected address octets: '",unpack('H*',substr($bytes,0,2)),"'\n",
          " ***** Frame rejected\n";
    $bytes = '';
  }
  if ($bytes) {
    if ($data) {
      &parsemessage;
    } else {  
      print " ***** Unexpected data: '",unpack('H*',$bytes),"'\n";
      $bytes = '';
    }
  }
}

sub parsemessage {
  my($pdisc,$crl,$cref,$messtyp);
  print ' ',sprintf('%3.0d',length($bytes))," octets signalling:\n";
  ($byte,$bytes) = split(//,$bytes,2);
  if ($byte ne "\x08") {
    print '***** Unexpected Protocol Discriminator: ',ord($byte),".\n";
    $pdisc = ' PD=?????    ';
  } else {
    $pdisc = ' PD=Q.931    ';
  }
  ($byte,$bytes) = split(//,$bytes,2);
  $crl = ord($byte);
  ($cref,$bytes) = unpack("a$crl a*",$bytes) if $crl;
  print "   $calldir[ord(substr($cref,0,1))>>7]";
  $cref = $cref & "\x7F".("\xFF" x ($crl-1));
  print ' call CR=0x',unpack('H*',$cref),"$pdisc";
  ($byte,$bytes) = split(//,$bytes,2);
  $messtyp = $messtyp{ord($byte)};
  $messtyp = 'UNKNOW MESSAGE TYPE' if $messtyp eq '';
  print "$messtyp\n";
  while ($isdnd && $bytes) {
    &parselement;
  }
}

sub parselement {
  my($count) = 1;
  my($byte,$ielcode,$elen,$elem);
  ($byte,$bytes) = split(//,$bytes,2);
  if (ord($byte)>127) {
    printbyte($count,0,$byte,0,8,'INFORMATION ELEMENT',1);
    print "Single octet\n";
  } else {
    printbyte($count,0,$byte,0,8,'INFORMATION ELEMENT',1);
    $ielcode = ord($byte);
    if (exists($infelem{$ielcode})) {
      print "$infelem{$ielcode}\n";
    } else {
      print "Unknown element type\n";
    }
    $count++;
    ($byte,$bytes) = split(//,$bytes,2);
    printbyte($count,0,$byte,0,8,'IE length',1);
    $elen = ord($byte);
    print "$elen octets\n";
    ($elem,$bytes) = unpack("a$elen a*",$bytes);
    if ($ielcode==4) { parsebearer($count,$elem);
    } elsif ($ielcode==8) { parsecause($count,$elem);
    } elsif ($ielcode==20) { parsestate($count,$elem);
    } elsif ($ielcode==24) { parsechannel($count,$elem);
    } elsif ($ielcode==30) { parseprogress($count,$elem);
    } elsif ($ielcode==39) { parsenotifyind($count,$elem);
    } elsif ($ielcode==40) { parsedisplay($count,$elem);
    } elsif ($ielcode==41) { parsedatetime($count,$elem);
    } elsif ($ielcode==76) { parsenumber($count,$elem);
    } elsif ($ielcode==108) { parsenumber($count,$elem);
    } elsif ($ielcode==112) { parsenumber($count,$elem);
    } else { parseunknown($count,$elem,'');
    }
  }  
}

sub parsebearer {
  my($count,$bytes) = @_;
  my($byte,$extens,$trate,$skipped);
  $count++;
  ($byte,$bytes) = split(//,$bytes,2);
  $extens = printextens($count,0,$byte);
  printbyte(0,0,$byte,1,2,'Coding standard',1);
  print "$codingst[ord($byte & \"\x60\")>>5]\n";
  printbyte(0,0,$byte,3,5,'Info. trans. cap.',1);
  print "$transcap{ord($byte & \"\x1F\")}\n";
  $skipped = skipextens($count,$extens,$bytes,0);
  $bytes = substr($bytes,$skipped) if $skipped;
  $count++;
  ($byte,$bytes) = split(//,$bytes,2);
  $extens = printextens($count,0,$byte);
  printbyte(0,0,$byte,1,2,'Transfer mode',1);
  print "$transmode[ord($byte & \"\x60\")>>5]\n";
  printbyte(0,0,$byte,3,5,'Info. transfer rate',1);
  $trate = $byte & "\x1F";
  print "$transrate{ord($trate)}\n";
  $skipped = skipextens($count,$extens,$bytes,0);
  $bytes = substr($bytes,$skipped) if $skipped;
  if ($rtate == 24) {
    ($byte,$bytes) = split(//,$bytes,2);
    $extens = printextens($count,1,$byte);
    printbyte(0,0,$byte,1,7,'Multiplier',1);
    print ord($byte),"\n";
    $skipped = skipextens($count,$extens,$bytes,1);
    $bytes = substr($bytes,$skipped) if $skipped;
  }
  if ($bytes) {
    $count++;
    ($byte,$bytes) = split(//,$bytes,2);
    $extens = printextens($count,0,$byte);
    printbyte(0,0,$byte,1,2,'Layer ID',1);
    print "$layerid[ord($byte & \"\x60\")>>5]\n";
    printbyte(0,0,$byte,3,5,'Layer 1 protocol',1);
    print "$lay1prot[ord($byte & \"\x1F\")]\n";
    $skipped = skipextens($count,$extens,$bytes,0);
    $bytes = substr($bytes,$skipped) if $skipped;
  }
  parseunknown($count,$bytes,'') if $bytes;
}

sub parsestate {
  my($count,$bytes) = @_;
  my($byte,$extens,$skipped);
  $count++;
  ($byte,$bytes) = split(//,$bytes,2);
  printbyte($count,0,$byte,0,2,'Coding standard',1);
  print "$codingst[ord($byte & \"\xC0\")>>6]\n";
  printbyte(0,0,$byte,2,6,'Call state value',1);
  print "$stateval{ord($byte & \"\x3F\")}\n";
  parseunknown($count,$bytes,'') if $bytes;
}

sub parsecause {
  my($count,$bytes) = @_;
  my($byte,$extens,$skipped);
  $count++;
  ($byte,$bytes) = split(//,$bytes,2);
  $extens = printextens($count,0,$byte);
  printbyte(0,0,$byte,1,2,'Coding standard',1);
  print "$codingst[ord($byte & \"\x60\")>>5]\n";
  printbyte(0,0,$byte,3,1,'Spare',0);
  printbyte(0,0,$byte,4,4,'Location',1);
  print "$location{ord($byte & \"\x0F\")}\n";
  $skipped = skipextens($count,$extens,$bytes,0);
  $bytes = substr($bytes,$skipped) if $skipped;
  $count++;
  ($byte,$bytes) = split(//,$bytes,2);
  $extens = printextens($count,0,$byte);
  printbyte(0,0,$byte,1,3,'Cause class',1);
  print "$causeclass[ord($byte & \"\x70\")>>4]\n";
  printbyte(0,0,$byte,4,4,'Cause value',1);
  print "$causeval{ord($byte & \"\x7F\")}\n";
  $skipped = skipextens($count,$extens,$bytes,0);
  $bytes = substr($bytes,$skipped) if $skipped;
  parseunknown($count,$bytes,' Diagnostics') if $bytes;
}

sub parsechannel {
  my($count,$bytes) = @_;
  my($byte,$extens,$skipped,$explident,$othertyp,$chanmap);
  $count++;
  ($byte,$bytes) = split(//,$bytes,2);
  $extens = printextens($count,0,$byte);
  printbyte(0,0,$byte,1,1,'Interface ident.',1);
  $explident = ord($byte & "\x40")>>6;
  print "$intident[$explident]\n";
  printbyte(0,0,$byte,2,1,'Interface type',1);
  $othertyp = ord($byte & "\x20")>>5;
  print "$inttyp[$othertyp]\n";
  printbyte(0,0,$byte,3,1,'Spare',0);
  printbyte(0,0,$byte,4,1,'Preferred/exclusive',1);
  print "$prefexcl[ord($byte & \"\x08\")>>3]\n";
  printbyte(0,0,$byte,5,1,'Signalling channel',1);
  print "$signchan[ord($byte & \"\x04\")>>2]\n";
  printbyte(0,0,$byte,6,2,'Information channel',1);
  print "$infchan[ord($byte & \"\x03\")]\n";
  $skipped = skipextens($count,$extens,$bytes,0);
  $bytes = substr($bytes,$skipped) if $skipped;
  if ($othertyp) {
    if ($explident) {
      $extens = 1;
      while ($extens) {
        ($byte,$bytes) = split(//,$bytes,2);
        $extens = printextens($count,'1',$byte);
        printbyte(0,0,$byte,1,7,'Identifier value',0);
      }
    }
    ($byte,$bytes) = split(//,$bytes,2);
    $extens = printextens($count,'2',$byte);
    printbyte(0,0,$byte,1,2,'Coding standard',1);
    print "$codingst[ord($byte & \"\x60\")>>5]\n";
    printbyte(0,0,$byte,3,1,'Number/map',1);
    $chanmap = ord($byte & "\x10")>>4;
    print "$numormap[$chanmap]\n";
    printbyte(0,0,$byte,4,4,'Channel type',1);
    print "$chantyp{ord($byte & \"\x0F\")}\n";
    $skipped = skipextens($count,$extens,$bytes,0);
    $bytes = substr($bytes,$skipped) if $skipped;
    if ($bytes) {
      if ($chanmap) {
        while ($bytes) {
          printbyte($count,3,$byte,0,7,'Channel map',0);
        }
      } else {
        $extens = 1;
        while ($extens) {
          ($byte,$bytes) = split(//,$bytes,2);
          $extens = printextens($count,'3',$byte);
          printbyte(0,0,$byte,1,7,'Channel number',1);
          print 'channel ',ord($byte & "\x7F"),"\n";
        }
      }
    }
  }
  parseunknown($count,$bytes,'') if $bytes;
}

sub parseprogress {
  my($count,$bytes) = @_;
  my($byte,$extens,$skipped);
  $count++;
  ($byte,$bytes) = split(//,$bytes,2);
  $extens = printextens($count,0,$byte);
  printbyte(0,0,$byte,1,2,'Coding standard',1);
  print "$codingst[ord($byte & \"\x60\")>>5]\n";
  printbyte(0,0,$byte,3,1,'Spare',0);
  printbyte(0,0,$byte,4,4,'Location',1);
  print "$location{ord($byte & \"\xOF\")}\n";
  $skipped = skipextens($count,$extens,$bytes,0);
  $bytes = substr($bytes,$skipped) if $skipped;
  $count++;
  ($byte,$bytes) = split(//,$bytes,2);
  $extens = printextens($count,0,$byte);
  printbyte(0,0,$byte,1,7,'Progress description',1);
  print "$progress{ord($byte & \"\x7F\")}\n";
  $skipped = skipextens($count,$extens,$bytes,0);
  $bytes = substr($bytes,$skipped) if $skipped;
  parseunknown($count,$bytes,'') if $bytes;
}

sub parsenotifyind {
  my($count,$bytes) = @_;
  my($byte,$extens,$skipped);
  $count++;
  ($byte,$bytes) = split(//,$bytes,2);
  $extens = printextens($count,0,$byte);
  printbyte(0,0,$byte,1,7,'Notify Indicator',1);
  print "$notifyind[ord($byte & \"\x7F\")]\n";
  $skipped = skipextens($count,$extens,$bytes,0);
  $bytes = substr($bytes,$skipped) if $skipped;
  parseunknown($count,$bytes,'') if $bytes;
}

sub parsedisplay {
  my($count,$bytes) = @_;
  printstring($count,'Display string',$bytes);
}

sub parsedatetime {
  my($count,$bytes) = @_;
  for ($i = 0;$i<=5;$i++) {
    $count++;
    ($byte,$bytes) = split(//,$bytes,2);
    printbyte($count,0,$byte,0,8,$datetime[$i],1);
    print sprintf('%2.2d',ord($byte)),"\n";
  }
  parseunknown($count,$bytes,'') if $bytes;
}


sub parsenumber {
  my($count,$bytes) = @_;
  my($byte,$extens,$skipped,$scount);
  $count++;
  ($byte,$bytes) = split(//,$bytes,2);
  $extens = printextens($count,0,$byte);
  printbyte(0,0,$byte,1,3,'Type of number',1);
  print "$numbertype{ord($byte & \"\x7F\")}\n";
  printbyte(0,0,$byte,4,4,'Numbering plan ID',1);
  print "$planid{ord($byte & \"\x0F\")}\n";
  $scount = 0;
  if ($extens) {
    $scount++;
    ($byte,$bytes) = split(//,$bytes,2);
    $extens = printextens($count,1,$byte);
    printbyte(0,0,$byte,1,2,'Pres. indicator',1);
    print "$presindi[ord($byte & \"\x60\")>>5]\n";
    printbyte(0,0,$byte,3,3,'Spare',0);
    printbyte(0,0,$byte,6,2,'Screening indicator',1);
    print "$screindi[ord($byte & \"\x03\")]\n";
  }
  $skipped = skipextens($count,$extens,$bytes,$scount);
  $bytes = substr($bytes,$skipped) if $skipped;
  $count++;
  printstring($count,'Number',$bytes);
}

sub parseunknown {
  my($count,$bytes,$typnam) = @_;
  my($byte);
  while ($bytes) {
    $count++;
    ($byte,$bytes) = split(//,$bytes,2);
    printbyte($count,0,$byte,0,8,$typnam,0);
  }
}

sub skipextens {
  my($count,$extens,$bytes,$scount) = @_;
  my($byte,$lcount);
  $lcount = 0;
  while ($extens) {
    $lcount++;
    ($byte,$bytes) = split(//,$bytes,2);
    $extens = printextens($count,$scount+$lcount,$byte);
    printbyte(0,0,$byte,1,7,'',0);
  }
  return $lcount;
}

sub printextens {
  my($count,$scount,$byte) = @_;
  my($nocontin);
  printbyte($count,$scount,$byte,0,1,'Extension bit',1);
  $nocontin = ord($byte)>127||$byte eq '';
  print "$extend[$nocontin]\n";
  return !$nocontin; 
}

sub printstring {
  my($count,$title,$string) = @_;
  printf('%4.0d    ++++++++',$count);
  printf(' %-20s: ',$title);
  $string =~ s/[^\w !"#$%&'()*+,-.\/:;<=>?@[\\\]^_`{|}~]/ /go;
  print "'$string'\n";
}

sub printbyte {
  my($count,$scount,$byte,$left,$num,$string,$cont) = @_;
  if ($count==0) {
    print '    ';
  } else {
    printf('%4.0d',$count);
  }
  if ($scount==0) {
    print '   ';
  } else {
    printf('.%-2.0d',$scount);
  }
  $outrep = '--------';
  substr($outrep,$left,$num) = substr(unpack('B8',$byte),$left,$num) if $byte;
  print " $outrep";
  if ($cont) {
    printf(' %-20s: ',$string);
  } else {
    print " $string\n";
  }
}

sub initpppdata {
  %protocol = ('0001','Padding Protocol', # RFC1661
               '0002','Telebit Compressed IPX', # RFC 1552
               '0021','Internet Protocol version 4', # RFC1332
               '002d','Van Jacobson Compressed TCP/IP', # RFC1332
               '0061','IP-Compression-Protocol', # RFC2509
               '0235','Shiva Compressed NCP/IPX', # RFC 1552
               '8021','Internet Protocol Control Protocol', # RFC1332
               '8029','AppleTalk Protocol Control Protocol', # RFC1378
               '80fb','Individual link Compression Control Protocol', # RFC1962
               '80fd','Compression Control Protocol', # RFC1962
               'c021','Link Control Protocol', # RFC1661
               'c023','Password Authentication Protocol', # RFC1334
               'c025','Link-Quality-Report', # RFC1661
               'c02b','Bandwidth Allocation Control Protocol', # RFC2125
               'c02d','Bandwidth Allocation Protocol', # RFC2125
               'c223','Challenge-Handshake Authentication Protocol'); # RFC1994
  @lcpcode = ('Vendor-Specific', # RFC2153
              'Configure-Request', # RFC1661
              'Configure-Ack', # RFC1661
              'Configure-Nak', # RFC1661
              'Configure-Reject', # RFC1661
              'Terminate-Request', # RFC1661
              'Terminate-Ack', # RFC1661
              'Code-Reject', # RFC1661
              'Protocol-Reject', # RFC1661
              'Echo-Request', # RFC1661
              'Echo-Reply', # RFC1661
              'Discard-Request', # RFC1661
              'Identification', # RFC1570
              'Time-Remaining', # RFC1570
              'Reset-Request', # RFC1962
              'Reset-Reply'); # RFC1962
  %lcpoptlen = (1,2,2,4,5,4,10,1,17,2,23,2);
  %lcpoption =  (0,'Vendor-Specific', # RFC2153
                  1,'Maximum-Receive-Unit', # RFC1661
                  2,'Async-Control-Character-Map', # RFC1662
                  3,'Authentication-Protocol', # RFC1661
                  4,'Quality-Protocol', # RFC1661
                  5,'Magic-Number', # RFC1661
                  7,'Protocol-Field-Compression', # RFC1661
                  8,'Address-and-Control-Field-Compression', # RFC1661
                  9,'FCS-Alternatives', # RFC1570
                  10,'Self-Describing-Padding', # RFC1570
                  11,'Numbered-Mode', # RFC1663
                  13,'Callback', # RFC1570
                  15,'Compound-Frames', # RFC1570
                  17,'Multilink-MRRU', #RFC1990
                  18,'Short-Sequence-Number-Header', # RFC1990
                  19,'Endpoint-Discriminator', # RFC1990
                  23,'Link Discriminator', # RFC2125
                  28,'Internationalization'); # RFC2484
  %chapalg = (5,'with MD5'); # RFC1994
  @endclass = ('Null Class', # RFC1990
               'Locally Assigned Address', # RFC1990
               'Internet Protocol (IP) Address', # RFC1990
               'IEEE 802.1 Globally Assigned MAC Address', # RFC1990
               'PPP Magic-Number Block', # RFC1990
               'Public Switched Network Directory Number'); # RFC1990
  @backoper = ('Location is determined by user authentication', # RFC1570
               'Dialing string', # RFC1570
               'Location identifier', # RFC1570
               'E.164 number', # RFC1570
               'Distinguished name'); # RFC1570
  @fcsalt = ('Null FCS', # RFC1570
             'CCITT 16-bit FCS', # RFC1570
             'CCITT 32-bit FCS'); # RFC1570
  @papcode = ('', # RFC1334
              'Authenticate-Request', # RFC1334
              'Authenticate-Ack', # RFC1334
              'Authenticate-Nak'); # RFC1334
  @chapcode = ('', # RFC1994
               'Challenge', # RFC1994
               'Response', # RFC1994
               'Success', # RFC1994
               'Failure'); # RFC1994
  %ccpoption = (0,'Compression Type: OUI', # RFC1962
                 1,'Compression Type: Predictor type 1', # RFC1962
                 2,'Compression Type: Predictor type 2', # RFC1962
                 3,'Compression Type: Puddle Jumper', # RFC1962
                 16,'Compression Type: Hewlett-Packard PPC', # RFC1962
                 17,'Compression Type: Stac Electronics LZS', # RFC1962
                 18,'Compression Type: Microsoft PPC', # RFC1962
                 19,'Compression Type: Gandalf FZA', # RFC1962
                 20,'Compression Type: V.42bis compression', # RFC1962
                 21,'Compression Type: BSD LZW Compress', # RFC1974
                 255,'Compression Type: Reserved'); # RFC1962
  @lszcm = ('None', # RFC1974
            'LCB', # RFC1974
            'CRC', # RFC1974
            'Sequence Number', # RFC1974
            'Extended Mode'); # RFC1974
  @bacpoptlen = (0,4);
  @bacpoption = ('', # RFC2125
                  'Favored-Peer'); # RFC2125
  %ipcpoptlen = (3,4,4,4,129,4,130,4,131,4,132,4);
  %ipcpoption = (1,'IP-Addresses ** DEPRECATED **', # RFC1172
                 2,'IP-Compression-Protocol', # RFC1332
                 3,'IP-Address', # RFC1332
                 4,'Mobile-IPv4: Home Address', # RFC2290
               129,'Primary-DNS-Address', # RFC 1877
               130,'Primary-NBNS-Address', # RFC 1877
               131,'Secondary-DNS-Address', # RFC 1877
               132,'Secondary-NBNS-Address'); # RFC 1877
  @ipxcpoptlen = (0,4,6,0,0,0,0); # RFC1552
  @ipxcpoption = ('', # RFC1552
                  'IPX-Network-Number', # RFC1552
                  'IPX-Node-Number', # RFC1552
                  'IPX-Compression-Protocol', # RFC1552
                  'IPX-Routing-Protocol', # RFC1552
                  'IPX-Router-Name', # RFC1552
                  'IPX-Configuration-Complete'); # RFC1552
  @ipxcprouting = ('No routing protocol required', # RFC1552
                   'RESERVED', # RFC1552
                   'Novell RIP/SAP required', # RFC1552
                   '', # RFC1552
                   'Novell NLSP required'); # RFC1552
  @bapcode = ('', # RFC2125
              'Call-Request', # RFC2125
              'Call-Response', # RFC2125
              'Callback-Request', # RFC2125
              'Callback-Response', # RFC2125
              'Link-Drop-Query-Request', # RFC2125
              'Link-Drop-Query-Response', # RFC2125
              'Call-Status-Indication', # RFC2125
              'Call-Status-Response'); # RFC2125
  @bapresp = ('Request-Ack', # RFC2125
              'Request-Nak', # RFC2125
              'Request-Rej', # RFC2125
              'Request-Full-Nak'); # RFC2125
  @bapoptlen =(0,0,0,0,0,2,0);
  @bapoption = ('', # RFC2125
                 'Link-Type', # RFC2125
                 'Phone-Delta', # RFC2125
                 'No-Phone-Number-Needed', # RFC2125
                 'Reason', # RFC2125
                 'Link-Discriminator', # RFC2125
                 'Call-Status'); # RFC2125
  @bapltyp = ('ISDN', # RFC2125
              'X.25', # RFC2125
              'analog', # RFC2125
              'switched digital (non-ISDN)', # RFC2125
              'ISDN data over voice'); # RFC2125
  @bapop2sublen = (0,1,-2,-2);
  @bapop2sub = ('', # RFC2125
                'Unique-Digits', # RFC2125
                'Subscriber-Number', # RFC2125
                'Phone-Number-Sub-Address'); # RFC2125
  @bapact = ('No retry', # RFC2125
             'Retry'); # RFC2125
}

sub initisdndata {
  %messtyp = (1,'ALERTING',
              2,'CALL PROCEEDING',
              3,'PROGRESS',
              5,'SETUP',
              7,'CONNECT',
             13,'SETUP ACKNOWLEDGE',
             15,'CONNECT ACKNOWLEDGE',
             69,'DISCONNECT',
             70,'RESTART',
             77,'RELEASE',
             78,'RESTART ACKNOWLEDGE',
             90,'RELEASE COMPLETE',
             96,'SEGMENT',
            117,'STATUS ENQUIRY',
            110,'NOTIFY',
            123,'INFORMATION',
            125,'STATUS');
  %infelem = (4,'Bearer capability',
              8,'Cause',
             20,'Call state',
             24,'Channel identification',
             30,'Progress indicator',
             39,'Notify indicator',
             40,'Display',
             41,'Date/Time',
             76,'Connected number',
             77,'Connected subadress',
            108,'Calling party number',
            109,'Calling party subadress',
            112,'Called party number',
            113,'Called party subaddress',
            121,'Restart indicator',
            124,'Low layer compatibility',
            125,'High layer compatibility');
  @crbit = ('C','R');
  @pfval = ('P','F');
  @stype = ('RR   ',
            'RNR  ',
            'REJ  ');
  %utype = (0,'UI   ',
           12,'DM   ',
           64,'DISC ',
           96,'UA   ',
          108,'SABME',
          132,'FRMR ',
          172,'XID  ');
  @calldir = ('Outgoing','Incoming');
  @extend = ('continued','not continued');
  @codingst = ('CCITT standardized coding');
  %transcap = (0,'speech',
               8,'unrestricted digital information',
               9,'restricted digital information',
              16,'3.1 kHz audio',
              17,'unrestricted digital information with tones',
              24,'video');
  @transmode = ('circuit mode','packet mode');
  %transrate = (16,'64 kbit/s',
                17,'2x 64 kbits/s',
                19,'384 kbit/s',
                21,'1536 kbit/s',
                23,'1920 kbit/s',
                24,'multi-rate');
  @layerid = ('','layer 1');
  @lay1prot = ('',
               'CCITT rec. V.110/X.30',
               'CCITT rec. G.711 mu-law',
               'CCITT rec. G.711 A-law',
               'MICDA',
               'CCITT rec. H.221/H.242',
               '',
               'not CCITT defined',
               'CCITT rec. V.120',
               'CCITT rec. X.31');
  %numbertype = (0,'unknown',
                 1,'unknown',
                 9,'unknown',
                17,'international number',
                25,'level 2 regional number',
                33,'national number',
                41,'level 1 regional number',
                57,'PISN specific number',
                65,'subscriber number',
                73,'level 0 regional number');
  %planid =  (0,'unknown',
              1,'ISDN/Telephony numbering plan',
              9,'private numbering plan');
  @presindi = ('presentation allowed',
               'presentation restricted',
               'number not available',
               'reserved');
  @screindi = ('user provided, not screened',
               'user provided, verified',
               'reserved',
               'network provided');
  %location = (0,'user',
               1,'private network serving local user',
               2,'public network serving local user',
               3,'transit network',
               4,'public network serving remote user',
               5,'private network serving remote user',
               7,'international network',
              10,'network beyond internetworking point');
  @causeclass = ('normal event',
                 'normal event',
                 'resource unavailable',
                 'service or option not available',
                 'service or option not implemented',
                 'invalid message (e.g. parameter out of range)',
                 'protocol error (e.g. unknown message)',
                 'interworking');
  %causeval = (1,'unallocated (unassigned) number',
               3,'no route to destination',
               6,'channel unacceptable',
              16,'normal call clearing',
              17,'user busy',
              18,'no user responding',
              19,'no answer from user (user alerted)',
              21,'call rejected',
              22,'number changed',
              27,'destination out of order',
              28,'invalid number format',
              30,'response to STATUS ENQUIRY',
              31,'normal, unspecified',
              34,'no circuit/channel available',
              41,'temporary failure',
              44,'requested circuit/channel not available',
              57,'bearer capability not authorized',
              58,'bearer capability not presently available',
              63,'service or option not available, unspecified',
              65,'bearer capability not implemented',
              81,'invalid call reference value',
              82,'identified channel does not exist',
              88,'incompatible destination',
              96,'mandatory information element is missing',
              97,'message type non-existent or not implemented',
              98,'message not compatible with call state or message non-existent or not implemented',
              99,'information element non-existent or not implemented',
             100,'invalid information element contents',
             101,'message not compatible with call state',
             102,'recovery on timer expiry',
             111,'protocol error, unspecified');
  @prefexcl = ('indicated channel is preferred',
               'exclusive; only indicated channel acceptable');
  @intident = ('implicitly identified','explicitly identified');
  @inttyp = ('basic interface','other interface type');
  @signchan = ('not the signalling channel','reserved');
  @infchan = ('no channel',
              'as indicated in the following octets',
              'reserved',
              'any channel');
  @numormap = ('channel number','channel map');
  %chantyp = (3,'B channel units (64 kbit/s)');
  %progress = (1,'call is not end-to-end ISDN',
               2,'destination address is non-ISDN',
               3,'origination address is non-ISDN',
               4,'call has returned to the ISDN',
               8,'in-band information or appropriate pattern now available');
  @notifyind = ('user suspended',
                'user resumed',
                'bearer service change');
  @datetime = ('Year','Month','Day','Hour','Minute','Second');
  %stateval = (0,'null',
               1,'call initiated',
               2,'overlap sending',
               3,'outgoing call proceeding',
               4,'call delivered',
               6,'call present',
               7,'call received',
               8,'connect request',
               9,'incoming call proceeding',
              10,'active',
              11,'disconnect Request',
              12,'disconnect Indication',
              19,'release request',
              25,'overlap receiving');
}
